@integrity-labs/agt-cli 0.28.1172 → 0.28.1174

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin/agt.js CHANGED
@@ -40,7 +40,7 @@ import {
40
40
  success,
41
41
  table,
42
42
  warn
43
- } from "../chunk-7XUOPTSD.js";
43
+ } from "../chunk-H3LFHQQC.js";
44
44
  import {
45
45
  getProjectDir,
46
46
  isSessionResumeDisabled,
@@ -5513,7 +5513,7 @@ import { execFileSync, execSync } from "child_process";
5513
5513
  import { existsSync as existsSync11, realpathSync as realpathSync2 } from "fs";
5514
5514
  import chalk18 from "chalk";
5515
5515
  import ora16 from "ora";
5516
- var cliVersion = true ? "0.28.1172" : "dev";
5516
+ var cliVersion = true ? "0.28.1174" : "dev";
5517
5517
  async function fetchLatestVersion() {
5518
5518
  const host2 = getHost();
5519
5519
  if (!host2) return null;
@@ -6773,7 +6773,7 @@ function handleError(err) {
6773
6773
  }
6774
6774
 
6775
6775
  // src/bin/agt.ts
6776
- var cliVersion2 = true ? "0.28.1172" : "dev";
6776
+ var cliVersion2 = true ? "0.28.1174" : "dev";
6777
6777
  var program = new Command();
6778
6778
  program.name("agt").description("Augmented CLI \u2014 agent provisioning and management").version(cliVersion2).option("--json", "Emit machine-readable JSON output (suppress spinners and colors)").option("--skip-update-check", "Skip the automatic update check on startup");
6779
6779
  program.hook("preAction", async (thisCommand, actionCommand) => {
@@ -8078,7 +8078,7 @@ function exchangeFailureKind(err) {
8078
8078
  }
8079
8079
 
8080
8080
  // src/lib/api-client.ts
8081
- var agtCliVersion = true ? "0.28.1172" : "dev";
8081
+ var agtCliVersion = true ? "0.28.1174" : "dev";
8082
8082
  var lastConfigHash = null;
8083
8083
  function setConfigHash(hash) {
8084
8084
  lastConfigHash = hash && hash.length > 0 ? hash : null;
@@ -12826,7 +12826,7 @@ function resolveStableAgtBin(rawPath) {
12826
12826
  }
12827
12827
  async function managerInstallCommand(opts = {}) {
12828
12828
  const json = isJsonMode();
12829
- const { installSupervisor, supervisorStatus } = await import("./manager-supervisor-IVMG2ACE.js");
12829
+ const { installSupervisor, supervisorStatus } = await import("./manager-supervisor-KIFBGETA.js");
12830
12830
  const intervalSec = parseInt(opts.interval ?? "10", 10);
12831
12831
  if (isNaN(intervalSec) || intervalSec < 5) {
12832
12832
  const msg = "Interval must be at least 5 seconds.";
@@ -12888,7 +12888,7 @@ async function managerInstallCommand(opts = {}) {
12888
12888
  }
12889
12889
  async function managerUninstallCommand() {
12890
12890
  const json = isJsonMode();
12891
- const { uninstallSupervisor } = await import("./manager-supervisor-IVMG2ACE.js");
12891
+ const { uninstallSupervisor } = await import("./manager-supervisor-KIFBGETA.js");
12892
12892
  const result = await uninstallSupervisor();
12893
12893
  if (!result.ok) {
12894
12894
  if (json) jsonOutput({ ok: false, error: result.error });
@@ -12904,7 +12904,7 @@ async function managerUninstallCommand() {
12904
12904
  }
12905
12905
  async function managerInstallSystemUnitCommand(opts = {}) {
12906
12906
  const json = isJsonMode();
12907
- const { installSystemUnit, systemUnitStatus } = await import("./manager-supervisor-IVMG2ACE.js");
12907
+ const { installSystemUnit, systemUnitStatus } = await import("./manager-supervisor-KIFBGETA.js");
12908
12908
  const intervalSec = parseInt(opts.interval ?? "10", 10);
12909
12909
  if (isNaN(intervalSec) || intervalSec < 5) {
12910
12910
  const msg = "Interval must be at least 5 seconds.";
@@ -12960,7 +12960,7 @@ async function managerInstallSystemUnitCommand(opts = {}) {
12960
12960
  }
12961
12961
  async function managerUninstallSystemUnitCommand() {
12962
12962
  const json = isJsonMode();
12963
- const { uninstallSystemUnit } = await import("./manager-supervisor-IVMG2ACE.js");
12963
+ const { uninstallSystemUnit } = await import("./manager-supervisor-KIFBGETA.js");
12964
12964
  const result = await uninstallSystemUnit();
12965
12965
  if (!result.ok) {
12966
12966
  if (json) jsonOutput({ ok: false, error: result.error });
@@ -13235,4 +13235,4 @@ export {
13235
13235
  managerInstallSystemUnitCommand,
13236
13236
  managerUninstallSystemUnitCommand
13237
13237
  };
13238
- //# sourceMappingURL=chunk-7XUOPTSD.js.map
13238
+ //# sourceMappingURL=chunk-H3LFHQQC.js.map
@@ -77,7 +77,7 @@ import {
77
77
  setConfigHash,
78
78
  shellGuard,
79
79
  tripClass
80
- } from "../chunk-7XUOPTSD.js";
80
+ } from "../chunk-H3LFHQQC.js";
81
81
  import {
82
82
  CLAUDE_CODE_SECURITY_FLOOR,
83
83
  SELF_UPDATE_TIMEOUTS,
@@ -15928,7 +15928,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
15928
15928
  var lastVersionCheckAt = 0;
15929
15929
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
15930
15930
  var lastResponsivenessProbeAt = 0;
15931
- var agtCliVersion = true ? "0.28.1172" : "dev";
15931
+ var agtCliVersion = true ? "0.28.1174" : "dev";
15932
15932
  function resolveBrewPath(execFileSync3) {
15933
15933
  try {
15934
15934
  const out = execFileSync3("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -320,6 +320,10 @@ function shellQuoteForSystemd(arg) {
320
320
  }
321
321
  var LINUX_SYSTEM_UNIT_PATH = "/etc/systemd/system/agt-manager.service";
322
322
  var LINUX_SYSTEM_ENV_FILE_PATH = "/etc/agt/agt-manager.env";
323
+ var LINUX_RECOVER_SERVICE_NAME = "agt-manager-recover.service";
324
+ var LINUX_RECOVER_TIMER_NAME = "agt-manager-recover.timer";
325
+ var LINUX_RECOVER_SERVICE_PATH = `/etc/systemd/system/${LINUX_RECOVER_SERVICE_NAME}`;
326
+ var LINUX_RECOVER_TIMER_PATH = `/etc/systemd/system/${LINUX_RECOVER_TIMER_NAME}`;
323
327
  async function installSystemUnit(opts) {
324
328
  if (platform() !== "linux") {
325
329
  return {
@@ -370,11 +374,21 @@ async function installSystemUnit(opts) {
370
374
  chmodSync(LINUX_SYSTEM_ENV_FILE_PATH, 384);
371
375
  writeFileSync(LINUX_SYSTEM_UNIT_PATH, renderLinuxSystemUnit(opts), { mode: 420 });
372
376
  chmodSync(LINUX_SYSTEM_UNIT_PATH, 420);
377
+ writeFileSync(LINUX_RECOVER_SERVICE_PATH, renderLinuxRecoverService(), { mode: 420 });
378
+ chmodSync(LINUX_RECOVER_SERVICE_PATH, 420);
379
+ writeFileSync(LINUX_RECOVER_TIMER_PATH, renderLinuxRecoverTimer(), { mode: 420 });
380
+ chmodSync(LINUX_RECOVER_TIMER_PATH, 420);
373
381
  try {
374
382
  execFileSync("systemctl", ["daemon-reload"], { stdio: "pipe" });
375
383
  } catch (err) {
376
384
  return { ok: false, error: `systemctl daemon-reload failed: ${err.message}` };
377
385
  }
386
+ let recoverTimerNote = `Recovery timer ${LINUX_RECOVER_TIMER_NAME} enabled.`;
387
+ try {
388
+ execFileSync("systemctl", ["enable", "--now", LINUX_RECOVER_TIMER_NAME], { stdio: "pipe" });
389
+ } catch (err) {
390
+ recoverTimerNote = `WARNING: could not enable ${LINUX_RECOVER_TIMER_NAME}, so this host will not recover by itself from StartLimitBurst: ${err.message}`;
391
+ }
378
392
  try {
379
393
  execFileSync("systemctl", ["enable", "--now", LINUX_UNIT_NAME], { stdio: "pipe" });
380
394
  } catch (err) {
@@ -394,7 +408,7 @@ async function installSystemUnit(opts) {
394
408
  }
395
409
  return {
396
410
  ok: true,
397
- details: `Loaded systemd system unit ${LINUX_UNIT_NAME} at ${LINUX_SYSTEM_UNIT_PATH} (status: ${isActive}). Will auto-start on reboot via multi-user.target. Logs: ${join(opts.configDir, "manager.log")} and 'journalctl -u ${LINUX_UNIT_NAME}'.`
411
+ details: `Loaded systemd system unit ${LINUX_UNIT_NAME} at ${LINUX_SYSTEM_UNIT_PATH} (status: ${isActive}). Will auto-start on reboot via multi-user.target. Logs: ${join(opts.configDir, "manager.log")} and 'journalctl -u ${LINUX_UNIT_NAME}'. ${recoverTimerNote}`
398
412
  };
399
413
  }
400
414
  async function uninstallSystemUnit() {
@@ -404,7 +418,12 @@ async function uninstallSystemUnit() {
404
418
  if (process.getuid?.() !== 0) {
405
419
  return { ok: false, error: "uninstallSystemUnit requires root." };
406
420
  }
421
+ bestEffort(() => execFileSync("systemctl", ["disable", "--now", LINUX_RECOVER_TIMER_NAME], { stdio: "ignore" }));
422
+ bestEffort(() => execFileSync("systemctl", ["stop", LINUX_RECOVER_SERVICE_NAME], { stdio: "ignore" }));
407
423
  bestEffort(() => execFileSync("systemctl", ["disable", "--now", LINUX_UNIT_NAME], { stdio: "ignore" }));
424
+ for (const path of [LINUX_RECOVER_TIMER_PATH, LINUX_RECOVER_SERVICE_PATH]) {
425
+ if (existsSync(path)) bestEffort(() => unlinkSync(path));
426
+ }
408
427
  let removed = false;
409
428
  if (existsSync(LINUX_SYSTEM_UNIT_PATH)) {
410
429
  try {
@@ -449,12 +468,101 @@ function systemUnitStatus() {
449
468
  }
450
469
  return { kind: "installed", pid, details: `Unit: ${LINUX_SYSTEM_UNIT_PATH} (${activeState})` };
451
470
  }
452
- function buildSystemUnitRepairExecStartPre(agtBin, user) {
471
+ function buildSystemUnitRepairExecStartPre(agtBin, user, opts = {}) {
453
472
  const sudo = user === "root" ? "" : "sudo -n ";
454
- const versionExpansion = "$${AGT_CLI_PIN_VERSION:-$${AGT_CLI_RELEASE_CHANNEL:-latest}}";
473
+ const proc = opts.procRoot ?? "/proc";
474
+ const bin = '"' + agtBin + '"';
475
+ const versionExpansion = "${AGT_CLI_PIN_VERSION:-${AGT_CLI_RELEASE_CHANNEL:-latest}}";
455
476
  const installSpec = '"@integrity-labs/agt-cli@' + versionExpansion + '"';
456
- const script = '"' + agtBin + '" --version >/dev/null 2>&1 || { echo "[agt-manager] agt binary missing/dangling - reinstalling @integrity-labs/agt-cli@' + versionExpansion + '"; ' + sudo + "npm install -g " + installSpec + " --registry=https://registry.npmjs.org; }";
457
- return "ExecStartPre=/bin/sh -c '" + script + "'";
477
+ const script = [
478
+ bin + " --version >/dev/null 2>&1 || {",
479
+ 'echo "[agt-manager] agt binary missing/dangling - reinstalling @integrity-labs/agt-cli@' + versionExpansion + '";',
480
+ // ENG-11695: the shell twin of `anyPackageInstallRunning` and
481
+ // `isPackageInstallArgv` in disk-guard.ts, because this runs when `agt`
482
+ // cannot, so it cannot call them. Cleaning a package cache under a running
483
+ // install can cost that install its binary (Obi Wan b8ed1339eaf2). Same
484
+ // rules: the process itself must be npm/pnpm, a container's install does
485
+ // not count, and a root or a /proc that cannot be read counts as busy. Also the
486
+ // same limit: a non-root manager under `hidepid` can't see root's installs. The
487
+ // fleet's manager runs as root, which hidepid doesn't restrict.
488
+ "busy() {",
489
+ "[ -r " + proc + "/self/cmdline ] || return 0;",
490
+ "for p in " + proc + "/[0-9]*; do",
491
+ 'c=$(tr "[:cntrl:]" " " < "$p/cmdline" 2>/dev/null) || continue;',
492
+ "set -f; set -- $c; set +f;",
493
+ "[ $# -gt 0 ] || continue;",
494
+ 'case "${1##*/}" in npm|pnpm) shift ;; node|nodejs) case "${2##*/}" in npm|npm-cli.js|pnpm|pnpm.js|pnpm.cjs) shift 2 ;; *) continue ;; esac ;; *) continue ;; esac;',
495
+ 'n=0; for a in "$@"; do',
496
+ 'case "$a" in -*) continue ;; esac;',
497
+ 'case "$a" in',
498
+ 'install|i|ci|add|update|upgrade|up) { [ ! -e "$p/root" ] || [ "$p/root" -ef / ]; } && return 0; break ;;',
499
+ 'run|run-script|exec|x|dlx|test|start) [ "$n" = 0 ] && break ;;',
500
+ "esac;",
501
+ 'n=$((n+1)); [ "$n" -ge 2 ] && break;',
502
+ "done;",
503
+ "done; return 1; };",
504
+ 'pkgclean() { if busy; then echo "[agt-manager] skipping $* - an npm/pnpm install is running"; else "$@" >/dev/null 2>&1 || true; fi; };',
505
+ // ENG-11695 ask 2: the same safe cleanup as SAFE_CLEANUP_STEPS, at the same
506
+ // threshold as SELF_UPDATE_MIN_FREE_BYTES (1 GiB in KiB). Caches and logs
507
+ // only, nothing deleted by path. Unlike the self-update guard this never
508
+ // refuses the install: the binary is already broken, so there is nothing
509
+ // left for a failed install to lose. A `df` that fails reads as low.
510
+ "set -- $(df -Pk / 2>/dev/null | tail -1);",
511
+ 'if [ "${4:-0}" -lt 1048576 ]; then',
512
+ 'echo "[agt-manager] under 1 GiB free - running safe cleanup before reinstalling";',
513
+ // `timeout` goes INSIDE sudo: sudo forwards TERM but can't forward KILL, so a
514
+ // `timeout` outside it kills sudo and leaves the step running, possibly into
515
+ // the install below (Obi Wan 95130c6e0a21).
516
+ sudo + "timeout -k 10 120 journalctl --vacuum-size=200M >/dev/null 2>&1 || true;",
517
+ "pkgclean timeout -k 10 120 npm cache clean --force;",
518
+ "pkgclean timeout -k 10 120 pnpm store prune;",
519
+ ...sudo ? ["pkgclean sudo -n -H timeout -k 10 120 npm cache clean --force;"] : [],
520
+ "fi;",
521
+ sudo + "npm install -g " + installSpec + " --registry=https://registry.npmjs.org;",
522
+ // ENG-11695 ask 1: npm does not always rewrite a link it did not create, so
523
+ // a dangling or self-referencing one can survive a good install. Only ever
524
+ // a link or nothing: a real file at this path is not ours to replace.
525
+ "if ! " + bin + " --version >/dev/null 2>&1; then",
526
+ 't="$(' + sudo + 'npm root -g 2>/dev/null)/@integrity-labs/agt-cli/dist/bin/agt.js";',
527
+ 'if [ -f "$t" ] && { [ -L ' + bin + " ] || [ ! -e " + bin + " ]; }; then",
528
+ 'echo "[agt-manager] relinking ' + agtBin + ' to $t";',
529
+ sudo + 'ln -sfn "$t" ' + bin + ";",
530
+ "fi;",
531
+ "fi;",
532
+ // The exit status of the whole repair: did it work?
533
+ bin + " --version >/dev/null 2>&1;",
534
+ "}"
535
+ ].join(" ");
536
+ return "ExecStartPre=/bin/sh -c '" + script.replace(/\$/g, "$$$$") + "'";
537
+ }
538
+ var RECOVER_INTERVAL = "15min";
539
+ function renderLinuxRecoverService() {
540
+ return `# agt manager recovery (ENG-11695)
541
+ # Managed by \`agt manager install-system-unit\` \u2014 edits will be overwritten.
542
+
543
+ [Unit]
544
+ Description=Restart the Augmented host manager if systemd has latched it to failed
545
+
546
+ [Service]
547
+ Type=oneshot
548
+ ExecStart=/bin/sh -c 'systemctl is-failed --quiet ${LINUX_UNIT_NAME} || exit 0; echo "[agt-manager-recover] ${LINUX_UNIT_NAME} is failed - resetting and starting"; systemctl reset-failed ${LINUX_UNIT_NAME}; systemctl start --no-block ${LINUX_UNIT_NAME}'
549
+ `;
550
+ }
551
+ function renderLinuxRecoverTimer() {
552
+ return `# agt manager recovery timer (ENG-11695)
553
+ # Managed by \`agt manager install-system-unit\` \u2014 edits will be overwritten.
554
+
555
+ [Unit]
556
+ Description=Check every ${RECOVER_INTERVAL} whether the Augmented host manager is latched to failed
557
+
558
+ [Timer]
559
+ OnBootSec=${RECOVER_INTERVAL}
560
+ OnUnitActiveSec=${RECOVER_INTERVAL}
561
+ Unit=${LINUX_RECOVER_SERVICE_NAME}
562
+
563
+ [Install]
564
+ WantedBy=timers.target
565
+ `;
458
566
  }
459
567
  function renderLinuxSystemUnit(opts) {
460
568
  const user = opts.user ?? "root";
@@ -486,6 +594,7 @@ Wants=network-online.target
486
594
  # transient miss needs several attempts within the window to self-heal before
487
595
  # systemd would otherwise latch the unit to \`failed\`. 10 tries / 5 min gives
488
596
  # the repair room to recover without spinning forever on a genuinely broken host.
597
+ # ENG-11695: if it does latch, ${LINUX_RECOVER_TIMER_NAME} starts another round.
489
598
  StartLimitBurst=10
490
599
  StartLimitIntervalSec=300
491
600
 
@@ -500,6 +609,12 @@ EnvironmentFile=${LINUX_SYSTEM_ENV_FILE_PATH}
500
609
  # interrupted self-update can't latch the unit to \`failed\` with no recovery.
501
610
  ${buildSystemUnitRepairExecStartPre(opts.agtBin, user)}
502
611
  ExecStart=${execArgs}
612
+ # ENG-11695: this limit covers ExecStartPre, and the default 90s can't fit the
613
+ # repair on a full disk. Worst case is 4 cleanup steps x 130s (each under its own
614
+ # \`timeout -k 10 120\`, so one that ignores TERM still can't eat the budget) plus
615
+ # 200s for npm. A hung npm is killed here and retried. A healthy start never gets
616
+ # close.
617
+ TimeoutStartSec=720
503
618
 
504
619
  # on-failure (not always) so a clean \`systemctl stop\` doesn't fight a
505
620
  # respawn loop. The ExecStartPre repair + the widened StartLimit above turn a
@@ -527,6 +642,12 @@ var _internals = {
527
642
  renderMacosPlist,
528
643
  renderLinuxUnit,
529
644
  renderLinuxSystemUnit,
645
+ renderLinuxRecoverService,
646
+ renderLinuxRecoverTimer,
647
+ LINUX_RECOVER_SERVICE_NAME,
648
+ LINUX_RECOVER_TIMER_NAME,
649
+ LINUX_RECOVER_SERVICE_PATH,
650
+ LINUX_RECOVER_TIMER_PATH,
530
651
  renderLinuxEnvFile,
531
652
  escapeXml,
532
653
  escapeForSystemdEnv,
@@ -547,4 +668,4 @@ export {
547
668
  uninstallSupervisor,
548
669
  uninstallSystemUnit
549
670
  };
550
- //# sourceMappingURL=manager-supervisor-IVMG2ACE.js.map
671
+ //# sourceMappingURL=manager-supervisor-KIFBGETA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/manager-supervisor.ts"],"sourcesContent":["/**\n * OS-level supervision for the agt manager daemon (ENG-4593).\n *\n * Hands manager lifecycle off to the OS so a missing process is the OS's\n * problem, not ours. Replaces the in-process `agt manager start\n * --supervise` wrapper from ENG-4488.\n *\n * macOS: launchd LaunchAgent (KeepAlive=true + ThrottleInterval=10)\n * Linux: systemd --user unit (Restart=always + RestartSec=5) [Phase 2]\n * systemd system unit (Restart=on-failure, root) [Phase 3 / ENG-4706]\n *\n * Public surface:\n * - installSupervisor(opts) — write the unit, load/enable it, verify\n * - uninstallSupervisor() — unload/disable + remove the unit (idempotent)\n * - supervisorStatus() — quick readout of whether the OS knows about us\n *\n * - installSystemUnit(opts) — Linux only, root-only. Writes\n * /etc/systemd/system/agt-manager.service\n * so the manager survives EC2 reboot\n * without `loginctl enable-linger`\n * gymnastics. Used by host-bootstrap and\n * the SSM backfill runbook (ENG-4706).\n * - uninstallSystemUnit()\n * - systemUnitStatus()\n *\n * Each platform implementation lives in its own helper. The dispatcher\n * picks one based on `process.platform` and rejects unsupported OSes\n * with a clear error rather than silently no-oping.\n */\n\nimport { execFileSync } from 'node:child_process';\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n unlinkSync,\n writeFileSync,\n chmodSync,\n} from 'node:fs';\nimport { homedir, platform } from 'node:os';\nimport { dirname, join } from 'node:path';\n\n// macOS LaunchAgent label and on-disk path. Stable so install/uninstall\n// can find each other without a state file.\nconst MACOS_LABEL = 'team.augmented.agt-manager';\nconst MACOS_PLIST = join(homedir(), 'Library', 'LaunchAgents', `${MACOS_LABEL}.plist`);\n\n// Linux systemd --user unit. Lives under the operator's XDG config so it\n// survives reboot only after `loginctl enable-linger` (we surface that as\n// a follow-up step rather than running it ourselves — needs root).\nconst LINUX_UNIT_NAME = 'agt-manager.service';\nconst LINUX_UNIT_PATH = join(homedir(), '.config', 'systemd', 'user', LINUX_UNIT_NAME);\n// Secrets live in a separate EnvironmentFile (mode 0600) instead of\n// being inlined into the unit body. The unit file itself is still\n// 0600 but plain Environment= lines in it are visible to anything\n// that can read systemd's parsed unit cache (`systemctl show`,\n// `systemd-cgls --all`, etc.). EnvironmentFile content stays private\n// to the systemd manager process.\nconst LINUX_ENV_FILE_PATH = join(homedir(), '.config', 'systemd', 'user', 'agt-manager.env');\n\nexport interface InstallOpts {\n /** Absolute path to the agt binary the supervisor should launch. */\n agtBin: string;\n /** Poll interval passed to `agt manager start`. Min 5 seconds. */\n intervalSec: number;\n /** Manager config directory (where pid/log/state files live). */\n configDir: string;\n /**\n * Environment variables the supervised manager needs at launch time.\n * AGT_HOST and AGT_API_KEY are required for the manager to call the\n * API; AGT_TEAM is optional but typical.\n */\n env: Record<string, string>;\n}\n\nexport type SupervisorPresence =\n | { kind: 'installed'; pid: number | null; details: string }\n | { kind: 'not-installed' }\n | { kind: 'unsupported-platform'; platform: string };\n\n// ---------------------------------------------------------------------------\n// Public dispatcher\n// ---------------------------------------------------------------------------\n\nexport async function installSupervisor(opts: InstallOpts): Promise<{ ok: true; details: string } | { ok: false; error: string }> {\n switch (platform()) {\n case 'darwin':\n return installMacos(opts);\n case 'linux':\n return installLinux(opts);\n default:\n return { ok: false, error: `Unsupported platform: ${platform()}. Supervisor install is macOS / Linux only.` };\n }\n}\n\nexport async function uninstallSupervisor(): Promise<{ ok: true; details: string } | { ok: false; error: string }> {\n switch (platform()) {\n case 'darwin':\n return uninstallMacos();\n case 'linux':\n return uninstallLinux();\n default:\n return { ok: false, error: `Unsupported platform: ${platform()}. Supervisor uninstall is macOS / Linux only.` };\n }\n}\n\nexport function supervisorStatus(): SupervisorPresence {\n switch (platform()) {\n case 'darwin':\n return statusMacos();\n case 'linux':\n return statusLinux();\n default:\n return { kind: 'unsupported-platform', platform: platform() };\n }\n}\n\n// ---------------------------------------------------------------------------\n// macOS — launchd LaunchAgent\n// ---------------------------------------------------------------------------\n\nfunction installMacos(opts: InstallOpts): { ok: true; details: string } | { ok: false; error: string } {\n if (!existsSync(opts.agtBin)) {\n return { ok: false, error: `agt binary not found at ${opts.agtBin}` };\n }\n if (opts.intervalSec < 5) {\n return { ok: false, error: 'intervalSec must be >= 5' };\n }\n if (!opts.env.AGT_HOST || !opts.env.AGT_API_KEY) {\n return { ok: false, error: 'AGT_HOST and AGT_API_KEY must be set in the calling shell so the supervisor can pass them to the manager. Run `agt setup <token>` first.' };\n }\n\n // If a previous version is already loaded, unload it before rewriting\n // the plist — otherwise launchctl bootstrap will fail with EALREADY.\n bestEffort(() => execFileSync('launchctl', ['unload', '-w', MACOS_PLIST], { stdio: 'ignore' }));\n\n // Ensure the LaunchAgents directory exists. macOS creates it on first\n // login, but freshly-installed admin users may not have it yet.\n mkdirSync(dirname(MACOS_PLIST), { recursive: true });\n // 0o600 — plist embeds AGT_API_KEY in EnvironmentVariables. World-\n // readable would expose the host's tlk_ token to every local user.\n writeFileSync(MACOS_PLIST, renderMacosPlist(opts), { mode: 0o600 });\n chmodSync(MACOS_PLIST, 0o600);\n\n try {\n execFileSync('launchctl', ['load', '-w', MACOS_PLIST], { stdio: 'pipe' });\n } catch (err) {\n return { ok: false, error: `launchctl load failed: ${(err as Error).message}` };\n }\n\n // Verify launchd actually picked it up. `list <label>` returns 0 with\n // a plist of the loaded job's metadata; non-zero if not loaded.\n try {\n const out = execFileSync('launchctl', ['list', MACOS_LABEL], { encoding: 'utf-8' });\n return {\n ok: true,\n details: `Loaded launchd LaunchAgent ${MACOS_LABEL}. Logs: ${join(opts.configDir, 'manager.log')}.\\n${out.trim().split('\\n').slice(0, 10).join('\\n')}`,\n };\n } catch (err) {\n return { ok: false, error: `Plist written but launchctl list reported nothing for ${MACOS_LABEL}: ${(err as Error).message}` };\n }\n}\n\nfunction uninstallMacos(): { ok: true; details: string } | { ok: false; error: string } {\n // Both steps are idempotent — unload reports an error if nothing's\n // loaded, removing the file errors if it doesn't exist. Swallow both;\n // the desired end state is \"no LaunchAgent for this label\".\n bestEffort(() => execFileSync('launchctl', ['unload', '-w', MACOS_PLIST], { stdio: 'ignore' }));\n let removed = false;\n if (existsSync(MACOS_PLIST)) {\n try {\n unlinkSync(MACOS_PLIST);\n removed = true;\n } catch (err) {\n return { ok: false, error: `Failed to remove ${MACOS_PLIST}: ${(err as Error).message}` };\n }\n }\n return {\n ok: true,\n details: removed\n ? `Removed ${MACOS_LABEL} from launchd and deleted ${MACOS_PLIST}.`\n : `${MACOS_LABEL} was already uninstalled (no plist at ${MACOS_PLIST}).`,\n };\n}\n\nfunction statusMacos(): SupervisorPresence {\n if (!existsSync(MACOS_PLIST)) return { kind: 'not-installed' };\n // launchctl list emits a 3-column line: PID Status Label. PID is \"-\"\n // when the job is loaded but not currently running.\n try {\n const out = execFileSync('launchctl', ['list', MACOS_LABEL], { encoding: 'utf-8' });\n const pidMatch = /\"PID\"\\s*=\\s*(\\d+);/.exec(out);\n const pid = pidMatch ? Number(pidMatch[1]) : null;\n return { kind: 'installed', pid, details: `Plist: ${MACOS_PLIST}` };\n } catch {\n // File exists but launchctl can't find the label → user removed it\n // out-of-band. Surface as \"not installed\" so re-install just works.\n return { kind: 'not-installed' };\n }\n}\n\nfunction renderMacosPlist(opts: InstallOpts): string {\n // Load the operator's env into the plist so the manager has the same\n // AGT_HOST / AGT_API_KEY / AGT_TEAM the shell session does. launchd\n // does NOT inherit env from the user's shell — anything we don't bake\n // in here won't be visible to the process.\n const envEntries = Object.entries(opts.env)\n .filter(([k, v]) => k.length > 0 && v != null)\n .map(([k, v]) => ` <key>${escapeXml(k)}</key>\\n <string>${escapeXml(v)}</string>`)\n .join('\\n');\n\n const args = [\n 'manager',\n 'start',\n '--interval',\n String(opts.intervalSec),\n '--config-dir',\n opts.configDir,\n ];\n const argsXml = args.map((a) => ` <string>${escapeXml(a)}</string>`).join('\\n');\n\n const logPath = join(opts.configDir, 'manager.log');\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${MACOS_LABEL}</string>\n\n <key>ProgramArguments</key>\n <array>\n <string>${escapeXml(opts.agtBin)}</string>\n${argsXml}\n </array>\n\n <key>RunAtLoad</key>\n <true/>\n\n <key>KeepAlive</key>\n <true/>\n\n <!-- Throttle restarts so a crashloop doesn't peg the CPU. 10 seconds\n between launches is enough room for transient API blips while\n still recovering quickly from a real crash. -->\n <key>ThrottleInterval</key>\n <integer>10</integer>\n\n <!-- Where the manager's stdout / stderr land. The manager's logger\n redirects stderr into manager.log itself, but launchd needs an\n explicit destination at the OS level for any pre-logger output\n (e.g., Node startup errors before the watchdog wires up). -->\n <key>StandardOutPath</key>\n <string>${escapeXml(logPath)}</string>\n <key>StandardErrorPath</key>\n <string>${escapeXml(logPath)}</string>\n\n <key>WorkingDirectory</key>\n <string>${escapeXml(opts.configDir)}</string>\n\n <key>EnvironmentVariables</key>\n <dict>\n${envEntries}\n </dict>\n</dict>\n</plist>\n`;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction bestEffort(fn: () => void): void {\n try { fn(); } catch { /* swallow */ }\n}\n\nfunction escapeXml(s: string): string {\n return s\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&apos;');\n}\n\n// ---------------------------------------------------------------------------\n// Linux — systemd --user unit\n// ---------------------------------------------------------------------------\n\nfunction installLinux(opts: InstallOpts): { ok: true; details: string } | { ok: false; error: string } {\n if (!existsSync(opts.agtBin)) {\n return { ok: false, error: `agt binary not found at ${opts.agtBin}` };\n }\n if (opts.intervalSec < 5) {\n return { ok: false, error: 'intervalSec must be >= 5' };\n }\n if (!opts.env.AGT_HOST || !opts.env.AGT_API_KEY) {\n return { ok: false, error: 'AGT_HOST and AGT_API_KEY must be set in the calling shell so the supervisor can pass them to the unit. Run `agt setup <token>` first.' };\n }\n\n // Stop any previous instance before rewriting the unit — saves a\n // restart loop while daemon-reload picks up the new file.\n bestEffort(() => execFileSync('systemctl', ['--user', 'stop', LINUX_UNIT_NAME], { stdio: 'ignore' }));\n\n // ~/.config/systemd/user/ is the canonical user-unit path. Create it\n // recursively in case this is a fresh user with no XDG layout yet.\n mkdirSync(dirname(LINUX_UNIT_PATH), { recursive: true, mode: 0o700 });\n // Write the secrets-only env file FIRST (mode 0600) so the unit can\n // reference it via EnvironmentFile=. Non-secret env stays in the\n // unit body via Environment= lines for visibility.\n writeFileSync(LINUX_ENV_FILE_PATH, renderLinuxEnvFile(opts), { mode: 0o600 });\n chmodSync(LINUX_ENV_FILE_PATH, 0o600);\n writeFileSync(LINUX_UNIT_PATH, renderLinuxUnit(opts), { mode: 0o600 });\n chmodSync(LINUX_UNIT_PATH, 0o600);\n\n try {\n execFileSync('systemctl', ['--user', 'daemon-reload'], { stdio: 'pipe' });\n } catch (err) {\n return { ok: false, error: `systemctl --user daemon-reload failed: ${(err as Error).message}` };\n }\n\n // `enable --now` enables the unit at boot AND starts it immediately.\n // Surviving reboot on a headless host additionally requires\n // `loginctl enable-linger <user>` (root-only); we surface that as a\n // post-install hint rather than running it ourselves.\n try {\n execFileSync('systemctl', ['--user', 'enable', '--now', LINUX_UNIT_NAME], { stdio: 'pipe' });\n } catch (err) {\n return { ok: false, error: `systemctl --user enable --now failed: ${(err as Error).message}. (Hint: 'systemctl --user' requires a per-user dbus session; on a fresh SSH login try 'export XDG_RUNTIME_DIR=/run/user/$(id -u)' first.)` };\n }\n\n let isActive = '';\n try {\n isActive = execFileSync('systemctl', ['--user', 'is-active', LINUX_UNIT_NAME], { encoding: 'utf-8' }).trim();\n } catch (err) {\n // is-active exits non-zero for inactive/failed states. Capture its\n // output so the operator sees the real status.\n isActive = ((err as { stdout?: Buffer; stderr?: Buffer }).stdout?.toString() ?? '').trim() || 'unknown';\n }\n\n // Don't claim the install succeeded if the unit ended up failed /\n // inactive / unknown. Without this, the CLI would exit 0 with a\n // green \"supervisor installed\" message while the manager was\n // already broken.\n if (isActive !== 'active') {\n return {\n ok: false,\n error: `systemd loaded ${LINUX_UNIT_NAME} but it did not become active (status: ${isActive}). Run 'systemctl --user status ${LINUX_UNIT_NAME}' or 'journalctl --user -u ${LINUX_UNIT_NAME} -n 50' for details.`,\n };\n }\n\n return {\n ok: true,\n details: `Loaded systemd --user unit ${LINUX_UNIT_NAME} (status: ${isActive}). Logs: ${join(opts.configDir, 'manager.log')}.\\nFor reboot survival on a headless host run as root: 'loginctl enable-linger ${process.env['USER'] ?? '<your-user>'}'`,\n };\n}\n\nfunction uninstallLinux(): { ok: true; details: string } | { ok: false; error: string } {\n // disable --now stops + un-enables in one shot. Both legs are idempotent\n // — disable on a non-existent unit returns 0 with a notice on stderr.\n bestEffort(() => execFileSync('systemctl', ['--user', 'disable', '--now', LINUX_UNIT_NAME], { stdio: 'ignore' }));\n\n let removed = false;\n if (existsSync(LINUX_UNIT_PATH)) {\n try {\n unlinkSync(LINUX_UNIT_PATH);\n removed = true;\n } catch (err) {\n return { ok: false, error: `Failed to remove ${LINUX_UNIT_PATH}: ${(err as Error).message}` };\n }\n }\n // Best-effort cleanup of the secrets-only env file too; we don't\n // want a stale AGT_API_KEY left on disk after uninstall.\n if (existsSync(LINUX_ENV_FILE_PATH)) {\n bestEffort(() => unlinkSync(LINUX_ENV_FILE_PATH));\n }\n\n // Reload so a future install with the same path doesn't reuse the\n // stale unit metadata systemd cached in memory.\n bestEffort(() => execFileSync('systemctl', ['--user', 'daemon-reload'], { stdio: 'ignore' }));\n\n return {\n ok: true,\n details: removed\n ? `Disabled systemd --user unit ${LINUX_UNIT_NAME} and deleted ${LINUX_UNIT_PATH}.`\n : `${LINUX_UNIT_NAME} was already uninstalled (no unit file at ${LINUX_UNIT_PATH}).`,\n };\n}\n\nfunction statusLinux(): SupervisorPresence {\n if (!existsSync(LINUX_UNIT_PATH)) return { kind: 'not-installed' };\n let pid: number | null = null;\n let activeState = 'unknown';\n try {\n const out = execFileSync(\n 'systemctl',\n ['--user', 'show', LINUX_UNIT_NAME, '--property=MainPID,ActiveState'],\n { encoding: 'utf-8' },\n );\n const pidMatch = /^MainPID=(\\d+)/m.exec(out);\n if (pidMatch && pidMatch[1] !== '0') pid = Number(pidMatch[1]);\n const stateMatch = /^ActiveState=(\\S+)/m.exec(out);\n if (stateMatch) activeState = stateMatch[1] ?? 'unknown';\n } catch (err) {\n // The unit file exists but `systemctl --user` couldn't probe it\n // — most often because XDG_RUNTIME_DIR / the per-user dbus isn't\n // available in this shell. Don't lie to the operator about\n // whether the supervisor is installed; report 'installed,\n // unknown state' instead.\n return {\n kind: 'installed',\n pid: null,\n details: `Unit: ${LINUX_UNIT_PATH} (could not query systemd --user: ${(err as Error).message})`,\n };\n }\n return { kind: 'installed', pid, details: `Unit: ${LINUX_UNIT_PATH} (${activeState})` };\n}\n\n// Secrets that go into EnvironmentFile= instead of inline Environment=.\n// Anything in this set is written to LINUX_ENV_FILE_PATH (mode 0600)\n// and stripped from the unit body. Public env (PATH, AGT_HOST, AGT_TEAM)\n// stays inline for visibility in `systemctl show`.\nconst LINUX_SECRET_ENV_KEYS: ReadonlySet<string> = new Set(['AGT_API_KEY']);\n\nfunction renderLinuxUnit(opts: InstallOpts): string {\n // Non-secret env stays in the unit body via Environment= lines;\n // secrets are pulled from the EnvironmentFile= written separately.\n // systemd parses Environment= with a peculiar grammar — quotes are\n // optional but values can't contain literal newlines. We trust the\n // env values we forward and quote everything to be safe with spaces.\n const envLines = Object.entries(opts.env)\n .filter(([k, v]) => k.length > 0 && v != null && !LINUX_SECRET_ENV_KEYS.has(k))\n .map(([k, v]) => `Environment=\"${k}=${escapeForSystemdEnv(v)}\"`)\n .join('\\n');\n\n // ExecStart cannot use shell metacharacters; pass the args explicitly\n // via systemd's array-of-args style (newline-separated wouldn't\n // parse — systemd splits on whitespace within ExecStart). So we\n // shell-quote each arg and join.\n const execArgs = [\n opts.agtBin,\n 'manager',\n 'start',\n '--interval',\n String(opts.intervalSec),\n '--config-dir',\n opts.configDir,\n ]\n .map(shellQuoteForSystemd)\n .join(' ');\n\n const logPath = join(opts.configDir, 'manager.log');\n\n return `# agt manager supervisor (ENG-4593)\n# Managed by \\`agt manager install\\` — edits will be overwritten.\n\n[Unit]\nDescription=Augmented host manager daemon\nAfter=network-online.target\nWants=network-online.target\n# Restart rate-limit lives here per systemd.unit(5). Older systemds\n# tolerated these directives in the service section but newer ones\n# silently ignore them there, leaving the crashloop unprotected.\nStartLimitBurst=5\nStartLimitIntervalSec=60\n\n[Service]\nType=simple\nWorkingDirectory=${opts.configDir}\n${envLines}\nEnvironmentFile=${LINUX_ENV_FILE_PATH}\nExecStart=${execArgs}\n\n# Always come back from any exit; back off 5s between launches so a\n# crashloop can't peg the CPU. The 5/60s window above caps total\n# attempts before systemd marks the unit failed.\nRestart=always\nRestartSec=5\n\n# stdout/stderr → manager.log. journalctl --user -u agt-manager\n# still captures everything; this just gives operators a single file\n# to tail without learning systemd plumbing.\nStandardOutput=append:${logPath}\nStandardError=append:${logPath}\n\n[Install]\nWantedBy=default.target\n`;\n}\n\n/**\n * Render the secrets-only env file referenced by EnvironmentFile= in\n * the unit. Format: `KEY=VALUE` per line (systemd's EnvironmentFile\n * parser doesn't support quoting the value the way Environment= does,\n * but it tolerates literal characters as long as the value has no\n * newline). One key per line; trailing newline.\n */\nfunction renderLinuxEnvFile(opts: InstallOpts): string {\n return Object.entries(opts.env)\n .filter(([k, v]) => LINUX_SECRET_ENV_KEYS.has(k) && v != null)\n .map(([k, v]) => `${k}=${v}\\n`)\n .join('');\n}\n\nfunction escapeForSystemdEnv(value: string): string {\n // Inside an Environment=\"K=V\" line, \" and \\\\ need escaping. Newlines\n // would terminate the directive — reject them upstream.\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n}\n\nfunction shellQuoteForSystemd(arg: string): string {\n // systemd's ExecStart parser supports double-quoted strings with\n // backslash escapes, similar to a POSIX shell but no command\n // substitution. Wrap any arg containing whitespace or shell metas.\n if (/^[A-Za-z0-9_./:=-]+$/.test(arg)) return arg;\n return `\"${arg.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"')}\"`;\n}\n\n// ---------------------------------------------------------------------------\n// Linux — systemd system unit (ENG-4706)\n// ---------------------------------------------------------------------------\n//\n// The `--user` unit above survives reboot only if `loginctl enable-linger`\n// is set per-user. On headless EC2 hosts that's a footgun: a reboot (kernel\n// patch, instance-type change, AWS-initiated maintenance) silently takes\n// every agent on the host offline until an operator manually re-launches\n// the manager. The system unit lives at /etc/systemd/system/, runs as root,\n// and is enabled into multi-user.target — so it comes up automatically at\n// boot just like any other service.\n//\n// Both unit kinds can co-exist; statusLinux() looks at the user one,\n// systemUnitStatus() looks at the system one. The CLI surfaces them as\n// `agt manager install` (user, dev workflow) and `agt manager\n// install-system-unit` (root, host-infra workflow) so the operator\n// chooses explicitly.\n\nconst LINUX_SYSTEM_UNIT_PATH = '/etc/systemd/system/agt-manager.service';\nconst LINUX_SYSTEM_ENV_FILE_PATH = '/etc/agt/agt-manager.env';\n// ENG-11695: resets and restarts the manager after StartLimitBurst latches it.\nconst LINUX_RECOVER_SERVICE_NAME = 'agt-manager-recover.service';\nconst LINUX_RECOVER_TIMER_NAME = 'agt-manager-recover.timer';\nconst LINUX_RECOVER_SERVICE_PATH = `/etc/systemd/system/${LINUX_RECOVER_SERVICE_NAME}`;\nconst LINUX_RECOVER_TIMER_PATH = `/etc/systemd/system/${LINUX_RECOVER_TIMER_NAME}`;\n\nexport interface InstallSystemUnitOpts extends InstallOpts {\n /**\n * Unix user the manager runs as. EC2 default is `root`. Other hosts\n * can run it as a less-privileged user, but the user must own\n * `configDir` and have permission to write to it. Defaults to `root`\n * when omitted (matches the ticket spec).\n */\n user?: string;\n}\n\nexport async function installSystemUnit(\n opts: InstallSystemUnitOpts,\n): Promise<{ ok: true; details: string } | { ok: false; error: string }> {\n if (platform() !== 'linux') {\n return {\n ok: false,\n error: `installSystemUnit is Linux-only (current platform: ${platform()}). For local dev on macOS use installSupervisor.`,\n };\n }\n if (process.getuid?.() !== 0) {\n return {\n ok: false,\n error: 'installSystemUnit requires root. Re-run with sudo, or call from the EC2 user-data bootstrap which already runs as root.',\n };\n }\n if (!existsSync(opts.agtBin)) {\n return { ok: false, error: `agt binary not found at ${opts.agtBin}` };\n }\n if (opts.intervalSec < 5) {\n return { ok: false, error: 'intervalSec must be >= 5' };\n }\n if (!opts.env.AGT_HOST || !opts.env.AGT_API_KEY) {\n return {\n ok: false,\n error: 'AGT_HOST and AGT_API_KEY must be set so the system unit can pass them through. Run `agt setup <token>` first (it writes them to /etc/environment, which this installer reads).',\n };\n }\n\n // Stop any running instance so daemon-reload picks up the rewritten\n // unit cleanly without a respawn race.\n bestEffort(() => execFileSync('systemctl', ['stop', LINUX_UNIT_NAME], { stdio: 'ignore' }));\n\n // Belt-and-braces (ENG-4706 backfill): kill any pre-existing manager\n // process that wasn't started via systemd. On hosts that were\n // bootstrapped before this PR, the manager runs nohup'd from a one-\n // shot SSM command. Installing the system unit without stopping that\n // older process leaves TWO managers polling Supabase simultaneously —\n // they race on agent ticks, double-inject channel messages, and\n // double-write audit_log rows. SIGTERM gives it 3s to wind down\n // cleanly before the systemd start grabs the supervisor seat.\n // Ignored on hosts that have no rogue manager (the typical fresh-\n // bootstrap path).\n bestEffort(() => {\n const out = execFileSync('pgrep', ['-f', 'agt manager start'], { stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf-8' });\n const pids = out\n .split('\\n')\n .map((s) => s.trim())\n .filter((s) => s.length > 0 && /^\\d+$/.test(s) && Number(s) !== process.pid);\n if (pids.length === 0) return;\n for (const pid of pids) {\n bestEffort(() => process.kill(Number(pid), 'SIGTERM'));\n }\n // Brief settle so the just-killed manager releases its Supabase\n // long-polls before the new one connects. 3s matches the default\n // poll interval; a manager that hasn't shut down in that window\n // gets SIGKILLed below.\n const deadline = Date.now() + 3_000;\n while (Date.now() < deadline) {\n try {\n execFileSync('pgrep', ['-f', 'agt manager start'], { stdio: 'ignore' });\n } catch {\n return;\n }\n // Spin — busy-wait is fine for a 3s ceiling and avoids pulling in\n // a sleep helper. This path runs once at install, never on the\n // hot poll loop.\n }\n // Still alive — escalate.\n for (const pid of pids) {\n bestEffort(() => process.kill(Number(pid), 'SIGKILL'));\n }\n });\n\n // Secrets-only env file lives under /etc/agt/ (root:root, 0600). Same\n // rationale as the user-unit env file: keep AGT_API_KEY out of any\n // tool that reads systemd's parsed unit cache.\n mkdirSync(dirname(LINUX_SYSTEM_ENV_FILE_PATH), { recursive: true, mode: 0o700 });\n writeFileSync(LINUX_SYSTEM_ENV_FILE_PATH, renderLinuxEnvFile(opts), { mode: 0o600 });\n chmodSync(LINUX_SYSTEM_ENV_FILE_PATH, 0o600);\n writeFileSync(LINUX_SYSTEM_UNIT_PATH, renderLinuxSystemUnit(opts), { mode: 0o644 });\n chmodSync(LINUX_SYSTEM_UNIT_PATH, 0o644);\n writeFileSync(LINUX_RECOVER_SERVICE_PATH, renderLinuxRecoverService(), { mode: 0o644 });\n chmodSync(LINUX_RECOVER_SERVICE_PATH, 0o644);\n writeFileSync(LINUX_RECOVER_TIMER_PATH, renderLinuxRecoverTimer(), { mode: 0o644 });\n chmodSync(LINUX_RECOVER_TIMER_PATH, 0o644);\n\n try {\n execFileSync('systemctl', ['daemon-reload'], { stdio: 'pipe' });\n } catch (err) {\n return { ok: false, error: `systemctl daemon-reload failed: ${(err as Error).message}` };\n }\n\n // ENG-11695: enabled BEFORE the manager's own start, so a manager that fails\n // to come up right here is still recovered later. It must not fail the\n // install: the manager runs without it, as it always has, and the failure is\n // reported in the result instead.\n let recoverTimerNote = `Recovery timer ${LINUX_RECOVER_TIMER_NAME} enabled.`;\n try {\n execFileSync('systemctl', ['enable', '--now', LINUX_RECOVER_TIMER_NAME], { stdio: 'pipe' });\n } catch (err) {\n recoverTimerNote = `WARNING: could not enable ${LINUX_RECOVER_TIMER_NAME}, so this host will not recover by itself from StartLimitBurst: ${(err as Error).message}`;\n }\n\n // `enable --now` enables at boot AND starts immediately. Unlike the\n // --user variant, this needs no enable-linger — multi-user.target\n // pulls us in regardless of any user session.\n try {\n execFileSync('systemctl', ['enable', '--now', LINUX_UNIT_NAME], { stdio: 'pipe' });\n } catch (err) {\n return { ok: false, error: `systemctl enable --now failed: ${(err as Error).message}` };\n }\n\n let isActive = '';\n try {\n isActive = execFileSync('systemctl', ['is-active', LINUX_UNIT_NAME], { encoding: 'utf-8' }).trim();\n } catch (err) {\n isActive = ((err as { stdout?: Buffer; stderr?: Buffer }).stdout?.toString() ?? '').trim() || 'unknown';\n }\n\n if (isActive !== 'active') {\n return {\n ok: false,\n error: `systemd loaded ${LINUX_UNIT_NAME} but it did not become active (status: ${isActive}). Run 'systemctl status ${LINUX_UNIT_NAME}' or 'journalctl -u ${LINUX_UNIT_NAME} -n 50' for details.`,\n };\n }\n\n return {\n ok: true,\n details: `Loaded systemd system unit ${LINUX_UNIT_NAME} at ${LINUX_SYSTEM_UNIT_PATH} (status: ${isActive}). Will auto-start on reboot via multi-user.target. Logs: ${join(opts.configDir, 'manager.log')} and 'journalctl -u ${LINUX_UNIT_NAME}'. ${recoverTimerNote}`,\n };\n}\n\nexport async function uninstallSystemUnit(): Promise<{ ok: true; details: string } | { ok: false; error: string }> {\n if (platform() !== 'linux') {\n return { ok: false, error: `uninstallSystemUnit is Linux-only (current platform: ${platform()}).` };\n }\n if (process.getuid?.() !== 0) {\n return { ok: false, error: 'uninstallSystemUnit requires root.' };\n }\n\n // ENG-11695: recovery goes first, or it could restart what this is removing. The\n // service too, because stopping the timer doesn't stop a run already in progress\n // (Obi Wan b12268328098).\n bestEffort(() => execFileSync('systemctl', ['disable', '--now', LINUX_RECOVER_TIMER_NAME], { stdio: 'ignore' }));\n bestEffort(() => execFileSync('systemctl', ['stop', LINUX_RECOVER_SERVICE_NAME], { stdio: 'ignore' }));\n bestEffort(() => execFileSync('systemctl', ['disable', '--now', LINUX_UNIT_NAME], { stdio: 'ignore' }));\n\n for (const path of [LINUX_RECOVER_TIMER_PATH, LINUX_RECOVER_SERVICE_PATH]) {\n if (existsSync(path)) bestEffort(() => unlinkSync(path));\n }\n\n let removed = false;\n if (existsSync(LINUX_SYSTEM_UNIT_PATH)) {\n try {\n unlinkSync(LINUX_SYSTEM_UNIT_PATH);\n removed = true;\n } catch (err) {\n return { ok: false, error: `Failed to remove ${LINUX_SYSTEM_UNIT_PATH}: ${(err as Error).message}` };\n }\n }\n if (existsSync(LINUX_SYSTEM_ENV_FILE_PATH)) {\n bestEffort(() => unlinkSync(LINUX_SYSTEM_ENV_FILE_PATH));\n }\n\n bestEffort(() => execFileSync('systemctl', ['daemon-reload'], { stdio: 'ignore' }));\n\n return {\n ok: true,\n details: removed\n ? `Disabled systemd system unit ${LINUX_UNIT_NAME} and deleted ${LINUX_SYSTEM_UNIT_PATH}.`\n : `${LINUX_UNIT_NAME} system unit was already uninstalled (no unit file at ${LINUX_SYSTEM_UNIT_PATH}).`,\n };\n}\n\nexport function systemUnitStatus(): SupervisorPresence {\n if (platform() !== 'linux') {\n return { kind: 'unsupported-platform', platform: platform() };\n }\n if (!existsSync(LINUX_SYSTEM_UNIT_PATH)) return { kind: 'not-installed' };\n let pid: number | null = null;\n let activeState = 'unknown';\n try {\n const out = execFileSync(\n 'systemctl',\n ['show', LINUX_UNIT_NAME, '--property=MainPID,ActiveState'],\n { encoding: 'utf-8' },\n );\n const pidMatch = /^MainPID=(\\d+)/m.exec(out);\n if (pidMatch && pidMatch[1] !== '0') pid = Number(pidMatch[1]);\n const stateMatch = /^ActiveState=(\\S+)/m.exec(out);\n if (stateMatch) activeState = stateMatch[1] ?? 'unknown';\n } catch (err) {\n return {\n kind: 'installed',\n pid: null,\n details: `Unit: ${LINUX_SYSTEM_UNIT_PATH} (could not query systemd: ${(err as Error).message})`,\n };\n }\n return { kind: 'installed', pid, details: `Unit: ${LINUX_SYSTEM_UNIT_PATH} (${activeState})` };\n}\n\n/**\n * ENG-6698: ExecStartPre self-heal for a missing/dangling `agt` binary.\n *\n * The incident (agt-aws-1, full-fleet outage): a CLI self-update killed\n * mid-`npm i -g` (npm removes the old package before writing the new) left\n * `/usr/bin/agt` a dangling symlink, so `ExecStart` failed with 203/EXEC and\n * systemd crash-looped the unit to `failed`. With no recovery path the whole\n * fleet went dark until an operator manually reinstalled over SSM.\n *\n * This guard runs before every `ExecStart`: if `agt --version` won't run, it\n * reinstalls the pinned version (ENG-7833, from the unit's AGT_CLI_PIN_VERSION\n * env) or, when unpinned, the SAME release channel (AGT_CLI_RELEASE_CHANNEL,\n * default `latest`) before the manager starts, so a dangling binary self-heals\n * on the next start attempt instead of latching to `failed` with no way back.\n * Preferring the pin here matters: reinstalling the channel tip on a pinned\n * host would silently un-pin it.\n *\n * Two escaping notes that are load-bearing:\n * - systemd does its OWN `${VAR}` expansion on Exec lines (and does not\n * support the POSIX `:-default` form), so every shell `$` is written as\n * `$$` to pass through verbatim to /bin/sh, which then does the\n * `${VAR:-latest}` expansion itself.\n * - the script is one single-quoted `/bin/sh -c` argument with no single\n * quotes inside; `agtBin` is a filesystem path (no single quotes on our\n * hosts) and is double-quoted so a stray space can't split the arg.\n *\n * ENG-11695 (loyalty-host-2, 26 Oct): a reinstall alone can't work on a full\n * disk, or fix a self-referencing link. So when the binary won't run, this now\n * makes room first and relinks afterwards. A healthy host still takes the first\n * `--version` and runs none of it.\n *\n * It has to be inline shell. It runs precisely when `agt` won't execute, so it\n * can't call into this package, and a helper script on disk would add a way for\n * the unit to fail that a healthy host doesn't have today.\n *\n * `procRoot` exists for tests, so they read a fixture and not a shared host's\n * real processes.\n */\nexport function buildSystemUnitRepairExecStartPre(\n agtBin: string,\n user: string,\n opts: { procRoot?: string } = {},\n): string {\n const sudo = user === 'root' ? '' : 'sudo -n ';\n const proc = opts.procRoot ?? '/proc';\n const bin = '\"' + agtBin + '\"';\n // ENG-7833: prefer the pin (AGT_CLI_PIN_VERSION) over the channel so a\n // dangling-binary repair reinstalls the PINNED version, not the channel tip;\n // otherwise the repair would silently un-pin the host on the next start.\n // Nested POSIX `:-`: pin if set, else channel, else `latest`.\n const versionExpansion = '${AGT_CLI_PIN_VERSION:-${AGT_CLI_RELEASE_CHANNEL:-latest}}';\n const installSpec = '\"@integrity-labs/agt-cli@' + versionExpansion + '\"';\n\n // Written as plain POSIX sh, then every `$` is doubled at the end. The unit\n // line constrains what can appear here: no single quote (it would end the\n // `sh -c` argument), no backslash (systemd unescapes inside quotes), and no\n // `%` (systemd specifiers). That is why `tr` takes `[:cntrl:]`, not `\\000`,\n // and why the root comparison is `-ef`, not `stat -c`.\n const script = [\n bin + ' --version >/dev/null 2>&1 || {',\n 'echo \"[agt-manager] agt binary missing/dangling - reinstalling @integrity-labs/agt-cli@' +\n versionExpansion +\n '\";',\n\n // ENG-11695: the shell twin of `anyPackageInstallRunning` and\n // `isPackageInstallArgv` in disk-guard.ts, because this runs when `agt`\n // cannot, so it cannot call them. Cleaning a package cache under a running\n // install can cost that install its binary (Obi Wan b8ed1339eaf2). Same\n // rules: the process itself must be npm/pnpm, a container's install does\n // not count, and a root or a /proc that cannot be read counts as busy. Also the\n // same limit: a non-root manager under `hidepid` can't see root's installs. The\n // fleet's manager runs as root, which hidepid doesn't restrict.\n 'busy() {',\n '[ -r ' + proc + '/self/cmdline ] || return 0;',\n 'for p in ' + proc + '/[0-9]*; do',\n 'c=$(tr \"[:cntrl:]\" \" \" < \"$p/cmdline\" 2>/dev/null) || continue;',\n 'set -f; set -- $c; set +f;',\n '[ $# -gt 0 ] || continue;',\n 'case \"${1##*/}\" in npm|pnpm) shift ;; node|nodejs) case \"${2##*/}\" in npm|npm-cli.js|pnpm|pnpm.js|pnpm.cjs) shift 2 ;; *) continue ;; esac ;; *) continue ;; esac;',\n 'n=0; for a in \"$@\"; do',\n 'case \"$a\" in -*) continue ;; esac;',\n 'case \"$a\" in',\n 'install|i|ci|add|update|upgrade|up) { [ ! -e \"$p/root\" ] || [ \"$p/root\" -ef / ]; } && return 0; break ;;',\n 'run|run-script|exec|x|dlx|test|start) [ \"$n\" = 0 ] && break ;;',\n 'esac;',\n 'n=$((n+1)); [ \"$n\" -ge 2 ] && break;',\n 'done;',\n 'done; return 1; };',\n 'pkgclean() { if busy; then echo \"[agt-manager] skipping $* - an npm/pnpm install is running\"; else \"$@\" >/dev/null 2>&1 || true; fi; };',\n\n // ENG-11695 ask 2: the same safe cleanup as SAFE_CLEANUP_STEPS, at the same\n // threshold as SELF_UPDATE_MIN_FREE_BYTES (1 GiB in KiB). Caches and logs\n // only, nothing deleted by path. Unlike the self-update guard this never\n // refuses the install: the binary is already broken, so there is nothing\n // left for a failed install to lose. A `df` that fails reads as low.\n 'set -- $(df -Pk / 2>/dev/null | tail -1);',\n 'if [ \"${4:-0}\" -lt 1048576 ]; then',\n 'echo \"[agt-manager] under 1 GiB free - running safe cleanup before reinstalling\";',\n // `timeout` goes INSIDE sudo: sudo forwards TERM but can't forward KILL, so a\n // `timeout` outside it kills sudo and leaves the step running, possibly into\n // the install below (Obi Wan 95130c6e0a21).\n sudo + 'timeout -k 10 120 journalctl --vacuum-size=200M >/dev/null 2>&1 || true;',\n 'pkgclean timeout -k 10 120 npm cache clean --force;',\n 'pkgclean timeout -k 10 120 pnpm store prune;',\n ...(sudo ? ['pkgclean sudo -n -H timeout -k 10 120 npm cache clean --force;'] : []),\n 'fi;',\n\n sudo + 'npm install -g ' + installSpec + ' --registry=https://registry.npmjs.org;',\n\n // ENG-11695 ask 1: npm does not always rewrite a link it did not create, so\n // a dangling or self-referencing one can survive a good install. Only ever\n // a link or nothing: a real file at this path is not ours to replace.\n 'if ! ' + bin + ' --version >/dev/null 2>&1; then',\n 't=\"$(' + sudo + 'npm root -g 2>/dev/null)/@integrity-labs/agt-cli/dist/bin/agt.js\";',\n 'if [ -f \"$t\" ] && { [ -L ' + bin + ' ] || [ ! -e ' + bin + ' ]; }; then',\n 'echo \"[agt-manager] relinking ' + agtBin + ' to $t\";',\n sudo + 'ln -sfn \"$t\" ' + bin + ';',\n 'fi;',\n 'fi;',\n\n // The exit status of the whole repair: did it work?\n bin + ' --version >/dev/null 2>&1;',\n '}',\n ].join(' ');\n\n // systemd does its own ${VAR} expansion, so `$` has to reach it as `$$`.\n return \"ExecStartPre=/bin/sh -c '\" + script.replace(/\\$/g, '$$$$') + \"'\";\n}\n\n/**\n * ENG-11695 ask 3: get out of `failed`.\n *\n * Once StartLimitBurst trips, systemd stops trying, and the ExecStartPre repair\n * above never runs again. A timer rather than `OnFailure=`, because a timer's\n * cadence is the bound: at most one episode (StartLimitBurst starts) per\n * interval, whatever the failure does. `OnFailure=` fires on each failure and\n * would need its own limit.\n *\n * `is-failed` leaves a clean `systemctl stop` alone, because that ends `inactive`.\n * It can't tell an operator's stop from a crash once the unit is `failed`, and a\n * stop can end there two ways: the manager was already `failed` (Obi Wan\n * 9ec9691edd4b), or its drain hung and the 15s backstop in stopPolling exited 1\n * (Obi Wan 08df74eca266). So a deliberate hold always means stopping this timer\n * too, and docs/host-infra/README.md says so.\n *\n * Don't close that gap with SuccessExitStatus=1: under Restart=on-failure a real\n * crash that exits 1 would then never restart. And don't filter on the unit's\n * Result here without testing it on a host. A filter that misses what a latched\n * unit actually reports turns off this recovery with no signal, and that costs far\n * more than a hung stop coming back.\n */\nconst RECOVER_INTERVAL = '15min';\n\nfunction renderLinuxRecoverService(): string {\n return `# agt manager recovery (ENG-11695)\n# Managed by \\`agt manager install-system-unit\\` — edits will be overwritten.\n\n[Unit]\nDescription=Restart the Augmented host manager if systemd has latched it to failed\n\n[Service]\nType=oneshot\nExecStart=/bin/sh -c 'systemctl is-failed --quiet ${LINUX_UNIT_NAME} || exit 0; echo \"[agt-manager-recover] ${LINUX_UNIT_NAME} is failed - resetting and starting\"; systemctl reset-failed ${LINUX_UNIT_NAME}; systemctl start --no-block ${LINUX_UNIT_NAME}'\n`;\n}\n\nfunction renderLinuxRecoverTimer(): string {\n return `# agt manager recovery timer (ENG-11695)\n# Managed by \\`agt manager install-system-unit\\` — edits will be overwritten.\n\n[Unit]\nDescription=Check every ${RECOVER_INTERVAL} whether the Augmented host manager is latched to failed\n\n[Timer]\nOnBootSec=${RECOVER_INTERVAL}\nOnUnitActiveSec=${RECOVER_INTERVAL}\nUnit=${LINUX_RECOVER_SERVICE_NAME}\n\n[Install]\nWantedBy=timers.target\n`;\n}\n\nfunction renderLinuxSystemUnit(opts: InstallSystemUnitOpts): string {\n const user = opts.user ?? 'root';\n const home = user === 'root' ? '/root' : `/home/${user}`;\n\n // Non-secret env stays in the unit body via Environment= lines;\n // secrets are pulled from EnvironmentFile= written separately.\n const envLines = Object.entries(opts.env)\n .filter(([k, v]) => k.length > 0 && v != null && !LINUX_SECRET_ENV_KEYS.has(k))\n .map(([k, v]) => `Environment=\"${k}=${escapeForSystemdEnv(v)}\"`)\n .join('\\n');\n\n const execArgs = [\n opts.agtBin,\n 'manager',\n 'start',\n '--interval',\n String(opts.intervalSec),\n '--config-dir',\n opts.configDir,\n ]\n .map(shellQuoteForSystemd)\n .join(' ');\n\n const logPath = join(opts.configDir, 'manager.log');\n\n return `# agt manager supervisor — system unit (ENG-4706)\n# Managed by \\`agt manager install-system-unit\\` — edits will be overwritten.\n#\n# Unlike the sibling --user unit at ~/.config/systemd/user/agt-manager.service,\n# this one is enabled into multi-user.target so it auto-starts on every\n# boot regardless of any user login. Use this on EC2 / headless hosts.\n\n[Unit]\nDescription=Augmented host manager daemon\nAfter=network-online.target\nWants=network-online.target\n# ENG-6698: widened from 5/60s. The ExecStartPre repair below reinstalls a\n# dangling agt binary on each start attempt (~7s of npm + RestartSec), so a\n# transient miss needs several attempts within the window to self-heal before\n# systemd would otherwise latch the unit to \\`failed\\`. 10 tries / 5 min gives\n# the repair room to recover without spinning forever on a genuinely broken host.\n# ENG-11695: if it does latch, ${LINUX_RECOVER_TIMER_NAME} starts another round.\nStartLimitBurst=10\nStartLimitIntervalSec=300\n\n[Service]\nType=simple\nUser=${user}\nWorkingDirectory=${opts.configDir}\nEnvironment=\"HOME=${home}\"\n${envLines}\nEnvironmentFile=${LINUX_SYSTEM_ENV_FILE_PATH}\n# ENG-6698: self-heal a missing/dangling agt binary before starting, so an\n# interrupted self-update can't latch the unit to \\`failed\\` with no recovery.\n${buildSystemUnitRepairExecStartPre(opts.agtBin, user)}\nExecStart=${execArgs}\n# ENG-11695: this limit covers ExecStartPre, and the default 90s can't fit the\n# repair on a full disk. Worst case is 4 cleanup steps x 130s (each under its own\n# \\`timeout -k 10 120\\`, so one that ignores TERM still can't eat the budget) plus\n# 200s for npm. A hung npm is killed here and retried. A healthy start never gets\n# close.\nTimeoutStartSec=720\n\n# on-failure (not always) so a clean \\`systemctl stop\\` doesn't fight a\n# respawn loop. The ExecStartPre repair + the widened StartLimit above turn a\n# dangling-binary crashloop into a self-healing one. Logs land in ${logPath}\n# via the manager's own writer plus journalctl -u ${LINUX_UNIT_NAME}.\nRestart=on-failure\nRestartSec=5\n\nStandardOutput=append:${logPath}\nStandardError=append:${logPath}\n\n[Install]\nWantedBy=multi-user.target\n`;\n}\n\n// Exposed for unit tests / introspection.\nexport const _internals = {\n MACOS_LABEL,\n MACOS_PLIST,\n LINUX_UNIT_NAME,\n LINUX_UNIT_PATH,\n LINUX_ENV_FILE_PATH,\n LINUX_SYSTEM_UNIT_PATH,\n LINUX_SYSTEM_ENV_FILE_PATH,\n LINUX_SECRET_ENV_KEYS,\n renderMacosPlist,\n renderLinuxUnit,\n renderLinuxSystemUnit,\n renderLinuxRecoverService,\n renderLinuxRecoverTimer,\n LINUX_RECOVER_SERVICE_NAME,\n LINUX_RECOVER_TIMER_NAME,\n LINUX_RECOVER_SERVICE_PATH,\n LINUX_RECOVER_TIMER_PATH,\n renderLinuxEnvFile,\n escapeXml,\n escapeForSystemdEnv,\n shellQuoteForSystemd,\n readPlist: () => (existsSync(MACOS_PLIST) ? readFileSync(MACOS_PLIST, 'utf-8') : null),\n readUnit: () => (existsSync(LINUX_UNIT_PATH) ? readFileSync(LINUX_UNIT_PATH, 'utf-8') : null),\n readSystemUnit: () => (existsSync(LINUX_SYSTEM_UNIT_PATH) ? readFileSync(LINUX_SYSTEM_UNIT_PATH, 'utf-8') : null),\n readEnvFile: () => (existsSync(LINUX_ENV_FILE_PATH) ? readFileSync(LINUX_ENV_FILE_PATH, 'utf-8') : null),\n readSystemEnvFile: () => (existsSync(LINUX_SYSTEM_ENV_FILE_PATH) ? readFileSync(LINUX_SYSTEM_ENV_FILE_PATH, 'utf-8') : null),\n};\n"],"mappings":"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","names":[]}
@@ -54082,6 +54082,7 @@ var MAINTENANCE_GATED_TOOLS = /* @__PURE__ */ new Set([
54082
54082
  // ADR-0025 approval tools, registered by `approval-tools.ts` against the same
54083
54083
  // server object, so the wrapper in index.ts covers them too.
54084
54084
  "cancel_approval",
54085
+ "approval_annotate",
54085
54086
  // Status / identity.
54086
54087
  "status_standup",
54087
54088
  "status_update",
package/dist/mcp/index.js CHANGED
@@ -47352,6 +47352,7 @@ var MAINTENANCE_GATED_TOOLS = /* @__PURE__ */ new Set([
47352
47352
  // ADR-0025 approval tools, registered by `approval-tools.ts` against the same
47353
47353
  // server object, so the wrapper in index.ts covers them too.
47354
47354
  "cancel_approval",
47355
+ "approval_annotate",
47355
47356
  // Status / identity.
47356
47357
  "status_standup",
47357
47358
  "status_update",
@@ -48694,6 +48695,22 @@ function describeKanbanLink(requestedCardId, resp) {
48694
48695
  }
48695
48696
  return ` Card ${requestedCardId} was NOT linked to this approval (${why}), so the card is not waiting on it and answering the card will not decide it. Leave the request as is (the approver can still decide it from the Approvals page) and do not park the card as a human Approve/Deny wait.`;
48696
48697
  }
48698
+ var APPROVAL_ANNOTATE_EVIDENCE_MAX = 1e3;
48699
+ function describeAnnotation(resp) {
48700
+ const head = `Recorded your recommendation (${resp.recommendation}) on request ${resp.request_id}. This is not a decision; the person still decides.`;
48701
+ switch (resp.card.outcome) {
48702
+ case "card_updated":
48703
+ return `${head} It now shows on their approval card.`;
48704
+ case "follow_up_posted":
48705
+ return `${head} It was posted as a follow-up beside their card. It also shows on the Approvals page.`;
48706
+ case "unchanged":
48707
+ return `${head} It is identical to your last one, which already reached their card, so nothing was re-sent.`;
48708
+ case "no_card":
48709
+ return `${head} No chat card was delivered for this request, so it shows on the Approvals page only.`;
48710
+ default:
48711
+ return `${head} It could NOT be added to their chat card (${resp.card.reason ?? "unknown"}), so it shows on the Approvals page only. Do not say they have seen it.`;
48712
+ }
48713
+ }
48697
48714
  function statusToOutcome(status) {
48698
48715
  switch (status) {
48699
48716
  case "active":
@@ -48803,6 +48820,31 @@ function registerApprovalTools(server2, deps) {
48803
48820
  }
48804
48821
  }
48805
48822
  );
48823
+ server2.tool(
48824
+ "approval_annotate",
48825
+ "Attach your recommendation and what you checked to an approval request from an agent that reports to you, directly or further down your reports-to chain. The person deciding sees it on their approval card, labelled as an agent recommendation. It is NOT a decision: it cannot approve, deny or change the request, and the human still decides. Use it after you have verified the facts yourself, never by repeating the requester's own words. Calling it again replaces your earlier recommendation. Only works while the request is pending.",
48826
+ {
48827
+ request_id: external_exports.string().min(1).describe("The request id from the notice telling you about your report's approval."),
48828
+ recommendation: external_exports.enum(["approve", "deny", "unsure"]).describe("What you recommend the person decides."),
48829
+ evidence: external_exports.string().min(1).max(APPROVAL_ANNOTATE_EVIDENCE_MAX).describe('What you checked and found, e.g. "tree clean, no stash, all commits on origin". Shown as one line of quoted text, attributed to you.')
48830
+ },
48831
+ async (params) => {
48832
+ try {
48833
+ const resp = await apiPost2(`/approvals/${encodeURIComponent(params.request_id)}/annotate`, {
48834
+ agent_id: agentId,
48835
+ recommendation: params.recommendation,
48836
+ evidence: params.evidence
48837
+ });
48838
+ return { content: [{ type: "text", text: describeAnnotation(resp) }] };
48839
+ } catch (err) {
48840
+ const msg = err instanceof Error ? err.message : String(err);
48841
+ return {
48842
+ content: [{ type: "text", text: `Could not attach the recommendation to ${params.request_id}: ${msg}` }],
48843
+ isError: true
48844
+ };
48845
+ }
48846
+ }
48847
+ );
48806
48848
  server2.tool(
48807
48849
  "cancel_approval",
48808
48850
  "Withdraw an approval request you no longer need (only works while it is still pending). The Manager stops being asked. Use when the underlying need went away.",
@@ -52424,6 +52466,8 @@ var LOCAL_TOOL_NAMES = /* @__PURE__ */ new Set([
52424
52466
  "request_approval",
52425
52467
  "check_approval",
52426
52468
  "cancel_approval",
52469
+ // ENG-11573: a managing agent's recommendation on a report's approval.
52470
+ "approval_annotate",
52427
52471
  "kanban_list",
52428
52472
  "kanban_search",
52429
52473
  "kanban_get",
@@ -31065,6 +31065,7 @@ var MAINTENANCE_GATED_TOOLS = /* @__PURE__ */ new Set([
31065
31065
  // ADR-0025 approval tools, registered by `approval-tools.ts` against the same
31066
31066
  // server object, so the wrapper in index.ts covers them too.
31067
31067
  "cancel_approval",
31068
+ "approval_annotate",
31068
31069
  // Status / identity.
31069
31070
  "status_standup",
31070
31071
  "status_update",
@@ -31282,6 +31282,7 @@ var MAINTENANCE_GATED_TOOLS = /* @__PURE__ */ new Set([
31282
31282
  // ADR-0025 approval tools, registered by `approval-tools.ts` against the same
31283
31283
  // server object, so the wrapper in index.ts covers them too.
31284
31284
  "cancel_approval",
31285
+ "approval_annotate",
31285
31286
  // Status / identity.
31286
31287
  "status_standup",
31287
31288
  "status_update",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.28.1172",
3
+ "version": "0.28.1174",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/manager-supervisor.ts"],"sourcesContent":["/**\n * OS-level supervision for the agt manager daemon (ENG-4593).\n *\n * Hands manager lifecycle off to the OS so a missing process is the OS's\n * problem, not ours. Replaces the in-process `agt manager start\n * --supervise` wrapper from ENG-4488.\n *\n * macOS: launchd LaunchAgent (KeepAlive=true + ThrottleInterval=10)\n * Linux: systemd --user unit (Restart=always + RestartSec=5) [Phase 2]\n * systemd system unit (Restart=on-failure, root) [Phase 3 / ENG-4706]\n *\n * Public surface:\n * - installSupervisor(opts) — write the unit, load/enable it, verify\n * - uninstallSupervisor() — unload/disable + remove the unit (idempotent)\n * - supervisorStatus() — quick readout of whether the OS knows about us\n *\n * - installSystemUnit(opts) — Linux only, root-only. Writes\n * /etc/systemd/system/agt-manager.service\n * so the manager survives EC2 reboot\n * without `loginctl enable-linger`\n * gymnastics. Used by host-bootstrap and\n * the SSM backfill runbook (ENG-4706).\n * - uninstallSystemUnit()\n * - systemUnitStatus()\n *\n * Each platform implementation lives in its own helper. The dispatcher\n * picks one based on `process.platform` and rejects unsupported OSes\n * with a clear error rather than silently no-oping.\n */\n\nimport { execFileSync } from 'node:child_process';\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n unlinkSync,\n writeFileSync,\n chmodSync,\n} from 'node:fs';\nimport { homedir, platform } from 'node:os';\nimport { dirname, join } from 'node:path';\n\n// macOS LaunchAgent label and on-disk path. Stable so install/uninstall\n// can find each other without a state file.\nconst MACOS_LABEL = 'team.augmented.agt-manager';\nconst MACOS_PLIST = join(homedir(), 'Library', 'LaunchAgents', `${MACOS_LABEL}.plist`);\n\n// Linux systemd --user unit. Lives under the operator's XDG config so it\n// survives reboot only after `loginctl enable-linger` (we surface that as\n// a follow-up step rather than running it ourselves — needs root).\nconst LINUX_UNIT_NAME = 'agt-manager.service';\nconst LINUX_UNIT_PATH = join(homedir(), '.config', 'systemd', 'user', LINUX_UNIT_NAME);\n// Secrets live in a separate EnvironmentFile (mode 0600) instead of\n// being inlined into the unit body. The unit file itself is still\n// 0600 but plain Environment= lines in it are visible to anything\n// that can read systemd's parsed unit cache (`systemctl show`,\n// `systemd-cgls --all`, etc.). EnvironmentFile content stays private\n// to the systemd manager process.\nconst LINUX_ENV_FILE_PATH = join(homedir(), '.config', 'systemd', 'user', 'agt-manager.env');\n\nexport interface InstallOpts {\n /** Absolute path to the agt binary the supervisor should launch. */\n agtBin: string;\n /** Poll interval passed to `agt manager start`. Min 5 seconds. */\n intervalSec: number;\n /** Manager config directory (where pid/log/state files live). */\n configDir: string;\n /**\n * Environment variables the supervised manager needs at launch time.\n * AGT_HOST and AGT_API_KEY are required for the manager to call the\n * API; AGT_TEAM is optional but typical.\n */\n env: Record<string, string>;\n}\n\nexport type SupervisorPresence =\n | { kind: 'installed'; pid: number | null; details: string }\n | { kind: 'not-installed' }\n | { kind: 'unsupported-platform'; platform: string };\n\n// ---------------------------------------------------------------------------\n// Public dispatcher\n// ---------------------------------------------------------------------------\n\nexport async function installSupervisor(opts: InstallOpts): Promise<{ ok: true; details: string } | { ok: false; error: string }> {\n switch (platform()) {\n case 'darwin':\n return installMacos(opts);\n case 'linux':\n return installLinux(opts);\n default:\n return { ok: false, error: `Unsupported platform: ${platform()}. Supervisor install is macOS / Linux only.` };\n }\n}\n\nexport async function uninstallSupervisor(): Promise<{ ok: true; details: string } | { ok: false; error: string }> {\n switch (platform()) {\n case 'darwin':\n return uninstallMacos();\n case 'linux':\n return uninstallLinux();\n default:\n return { ok: false, error: `Unsupported platform: ${platform()}. Supervisor uninstall is macOS / Linux only.` };\n }\n}\n\nexport function supervisorStatus(): SupervisorPresence {\n switch (platform()) {\n case 'darwin':\n return statusMacos();\n case 'linux':\n return statusLinux();\n default:\n return { kind: 'unsupported-platform', platform: platform() };\n }\n}\n\n// ---------------------------------------------------------------------------\n// macOS — launchd LaunchAgent\n// ---------------------------------------------------------------------------\n\nfunction installMacos(opts: InstallOpts): { ok: true; details: string } | { ok: false; error: string } {\n if (!existsSync(opts.agtBin)) {\n return { ok: false, error: `agt binary not found at ${opts.agtBin}` };\n }\n if (opts.intervalSec < 5) {\n return { ok: false, error: 'intervalSec must be >= 5' };\n }\n if (!opts.env.AGT_HOST || !opts.env.AGT_API_KEY) {\n return { ok: false, error: 'AGT_HOST and AGT_API_KEY must be set in the calling shell so the supervisor can pass them to the manager. Run `agt setup <token>` first.' };\n }\n\n // If a previous version is already loaded, unload it before rewriting\n // the plist — otherwise launchctl bootstrap will fail with EALREADY.\n bestEffort(() => execFileSync('launchctl', ['unload', '-w', MACOS_PLIST], { stdio: 'ignore' }));\n\n // Ensure the LaunchAgents directory exists. macOS creates it on first\n // login, but freshly-installed admin users may not have it yet.\n mkdirSync(dirname(MACOS_PLIST), { recursive: true });\n // 0o600 — plist embeds AGT_API_KEY in EnvironmentVariables. World-\n // readable would expose the host's tlk_ token to every local user.\n writeFileSync(MACOS_PLIST, renderMacosPlist(opts), { mode: 0o600 });\n chmodSync(MACOS_PLIST, 0o600);\n\n try {\n execFileSync('launchctl', ['load', '-w', MACOS_PLIST], { stdio: 'pipe' });\n } catch (err) {\n return { ok: false, error: `launchctl load failed: ${(err as Error).message}` };\n }\n\n // Verify launchd actually picked it up. `list <label>` returns 0 with\n // a plist of the loaded job's metadata; non-zero if not loaded.\n try {\n const out = execFileSync('launchctl', ['list', MACOS_LABEL], { encoding: 'utf-8' });\n return {\n ok: true,\n details: `Loaded launchd LaunchAgent ${MACOS_LABEL}. Logs: ${join(opts.configDir, 'manager.log')}.\\n${out.trim().split('\\n').slice(0, 10).join('\\n')}`,\n };\n } catch (err) {\n return { ok: false, error: `Plist written but launchctl list reported nothing for ${MACOS_LABEL}: ${(err as Error).message}` };\n }\n}\n\nfunction uninstallMacos(): { ok: true; details: string } | { ok: false; error: string } {\n // Both steps are idempotent — unload reports an error if nothing's\n // loaded, removing the file errors if it doesn't exist. Swallow both;\n // the desired end state is \"no LaunchAgent for this label\".\n bestEffort(() => execFileSync('launchctl', ['unload', '-w', MACOS_PLIST], { stdio: 'ignore' }));\n let removed = false;\n if (existsSync(MACOS_PLIST)) {\n try {\n unlinkSync(MACOS_PLIST);\n removed = true;\n } catch (err) {\n return { ok: false, error: `Failed to remove ${MACOS_PLIST}: ${(err as Error).message}` };\n }\n }\n return {\n ok: true,\n details: removed\n ? `Removed ${MACOS_LABEL} from launchd and deleted ${MACOS_PLIST}.`\n : `${MACOS_LABEL} was already uninstalled (no plist at ${MACOS_PLIST}).`,\n };\n}\n\nfunction statusMacos(): SupervisorPresence {\n if (!existsSync(MACOS_PLIST)) return { kind: 'not-installed' };\n // launchctl list emits a 3-column line: PID Status Label. PID is \"-\"\n // when the job is loaded but not currently running.\n try {\n const out = execFileSync('launchctl', ['list', MACOS_LABEL], { encoding: 'utf-8' });\n const pidMatch = /\"PID\"\\s*=\\s*(\\d+);/.exec(out);\n const pid = pidMatch ? Number(pidMatch[1]) : null;\n return { kind: 'installed', pid, details: `Plist: ${MACOS_PLIST}` };\n } catch {\n // File exists but launchctl can't find the label → user removed it\n // out-of-band. Surface as \"not installed\" so re-install just works.\n return { kind: 'not-installed' };\n }\n}\n\nfunction renderMacosPlist(opts: InstallOpts): string {\n // Load the operator's env into the plist so the manager has the same\n // AGT_HOST / AGT_API_KEY / AGT_TEAM the shell session does. launchd\n // does NOT inherit env from the user's shell — anything we don't bake\n // in here won't be visible to the process.\n const envEntries = Object.entries(opts.env)\n .filter(([k, v]) => k.length > 0 && v != null)\n .map(([k, v]) => ` <key>${escapeXml(k)}</key>\\n <string>${escapeXml(v)}</string>`)\n .join('\\n');\n\n const args = [\n 'manager',\n 'start',\n '--interval',\n String(opts.intervalSec),\n '--config-dir',\n opts.configDir,\n ];\n const argsXml = args.map((a) => ` <string>${escapeXml(a)}</string>`).join('\\n');\n\n const logPath = join(opts.configDir, 'manager.log');\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${MACOS_LABEL}</string>\n\n <key>ProgramArguments</key>\n <array>\n <string>${escapeXml(opts.agtBin)}</string>\n${argsXml}\n </array>\n\n <key>RunAtLoad</key>\n <true/>\n\n <key>KeepAlive</key>\n <true/>\n\n <!-- Throttle restarts so a crashloop doesn't peg the CPU. 10 seconds\n between launches is enough room for transient API blips while\n still recovering quickly from a real crash. -->\n <key>ThrottleInterval</key>\n <integer>10</integer>\n\n <!-- Where the manager's stdout / stderr land. The manager's logger\n redirects stderr into manager.log itself, but launchd needs an\n explicit destination at the OS level for any pre-logger output\n (e.g., Node startup errors before the watchdog wires up). -->\n <key>StandardOutPath</key>\n <string>${escapeXml(logPath)}</string>\n <key>StandardErrorPath</key>\n <string>${escapeXml(logPath)}</string>\n\n <key>WorkingDirectory</key>\n <string>${escapeXml(opts.configDir)}</string>\n\n <key>EnvironmentVariables</key>\n <dict>\n${envEntries}\n </dict>\n</dict>\n</plist>\n`;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction bestEffort(fn: () => void): void {\n try { fn(); } catch { /* swallow */ }\n}\n\nfunction escapeXml(s: string): string {\n return s\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&apos;');\n}\n\n// ---------------------------------------------------------------------------\n// Linux — systemd --user unit\n// ---------------------------------------------------------------------------\n\nfunction installLinux(opts: InstallOpts): { ok: true; details: string } | { ok: false; error: string } {\n if (!existsSync(opts.agtBin)) {\n return { ok: false, error: `agt binary not found at ${opts.agtBin}` };\n }\n if (opts.intervalSec < 5) {\n return { ok: false, error: 'intervalSec must be >= 5' };\n }\n if (!opts.env.AGT_HOST || !opts.env.AGT_API_KEY) {\n return { ok: false, error: 'AGT_HOST and AGT_API_KEY must be set in the calling shell so the supervisor can pass them to the unit. Run `agt setup <token>` first.' };\n }\n\n // Stop any previous instance before rewriting the unit — saves a\n // restart loop while daemon-reload picks up the new file.\n bestEffort(() => execFileSync('systemctl', ['--user', 'stop', LINUX_UNIT_NAME], { stdio: 'ignore' }));\n\n // ~/.config/systemd/user/ is the canonical user-unit path. Create it\n // recursively in case this is a fresh user with no XDG layout yet.\n mkdirSync(dirname(LINUX_UNIT_PATH), { recursive: true, mode: 0o700 });\n // Write the secrets-only env file FIRST (mode 0600) so the unit can\n // reference it via EnvironmentFile=. Non-secret env stays in the\n // unit body via Environment= lines for visibility.\n writeFileSync(LINUX_ENV_FILE_PATH, renderLinuxEnvFile(opts), { mode: 0o600 });\n chmodSync(LINUX_ENV_FILE_PATH, 0o600);\n writeFileSync(LINUX_UNIT_PATH, renderLinuxUnit(opts), { mode: 0o600 });\n chmodSync(LINUX_UNIT_PATH, 0o600);\n\n try {\n execFileSync('systemctl', ['--user', 'daemon-reload'], { stdio: 'pipe' });\n } catch (err) {\n return { ok: false, error: `systemctl --user daemon-reload failed: ${(err as Error).message}` };\n }\n\n // `enable --now` enables the unit at boot AND starts it immediately.\n // Surviving reboot on a headless host additionally requires\n // `loginctl enable-linger <user>` (root-only); we surface that as a\n // post-install hint rather than running it ourselves.\n try {\n execFileSync('systemctl', ['--user', 'enable', '--now', LINUX_UNIT_NAME], { stdio: 'pipe' });\n } catch (err) {\n return { ok: false, error: `systemctl --user enable --now failed: ${(err as Error).message}. (Hint: 'systemctl --user' requires a per-user dbus session; on a fresh SSH login try 'export XDG_RUNTIME_DIR=/run/user/$(id -u)' first.)` };\n }\n\n let isActive = '';\n try {\n isActive = execFileSync('systemctl', ['--user', 'is-active', LINUX_UNIT_NAME], { encoding: 'utf-8' }).trim();\n } catch (err) {\n // is-active exits non-zero for inactive/failed states. Capture its\n // output so the operator sees the real status.\n isActive = ((err as { stdout?: Buffer; stderr?: Buffer }).stdout?.toString() ?? '').trim() || 'unknown';\n }\n\n // Don't claim the install succeeded if the unit ended up failed /\n // inactive / unknown. Without this, the CLI would exit 0 with a\n // green \"supervisor installed\" message while the manager was\n // already broken.\n if (isActive !== 'active') {\n return {\n ok: false,\n error: `systemd loaded ${LINUX_UNIT_NAME} but it did not become active (status: ${isActive}). Run 'systemctl --user status ${LINUX_UNIT_NAME}' or 'journalctl --user -u ${LINUX_UNIT_NAME} -n 50' for details.`,\n };\n }\n\n return {\n ok: true,\n details: `Loaded systemd --user unit ${LINUX_UNIT_NAME} (status: ${isActive}). Logs: ${join(opts.configDir, 'manager.log')}.\\nFor reboot survival on a headless host run as root: 'loginctl enable-linger ${process.env['USER'] ?? '<your-user>'}'`,\n };\n}\n\nfunction uninstallLinux(): { ok: true; details: string } | { ok: false; error: string } {\n // disable --now stops + un-enables in one shot. Both legs are idempotent\n // — disable on a non-existent unit returns 0 with a notice on stderr.\n bestEffort(() => execFileSync('systemctl', ['--user', 'disable', '--now', LINUX_UNIT_NAME], { stdio: 'ignore' }));\n\n let removed = false;\n if (existsSync(LINUX_UNIT_PATH)) {\n try {\n unlinkSync(LINUX_UNIT_PATH);\n removed = true;\n } catch (err) {\n return { ok: false, error: `Failed to remove ${LINUX_UNIT_PATH}: ${(err as Error).message}` };\n }\n }\n // Best-effort cleanup of the secrets-only env file too; we don't\n // want a stale AGT_API_KEY left on disk after uninstall.\n if (existsSync(LINUX_ENV_FILE_PATH)) {\n bestEffort(() => unlinkSync(LINUX_ENV_FILE_PATH));\n }\n\n // Reload so a future install with the same path doesn't reuse the\n // stale unit metadata systemd cached in memory.\n bestEffort(() => execFileSync('systemctl', ['--user', 'daemon-reload'], { stdio: 'ignore' }));\n\n return {\n ok: true,\n details: removed\n ? `Disabled systemd --user unit ${LINUX_UNIT_NAME} and deleted ${LINUX_UNIT_PATH}.`\n : `${LINUX_UNIT_NAME} was already uninstalled (no unit file at ${LINUX_UNIT_PATH}).`,\n };\n}\n\nfunction statusLinux(): SupervisorPresence {\n if (!existsSync(LINUX_UNIT_PATH)) return { kind: 'not-installed' };\n let pid: number | null = null;\n let activeState = 'unknown';\n try {\n const out = execFileSync(\n 'systemctl',\n ['--user', 'show', LINUX_UNIT_NAME, '--property=MainPID,ActiveState'],\n { encoding: 'utf-8' },\n );\n const pidMatch = /^MainPID=(\\d+)/m.exec(out);\n if (pidMatch && pidMatch[1] !== '0') pid = Number(pidMatch[1]);\n const stateMatch = /^ActiveState=(\\S+)/m.exec(out);\n if (stateMatch) activeState = stateMatch[1] ?? 'unknown';\n } catch (err) {\n // The unit file exists but `systemctl --user` couldn't probe it\n // — most often because XDG_RUNTIME_DIR / the per-user dbus isn't\n // available in this shell. Don't lie to the operator about\n // whether the supervisor is installed; report 'installed,\n // unknown state' instead.\n return {\n kind: 'installed',\n pid: null,\n details: `Unit: ${LINUX_UNIT_PATH} (could not query systemd --user: ${(err as Error).message})`,\n };\n }\n return { kind: 'installed', pid, details: `Unit: ${LINUX_UNIT_PATH} (${activeState})` };\n}\n\n// Secrets that go into EnvironmentFile= instead of inline Environment=.\n// Anything in this set is written to LINUX_ENV_FILE_PATH (mode 0600)\n// and stripped from the unit body. Public env (PATH, AGT_HOST, AGT_TEAM)\n// stays inline for visibility in `systemctl show`.\nconst LINUX_SECRET_ENV_KEYS: ReadonlySet<string> = new Set(['AGT_API_KEY']);\n\nfunction renderLinuxUnit(opts: InstallOpts): string {\n // Non-secret env stays in the unit body via Environment= lines;\n // secrets are pulled from the EnvironmentFile= written separately.\n // systemd parses Environment= with a peculiar grammar — quotes are\n // optional but values can't contain literal newlines. We trust the\n // env values we forward and quote everything to be safe with spaces.\n const envLines = Object.entries(opts.env)\n .filter(([k, v]) => k.length > 0 && v != null && !LINUX_SECRET_ENV_KEYS.has(k))\n .map(([k, v]) => `Environment=\"${k}=${escapeForSystemdEnv(v)}\"`)\n .join('\\n');\n\n // ExecStart cannot use shell metacharacters; pass the args explicitly\n // via systemd's array-of-args style (newline-separated wouldn't\n // parse — systemd splits on whitespace within ExecStart). So we\n // shell-quote each arg and join.\n const execArgs = [\n opts.agtBin,\n 'manager',\n 'start',\n '--interval',\n String(opts.intervalSec),\n '--config-dir',\n opts.configDir,\n ]\n .map(shellQuoteForSystemd)\n .join(' ');\n\n const logPath = join(opts.configDir, 'manager.log');\n\n return `# agt manager supervisor (ENG-4593)\n# Managed by \\`agt manager install\\` — edits will be overwritten.\n\n[Unit]\nDescription=Augmented host manager daemon\nAfter=network-online.target\nWants=network-online.target\n# Restart rate-limit lives here per systemd.unit(5). Older systemds\n# tolerated these directives in the service section but newer ones\n# silently ignore them there, leaving the crashloop unprotected.\nStartLimitBurst=5\nStartLimitIntervalSec=60\n\n[Service]\nType=simple\nWorkingDirectory=${opts.configDir}\n${envLines}\nEnvironmentFile=${LINUX_ENV_FILE_PATH}\nExecStart=${execArgs}\n\n# Always come back from any exit; back off 5s between launches so a\n# crashloop can't peg the CPU. The 5/60s window above caps total\n# attempts before systemd marks the unit failed.\nRestart=always\nRestartSec=5\n\n# stdout/stderr → manager.log. journalctl --user -u agt-manager\n# still captures everything; this just gives operators a single file\n# to tail without learning systemd plumbing.\nStandardOutput=append:${logPath}\nStandardError=append:${logPath}\n\n[Install]\nWantedBy=default.target\n`;\n}\n\n/**\n * Render the secrets-only env file referenced by EnvironmentFile= in\n * the unit. Format: `KEY=VALUE` per line (systemd's EnvironmentFile\n * parser doesn't support quoting the value the way Environment= does,\n * but it tolerates literal characters as long as the value has no\n * newline). One key per line; trailing newline.\n */\nfunction renderLinuxEnvFile(opts: InstallOpts): string {\n return Object.entries(opts.env)\n .filter(([k, v]) => LINUX_SECRET_ENV_KEYS.has(k) && v != null)\n .map(([k, v]) => `${k}=${v}\\n`)\n .join('');\n}\n\nfunction escapeForSystemdEnv(value: string): string {\n // Inside an Environment=\"K=V\" line, \" and \\\\ need escaping. Newlines\n // would terminate the directive — reject them upstream.\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n}\n\nfunction shellQuoteForSystemd(arg: string): string {\n // systemd's ExecStart parser supports double-quoted strings with\n // backslash escapes, similar to a POSIX shell but no command\n // substitution. Wrap any arg containing whitespace or shell metas.\n if (/^[A-Za-z0-9_./:=-]+$/.test(arg)) return arg;\n return `\"${arg.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"')}\"`;\n}\n\n// ---------------------------------------------------------------------------\n// Linux — systemd system unit (ENG-4706)\n// ---------------------------------------------------------------------------\n//\n// The `--user` unit above survives reboot only if `loginctl enable-linger`\n// is set per-user. On headless EC2 hosts that's a footgun: a reboot (kernel\n// patch, instance-type change, AWS-initiated maintenance) silently takes\n// every agent on the host offline until an operator manually re-launches\n// the manager. The system unit lives at /etc/systemd/system/, runs as root,\n// and is enabled into multi-user.target — so it comes up automatically at\n// boot just like any other service.\n//\n// Both unit kinds can co-exist; statusLinux() looks at the user one,\n// systemUnitStatus() looks at the system one. The CLI surfaces them as\n// `agt manager install` (user, dev workflow) and `agt manager\n// install-system-unit` (root, host-infra workflow) so the operator\n// chooses explicitly.\n\nconst LINUX_SYSTEM_UNIT_PATH = '/etc/systemd/system/agt-manager.service';\nconst LINUX_SYSTEM_ENV_FILE_PATH = '/etc/agt/agt-manager.env';\n\nexport interface InstallSystemUnitOpts extends InstallOpts {\n /**\n * Unix user the manager runs as. EC2 default is `root`. Other hosts\n * can run it as a less-privileged user, but the user must own\n * `configDir` and have permission to write to it. Defaults to `root`\n * when omitted (matches the ticket spec).\n */\n user?: string;\n}\n\nexport async function installSystemUnit(\n opts: InstallSystemUnitOpts,\n): Promise<{ ok: true; details: string } | { ok: false; error: string }> {\n if (platform() !== 'linux') {\n return {\n ok: false,\n error: `installSystemUnit is Linux-only (current platform: ${platform()}). For local dev on macOS use installSupervisor.`,\n };\n }\n if (process.getuid?.() !== 0) {\n return {\n ok: false,\n error: 'installSystemUnit requires root. Re-run with sudo, or call from the EC2 user-data bootstrap which already runs as root.',\n };\n }\n if (!existsSync(opts.agtBin)) {\n return { ok: false, error: `agt binary not found at ${opts.agtBin}` };\n }\n if (opts.intervalSec < 5) {\n return { ok: false, error: 'intervalSec must be >= 5' };\n }\n if (!opts.env.AGT_HOST || !opts.env.AGT_API_KEY) {\n return {\n ok: false,\n error: 'AGT_HOST and AGT_API_KEY must be set so the system unit can pass them through. Run `agt setup <token>` first (it writes them to /etc/environment, which this installer reads).',\n };\n }\n\n // Stop any running instance so daemon-reload picks up the rewritten\n // unit cleanly without a respawn race.\n bestEffort(() => execFileSync('systemctl', ['stop', LINUX_UNIT_NAME], { stdio: 'ignore' }));\n\n // Belt-and-braces (ENG-4706 backfill): kill any pre-existing manager\n // process that wasn't started via systemd. On hosts that were\n // bootstrapped before this PR, the manager runs nohup'd from a one-\n // shot SSM command. Installing the system unit without stopping that\n // older process leaves TWO managers polling Supabase simultaneously —\n // they race on agent ticks, double-inject channel messages, and\n // double-write audit_log rows. SIGTERM gives it 3s to wind down\n // cleanly before the systemd start grabs the supervisor seat.\n // Ignored on hosts that have no rogue manager (the typical fresh-\n // bootstrap path).\n bestEffort(() => {\n const out = execFileSync('pgrep', ['-f', 'agt manager start'], { stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf-8' });\n const pids = out\n .split('\\n')\n .map((s) => s.trim())\n .filter((s) => s.length > 0 && /^\\d+$/.test(s) && Number(s) !== process.pid);\n if (pids.length === 0) return;\n for (const pid of pids) {\n bestEffort(() => process.kill(Number(pid), 'SIGTERM'));\n }\n // Brief settle so the just-killed manager releases its Supabase\n // long-polls before the new one connects. 3s matches the default\n // poll interval; a manager that hasn't shut down in that window\n // gets SIGKILLed below.\n const deadline = Date.now() + 3_000;\n while (Date.now() < deadline) {\n try {\n execFileSync('pgrep', ['-f', 'agt manager start'], { stdio: 'ignore' });\n } catch {\n return;\n }\n // Spin — busy-wait is fine for a 3s ceiling and avoids pulling in\n // a sleep helper. This path runs once at install, never on the\n // hot poll loop.\n }\n // Still alive — escalate.\n for (const pid of pids) {\n bestEffort(() => process.kill(Number(pid), 'SIGKILL'));\n }\n });\n\n // Secrets-only env file lives under /etc/agt/ (root:root, 0600). Same\n // rationale as the user-unit env file: keep AGT_API_KEY out of any\n // tool that reads systemd's parsed unit cache.\n mkdirSync(dirname(LINUX_SYSTEM_ENV_FILE_PATH), { recursive: true, mode: 0o700 });\n writeFileSync(LINUX_SYSTEM_ENV_FILE_PATH, renderLinuxEnvFile(opts), { mode: 0o600 });\n chmodSync(LINUX_SYSTEM_ENV_FILE_PATH, 0o600);\n writeFileSync(LINUX_SYSTEM_UNIT_PATH, renderLinuxSystemUnit(opts), { mode: 0o644 });\n chmodSync(LINUX_SYSTEM_UNIT_PATH, 0o644);\n\n try {\n execFileSync('systemctl', ['daemon-reload'], { stdio: 'pipe' });\n } catch (err) {\n return { ok: false, error: `systemctl daemon-reload failed: ${(err as Error).message}` };\n }\n\n // `enable --now` enables at boot AND starts immediately. Unlike the\n // --user variant, this needs no enable-linger — multi-user.target\n // pulls us in regardless of any user session.\n try {\n execFileSync('systemctl', ['enable', '--now', LINUX_UNIT_NAME], { stdio: 'pipe' });\n } catch (err) {\n return { ok: false, error: `systemctl enable --now failed: ${(err as Error).message}` };\n }\n\n let isActive = '';\n try {\n isActive = execFileSync('systemctl', ['is-active', LINUX_UNIT_NAME], { encoding: 'utf-8' }).trim();\n } catch (err) {\n isActive = ((err as { stdout?: Buffer; stderr?: Buffer }).stdout?.toString() ?? '').trim() || 'unknown';\n }\n\n if (isActive !== 'active') {\n return {\n ok: false,\n error: `systemd loaded ${LINUX_UNIT_NAME} but it did not become active (status: ${isActive}). Run 'systemctl status ${LINUX_UNIT_NAME}' or 'journalctl -u ${LINUX_UNIT_NAME} -n 50' for details.`,\n };\n }\n\n return {\n ok: true,\n details: `Loaded systemd system unit ${LINUX_UNIT_NAME} at ${LINUX_SYSTEM_UNIT_PATH} (status: ${isActive}). Will auto-start on reboot via multi-user.target. Logs: ${join(opts.configDir, 'manager.log')} and 'journalctl -u ${LINUX_UNIT_NAME}'.`,\n };\n}\n\nexport async function uninstallSystemUnit(): Promise<{ ok: true; details: string } | { ok: false; error: string }> {\n if (platform() !== 'linux') {\n return { ok: false, error: `uninstallSystemUnit is Linux-only (current platform: ${platform()}).` };\n }\n if (process.getuid?.() !== 0) {\n return { ok: false, error: 'uninstallSystemUnit requires root.' };\n }\n\n bestEffort(() => execFileSync('systemctl', ['disable', '--now', LINUX_UNIT_NAME], { stdio: 'ignore' }));\n\n let removed = false;\n if (existsSync(LINUX_SYSTEM_UNIT_PATH)) {\n try {\n unlinkSync(LINUX_SYSTEM_UNIT_PATH);\n removed = true;\n } catch (err) {\n return { ok: false, error: `Failed to remove ${LINUX_SYSTEM_UNIT_PATH}: ${(err as Error).message}` };\n }\n }\n if (existsSync(LINUX_SYSTEM_ENV_FILE_PATH)) {\n bestEffort(() => unlinkSync(LINUX_SYSTEM_ENV_FILE_PATH));\n }\n\n bestEffort(() => execFileSync('systemctl', ['daemon-reload'], { stdio: 'ignore' }));\n\n return {\n ok: true,\n details: removed\n ? `Disabled systemd system unit ${LINUX_UNIT_NAME} and deleted ${LINUX_SYSTEM_UNIT_PATH}.`\n : `${LINUX_UNIT_NAME} system unit was already uninstalled (no unit file at ${LINUX_SYSTEM_UNIT_PATH}).`,\n };\n}\n\nexport function systemUnitStatus(): SupervisorPresence {\n if (platform() !== 'linux') {\n return { kind: 'unsupported-platform', platform: platform() };\n }\n if (!existsSync(LINUX_SYSTEM_UNIT_PATH)) return { kind: 'not-installed' };\n let pid: number | null = null;\n let activeState = 'unknown';\n try {\n const out = execFileSync(\n 'systemctl',\n ['show', LINUX_UNIT_NAME, '--property=MainPID,ActiveState'],\n { encoding: 'utf-8' },\n );\n const pidMatch = /^MainPID=(\\d+)/m.exec(out);\n if (pidMatch && pidMatch[1] !== '0') pid = Number(pidMatch[1]);\n const stateMatch = /^ActiveState=(\\S+)/m.exec(out);\n if (stateMatch) activeState = stateMatch[1] ?? 'unknown';\n } catch (err) {\n return {\n kind: 'installed',\n pid: null,\n details: `Unit: ${LINUX_SYSTEM_UNIT_PATH} (could not query systemd: ${(err as Error).message})`,\n };\n }\n return { kind: 'installed', pid, details: `Unit: ${LINUX_SYSTEM_UNIT_PATH} (${activeState})` };\n}\n\n/**\n * ENG-6698: ExecStartPre self-heal for a missing/dangling `agt` binary.\n *\n * The incident (agt-aws-1, full-fleet outage): a CLI self-update killed\n * mid-`npm i -g` (npm removes the old package before writing the new) left\n * `/usr/bin/agt` a dangling symlink, so `ExecStart` failed with 203/EXEC and\n * systemd crash-looped the unit to `failed`. With no recovery path the whole\n * fleet went dark until an operator manually reinstalled over SSM.\n *\n * This guard runs before every `ExecStart`: if `agt --version` won't run, it\n * reinstalls the pinned version (ENG-7833, from the unit's AGT_CLI_PIN_VERSION\n * env) or, when unpinned, the SAME release channel (AGT_CLI_RELEASE_CHANNEL,\n * default `latest`) before the manager starts, so a dangling binary self-heals\n * on the next start attempt instead of latching to `failed` with no way back.\n * Preferring the pin here matters: reinstalling the channel tip on a pinned\n * host would silently un-pin it.\n *\n * Two escaping notes that are load-bearing:\n * - systemd does its OWN `${VAR}` expansion on Exec lines (and does not\n * support the POSIX `:-default` form), so every shell `$` is written as\n * `$$` to pass through verbatim to /bin/sh, which then does the\n * `${VAR:-latest}` expansion itself.\n * - the script is one single-quoted `/bin/sh -c` argument with no single\n * quotes inside; `agtBin` is a filesystem path (no single quotes on our\n * hosts) and is double-quoted so a stray space can't split the arg.\n */\nexport function buildSystemUnitRepairExecStartPre(agtBin: string, user: string): string {\n const sudo = user === 'root' ? '' : 'sudo -n ';\n // Single-quoted JS strings so the literal `$${...}` reaches the unit file\n // untouched (a template literal would try to interpolate `${...}`). The bare\n // expansion is embedded inside the echo's own double-quotes; the npm arg gets\n // its own double-quotes — NOT nested, or the inner quotes would break the\n // outer string and word-split the package spec.\n // ENG-7833: prefer the pin (AGT_CLI_PIN_VERSION) over the channel so a\n // dangling-binary repair reinstalls the PINNED version, not the channel tip;\n // otherwise the repair would silently un-pin the host on the next start.\n // Nested POSIX `:-`: pin if set, else channel, else `latest`.\n const versionExpansion = '$${AGT_CLI_PIN_VERSION:-$${AGT_CLI_RELEASE_CHANNEL:-latest}}';\n const installSpec = '\"@integrity-labs/agt-cli@' + versionExpansion + '\"';\n const script =\n '\"' + agtBin + '\" --version >/dev/null 2>&1 || ' +\n '{ echo \"[agt-manager] agt binary missing/dangling - reinstalling @integrity-labs/agt-cli@' +\n versionExpansion +\n '\"; ' +\n sudo +\n 'npm install -g ' +\n installSpec +\n ' --registry=https://registry.npmjs.org; }';\n return \"ExecStartPre=/bin/sh -c '\" + script + \"'\";\n}\n\nfunction renderLinuxSystemUnit(opts: InstallSystemUnitOpts): string {\n const user = opts.user ?? 'root';\n const home = user === 'root' ? '/root' : `/home/${user}`;\n\n // Non-secret env stays in the unit body via Environment= lines;\n // secrets are pulled from EnvironmentFile= written separately.\n const envLines = Object.entries(opts.env)\n .filter(([k, v]) => k.length > 0 && v != null && !LINUX_SECRET_ENV_KEYS.has(k))\n .map(([k, v]) => `Environment=\"${k}=${escapeForSystemdEnv(v)}\"`)\n .join('\\n');\n\n const execArgs = [\n opts.agtBin,\n 'manager',\n 'start',\n '--interval',\n String(opts.intervalSec),\n '--config-dir',\n opts.configDir,\n ]\n .map(shellQuoteForSystemd)\n .join(' ');\n\n const logPath = join(opts.configDir, 'manager.log');\n\n return `# agt manager supervisor — system unit (ENG-4706)\n# Managed by \\`agt manager install-system-unit\\` — edits will be overwritten.\n#\n# Unlike the sibling --user unit at ~/.config/systemd/user/agt-manager.service,\n# this one is enabled into multi-user.target so it auto-starts on every\n# boot regardless of any user login. Use this on EC2 / headless hosts.\n\n[Unit]\nDescription=Augmented host manager daemon\nAfter=network-online.target\nWants=network-online.target\n# ENG-6698: widened from 5/60s. The ExecStartPre repair below reinstalls a\n# dangling agt binary on each start attempt (~7s of npm + RestartSec), so a\n# transient miss needs several attempts within the window to self-heal before\n# systemd would otherwise latch the unit to \\`failed\\`. 10 tries / 5 min gives\n# the repair room to recover without spinning forever on a genuinely broken host.\nStartLimitBurst=10\nStartLimitIntervalSec=300\n\n[Service]\nType=simple\nUser=${user}\nWorkingDirectory=${opts.configDir}\nEnvironment=\"HOME=${home}\"\n${envLines}\nEnvironmentFile=${LINUX_SYSTEM_ENV_FILE_PATH}\n# ENG-6698: self-heal a missing/dangling agt binary before starting, so an\n# interrupted self-update can't latch the unit to \\`failed\\` with no recovery.\n${buildSystemUnitRepairExecStartPre(opts.agtBin, user)}\nExecStart=${execArgs}\n\n# on-failure (not always) so a clean \\`systemctl stop\\` doesn't fight a\n# respawn loop. The ExecStartPre repair + the widened StartLimit above turn a\n# dangling-binary crashloop into a self-healing one. Logs land in ${logPath}\n# via the manager's own writer plus journalctl -u ${LINUX_UNIT_NAME}.\nRestart=on-failure\nRestartSec=5\n\nStandardOutput=append:${logPath}\nStandardError=append:${logPath}\n\n[Install]\nWantedBy=multi-user.target\n`;\n}\n\n// Exposed for unit tests / introspection.\nexport const _internals = {\n MACOS_LABEL,\n MACOS_PLIST,\n LINUX_UNIT_NAME,\n LINUX_UNIT_PATH,\n LINUX_ENV_FILE_PATH,\n LINUX_SYSTEM_UNIT_PATH,\n LINUX_SYSTEM_ENV_FILE_PATH,\n LINUX_SECRET_ENV_KEYS,\n renderMacosPlist,\n renderLinuxUnit,\n renderLinuxSystemUnit,\n renderLinuxEnvFile,\n escapeXml,\n escapeForSystemdEnv,\n shellQuoteForSystemd,\n readPlist: () => (existsSync(MACOS_PLIST) ? readFileSync(MACOS_PLIST, 'utf-8') : null),\n readUnit: () => (existsSync(LINUX_UNIT_PATH) ? readFileSync(LINUX_UNIT_PATH, 'utf-8') : null),\n readSystemUnit: () => (existsSync(LINUX_SYSTEM_UNIT_PATH) ? readFileSync(LINUX_SYSTEM_UNIT_PATH, 'utf-8') : null),\n readEnvFile: () => (existsSync(LINUX_ENV_FILE_PATH) ? readFileSync(LINUX_ENV_FILE_PATH, 'utf-8') : null),\n readSystemEnvFile: () => (existsSync(LINUX_SYSTEM_ENV_FILE_PATH) ? readFileSync(LINUX_SYSTEM_ENV_FILE_PATH, 'utf-8') : null),\n};\n"],"mappings":"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","names":[]}