@songsid/agend 2.1.7 → 2.1.8-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/dist/agent-cli.js +14 -0
  2. package/dist/agent-cli.js.map +1 -1
  3. package/dist/agent-endpoint.d.ts +4 -1
  4. package/dist/agent-endpoint.js +10 -1
  5. package/dist/agent-endpoint.js.map +1 -1
  6. package/dist/backend/claude-code.d.ts +35 -5
  7. package/dist/backend/claude-code.js +288 -17
  8. package/dist/backend/claude-code.js.map +1 -1
  9. package/dist/backend/cli-env-probe-worker.d.ts +1 -0
  10. package/dist/backend/cli-env-probe-worker.js +25 -0
  11. package/dist/backend/cli-env-probe-worker.js.map +1 -0
  12. package/dist/backend/codex-session-lookup.d.ts +16 -0
  13. package/dist/backend/codex-session-lookup.js +76 -2
  14. package/dist/backend/codex-session-lookup.js.map +1 -1
  15. package/dist/backend/codex.d.ts +7 -4
  16. package/dist/backend/codex.js +120 -30
  17. package/dist/backend/codex.js.map +1 -1
  18. package/dist/backend/credential-profile.d.ts +9 -0
  19. package/dist/backend/credential-profile.js +20 -0
  20. package/dist/backend/credential-profile.js.map +1 -1
  21. package/dist/backend/grok.js +19 -0
  22. package/dist/backend/grok.js.map +1 -1
  23. package/dist/backend/muse.d.ts +6 -0
  24. package/dist/backend/muse.js +37 -3
  25. package/dist/backend/muse.js.map +1 -1
  26. package/dist/backend/types.d.ts +21 -2
  27. package/dist/backend/types.js +14 -3
  28. package/dist/backend/types.js.map +1 -1
  29. package/dist/channel/adapters/discord.d.ts +16 -4
  30. package/dist/channel/adapters/discord.js +20 -5
  31. package/dist/channel/adapters/discord.js.map +1 -1
  32. package/dist/channel/mcp-tools.js +6 -0
  33. package/dist/channel/mcp-tools.js.map +1 -1
  34. package/dist/cli.js +138 -48
  35. package/dist/cli.js.map +1 -1
  36. package/dist/config-validator.js +11 -0
  37. package/dist/config-validator.js.map +1 -1
  38. package/dist/config.d.ts +1 -0
  39. package/dist/config.js +1 -1
  40. package/dist/config.js.map +1 -1
  41. package/dist/cross-instance-envelope.d.ts +7 -0
  42. package/dist/cross-instance-envelope.js +19 -1
  43. package/dist/cross-instance-envelope.js.map +1 -1
  44. package/dist/daemon.d.ts +151 -6
  45. package/dist/daemon.js +550 -72
  46. package/dist/daemon.js.map +1 -1
  47. package/dist/delivery-outbox.d.ts +9 -1
  48. package/dist/delivery-outbox.js +25 -4
  49. package/dist/delivery-outbox.js.map +1 -1
  50. package/dist/fleet-context.d.ts +5 -0
  51. package/dist/fleet-context.js.map +1 -1
  52. package/dist/fleet-manager.d.ts +140 -18
  53. package/dist/fleet-manager.js +733 -106
  54. package/dist/fleet-manager.js.map +1 -1
  55. package/dist/forged-envelope-scan.d.ts +160 -0
  56. package/dist/forged-envelope-scan.js +312 -0
  57. package/dist/forged-envelope-scan.js.map +1 -0
  58. package/dist/general-knowledge/skills/persona-emoji/SKILL.md +52 -0
  59. package/dist/instance-lifecycle.d.ts +81 -6
  60. package/dist/instance-lifecycle.js +201 -17
  61. package/dist/instance-lifecycle.js.map +1 -1
  62. package/dist/locale.js +34 -4
  63. package/dist/locale.js.map +1 -1
  64. package/dist/login-controller.js +11 -4
  65. package/dist/login-controller.js.map +1 -1
  66. package/dist/login-flows.d.ts +18 -1
  67. package/dist/login-flows.js +9 -2
  68. package/dist/login-flows.js.map +1 -1
  69. package/dist/ls-rows.d.ts +17 -0
  70. package/dist/ls-rows.js +73 -0
  71. package/dist/ls-rows.js.map +1 -1
  72. package/dist/outbound-handlers.d.ts +3 -0
  73. package/dist/outbound-handlers.js +3 -3
  74. package/dist/outbound-handlers.js.map +1 -1
  75. package/dist/outbound-schemas.d.ts +21 -4
  76. package/dist/outbound-schemas.js +12 -0
  77. package/dist/outbound-schemas.js.map +1 -1
  78. package/dist/paths.js +4 -2
  79. package/dist/paths.js.map +1 -1
  80. package/dist/pause-marker.d.ts +10 -1
  81. package/dist/pause-marker.js +26 -2
  82. package/dist/pause-marker.js.map +1 -1
  83. package/dist/service-installer.d.ts +8 -0
  84. package/dist/service-installer.js +36 -1
  85. package/dist/service-installer.js.map +1 -1
  86. package/dist/settings-api.d.ts +8 -8
  87. package/dist/settings-api.js +12 -5
  88. package/dist/settings-api.js.map +1 -1
  89. package/dist/status-emojis.d.ts +19 -0
  90. package/dist/status-emojis.js.map +1 -1
  91. package/dist/tool-permissions.js +8 -2
  92. package/dist/tool-permissions.js.map +1 -1
  93. package/dist/topic-commands.d.ts +9 -0
  94. package/dist/topic-commands.js +64 -21
  95. package/dist/topic-commands.js.map +1 -1
  96. package/dist/transcript-monitor.js +3 -0
  97. package/dist/transcript-monitor.js.map +1 -1
  98. package/dist/transcript-sources.d.ts +63 -0
  99. package/dist/transcript-sources.js +171 -27
  100. package/dist/transcript-sources.js.map +1 -1
  101. package/dist/types.d.ts +25 -0
  102. package/dist/types.js +9 -1
  103. package/dist/types.js.map +1 -1
  104. package/dist/ui/settings.html +143 -49
  105. package/dist/wake-coordinator.d.ts +143 -0
  106. package/dist/wake-coordinator.js +321 -0
  107. package/dist/wake-coordinator.js.map +1 -0
  108. package/dist/web-api.d.ts +3 -1
  109. package/dist/web-api.js +1 -1
  110. package/dist/web-api.js.map +1 -1
  111. package/dist/web-terminal.d.ts +2 -0
  112. package/dist/web-terminal.js +3 -6
  113. package/dist/web-terminal.js.map +1 -1
  114. package/package.json +1 -1
  115. package/templates/systemd.service.ejs +7 -0
package/dist/cli.js CHANGED
@@ -550,6 +550,92 @@ delivery
550
550
  process.exitCode = 1;
551
551
  }
552
552
  });
553
+ delivery
554
+ .command("scan-forged-envelopes")
555
+ .description("Detect fabricated peer envelopes in a kiro receiver's own transcript (#995)")
556
+ .option("--instance <name>", "Scan one instance")
557
+ .option("--all", "Scan every kiro-cli instance in fleet.yaml")
558
+ .option("--json", "Machine-readable output")
559
+ .option("--kiro-db <path>", "Override the kiro conversation store path (testing)")
560
+ .option("--outbox-db <path>", "Override the delivery-outbox path (testing)")
561
+ .action(async (opts) => {
562
+ const { executeForgedEnvelopeScan, resolveScanTarget, loadReportedEnvelopeIds, recordReportedEnvelopeIds } = await import("./forged-envelope-scan.js");
563
+ if (!opts.instance && !opts.all) {
564
+ console.error("Specify --instance <name> or --all.");
565
+ process.exitCode = 1;
566
+ return;
567
+ }
568
+ let rawConfig;
569
+ try {
570
+ const loaded = loadRawFleetConfig(FLEET_CONFIG_PATH);
571
+ rawConfig = {
572
+ ...(loaded.defaults ? { defaults: loaded.defaults } : {}),
573
+ ...(loaded.instances ? { instances: loaded.instances } : {}),
574
+ };
575
+ }
576
+ catch (err) {
577
+ console.error(`Could not read fleet config: ${err.message}`);
578
+ process.exitCode = 1;
579
+ return;
580
+ }
581
+ const instances = rawConfig.instances ?? {};
582
+ const defaults = (rawConfig.defaults ?? {});
583
+ const knownInstances = new Set(Object.keys(instances));
584
+ const names = opts.all ? [...knownInstances] : [opts.instance];
585
+ if (names.length === 0 || (names.length === 1 && !knownInstances.has(names[0]))) {
586
+ console.error(`Unknown instance: ${names[0] ?? "(fleet has no instances)"}`);
587
+ process.exitCode = 1;
588
+ return;
589
+ }
590
+ // Effective config exactly like the daemon's: fleet defaults deep-merged
591
+ // under instance overrides — a credential profile inherited from defaults
592
+ // must resolve to the profile store (#1007 round-3).
593
+ const targets = names.map(name => resolveScanTarget(name, instances[name], defaults));
594
+ const reported = loadReportedEnvelopeIds(DATA_DIR);
595
+ const result = executeForgedEnvelopeScan({
596
+ targets,
597
+ knownInstances,
598
+ outboxDbPath: opts.outboxDb ?? join(DATA_DIR, "delivery-outbox.db"),
599
+ dataDir: DATA_DIR,
600
+ ...(opts.kiroDb ? { kiroDbOverride: opts.kiroDb } : {}),
601
+ alreadyReportedIds: reported,
602
+ });
603
+ for (const row of result.rows) {
604
+ for (const f of row.findings)
605
+ reported.add(f.message_id);
606
+ }
607
+ recordReportedEnvelopeIds(DATA_DIR, reported);
608
+ if (opts.json) {
609
+ console.log(JSON.stringify({ scans: result.rows, new_findings: result.newFindings }, null, 2));
610
+ }
611
+ else {
612
+ for (const row of result.rows) {
613
+ if (row.status === "unsupported-backend" || row.status === "error" || row.status === "unreadable-store") {
614
+ console.log(`- ${row.instance}: ${row.status} (${row.reason ?? row.note})`);
615
+ continue;
616
+ }
617
+ console.log(`- ${row.instance}: ${row.status} (checked ${row.checked}, delivered ${row.delivered}, unverifiable ${row.unverifiable})`);
618
+ for (const f of row.findings) {
619
+ console.log(` FORGED [from:${f.from}] (message_id: ${f.message_id})`);
620
+ console.log(` excerpt: ${f.excerpt}`);
621
+ console.log(` inject via send_to_instance: ${f.suggested_injection}`);
622
+ }
623
+ }
624
+ }
625
+ // Operator notification: visible here; wire to a schedule for routine runs.
626
+ if (result.newFindings > 0) {
627
+ console.error(`FORGED ENVELOPES: ${result.newFindings} new finding(s) — notify the operator and inject the warning(s) above.`);
628
+ }
629
+ else if (result.exitCode === 3) {
630
+ console.error(`UNREADABLE TRANSCRIPT: ${result.unreadable} instance(s) could not be scanned — not clean.`);
631
+ }
632
+ else if (result.exitCode === 4) {
633
+ // Fail-closed: no-conversation / partially-unverified / missing
634
+ // working_directory are "could not verify", never clean.
635
+ console.error(`INCOMPLETE SCAN: ${result.incomplete} instance(s) could not be fully verified — not clean.`);
636
+ }
637
+ process.exitCode = result.exitCode;
638
+ });
553
639
  fleet
554
640
  .command("activity")
555
641
  .description("Show fleet activity log — who talked to whom, tool calls, task updates")
@@ -1604,7 +1690,7 @@ program
1604
1690
  // service's own HOME, which may differ from what this command resolves (sudo,
1605
1691
  // AGEND_HOME) — an absent pid file does NOT mean it's stopped. Ask the service
1606
1692
  // manager first. This is the canonical restart the `update` flow spawns.
1607
- const { detectPlatform, getServicePath, getSystemServicePath, getSystemdServiceState, restartSystemdService, } = await import("./service-installer.js");
1693
+ const { detectPlatform, getServicePath, getSystemServicePath, getSystemdServiceState, restartSystemdService, ensureSystemdKillModeMixed, } = await import("./service-installer.js");
1608
1694
  const { spawn, spawnSync } = await import("node:child_process");
1609
1695
  const plat = detectPlatform();
1610
1696
  const pidPath = join(DATA_DIR, "fleet.pid");
@@ -1643,7 +1729,52 @@ program
1643
1729
  process.exitCode = 1;
1644
1730
  return;
1645
1731
  }
1646
- run(systemdTarget.user ? "systemctl --user daemon-reload" : "systemctl daemon-reload");
1732
+ // #908: before this restart's stop, so it already lets the fleet quit
1733
+ // each CLI itself instead of systemd SIGTERMing them all at once. A
1734
+ // restart that would still run on control-group (the line could not be
1735
+ // read, written or loaded) is refused: it is exactly the restart that
1736
+ // aborts kiro-cli into a core dump.
1737
+ const unitPath = systemdTarget.user ? getServicePath() : getSystemServicePath();
1738
+ const reloadCmd = systemdTarget.user ? "systemctl --user daemon-reload" : "systemctl daemon-reload";
1739
+ let outcome = "present";
1740
+ if (unitPath) {
1741
+ try {
1742
+ outcome = ensureSystemdKillModeMixed(unitPath);
1743
+ }
1744
+ catch {
1745
+ outcome = "unwritable";
1746
+ }
1747
+ }
1748
+ if (outcome === "unreadable" || outcome === "unwritable") {
1749
+ console.error(` ✗ Could not ${outcome === "unreadable" ? "read" : "update"} ${unitPath}, so this restart would still let systemd SIGTERM every CLI at once (kiro-cli core dumps, #908).`);
1750
+ console.error(` Not restarting. Add KillMode=mixed under [Service] in ${unitPath}, run \`${reloadCmd}\`, then \`agend restart\`.`);
1751
+ process.exitCode = 1;
1752
+ return;
1753
+ }
1754
+ if (outcome === "custom")
1755
+ console.log(` ⚠ ${unitPath} sets its own KillMode; left as is. KillMode=mixed avoids kiro-cli core dumps on restart (#908).`);
1756
+ const reloaded = run(reloadCmd);
1757
+ if (outcome !== "custom") {
1758
+ // Gate on what systemd has LOADED, not on what this run wrote: `agend
1759
+ // update` runs the new binary's `install --no-activate` first, which
1760
+ // already puts KillMode=mixed in the file, so a failed reload here
1761
+ // would otherwise go unnoticed and stop the fleet with control-group.
1762
+ const showCmd = `systemctl${systemdTarget.user ? " --user" : ""} show -p KillMode --value ${systemdTarget.unit}`;
1763
+ let loaded = "";
1764
+ try {
1765
+ loaded = execSync(showCmd, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: 5000 }).trim();
1766
+ }
1767
+ catch { /* unknown → refused below */ }
1768
+ if (loaded !== "mixed") {
1769
+ console.error(` ✗ systemd has ${loaded ? `KillMode=${loaded}` : "an unknown KillMode"} loaded for ${systemdTarget.unit}${reloaded ? "" : ` (\`${reloadCmd}\` failed)`}, so it would still SIGTERM every CLI at once (kiro-cli core dumps, #908).`);
1770
+ console.error(` Not restarting. Make sure ${unitPath ?? "the unit"} has KillMode=mixed under [Service], run \`${reloadCmd}\`, then \`agend restart\`.`);
1771
+ process.exitCode = 1;
1772
+ return;
1773
+ }
1774
+ }
1775
+ if (outcome === "added") {
1776
+ console.log(` ✓ ${unitPath}: added KillMode=mixed (the fleet stops its CLIs; systemd no longer signals them all at once)`);
1777
+ }
1647
1778
  if (systemdTarget.user) {
1648
1779
  try {
1649
1780
  execSync("systemctl --user reset-failed com.agend.fleet", { stdio: "pipe", timeout: 5000 });
@@ -2368,7 +2499,7 @@ async function lsAction(opts) {
2368
2499
  };
2369
2500
  // Row collection runs concurrently (see ls-rows.ts): the per-instance
2370
2501
  // tmux captures overlap instead of stacking their 2s bounds serially.
2371
- const { collectLsRows, capturePaneAsync, formatTimeSince } = await import("./ls-rows.js");
2502
+ const { collectLsRows, capturePaneAsync, formatTimeSince, lsStatusIcon, lsStatusLabel, displayWidth } = await import("./ls-rows.js");
2372
2503
  const { getTmuxSocketName } = await import("./paths.js");
2373
2504
  const socketName = getTmuxSocketName();
2374
2505
  const rows = await collectLsRows(allNames.map(name => {
@@ -2434,51 +2565,10 @@ async function lsAction(opts) {
2434
2565
  console.log(JSON.stringify(rows, null, 2));
2435
2566
  return;
2436
2567
  }
2437
- // Status icon — prefer tri-state 'state' field, fall back to idle boolean
2438
- const statusIcon = (s, idle, state) => {
2439
- if (s === "crashed")
2440
- return "\x1b[31m●\x1b[0m";
2441
- if (s === "stopped")
2442
- return "\x1b[90m○\x1b[0m";
2443
- if (state === "stuck")
2444
- return "\x1b[31m●\x1b[0m";
2445
- if (state === "paused" || s === "paused")
2446
- return "\x1b[2;33m●\x1b[0m";
2447
- if (state === "working")
2448
- return "\x1b[34m●\x1b[0m";
2449
- if (state === "idle")
2450
- return "\x1b[32m●\x1b[0m";
2451
- // Fallback for when API is unreachable
2452
- if (s === "running")
2453
- return idle === false ? "\x1b[34m●\x1b[0m" : "\x1b[32m●\x1b[0m";
2454
- return "\x1b[90m○\x1b[0m";
2455
- };
2456
- const statusLabel = (s, idle, state) => {
2457
- if (s === "crashed")
2458
- return "Crashed";
2459
- if (s === "stopped")
2460
- return "Stopped";
2461
- if (state === "stuck")
2462
- return "Stuck";
2463
- if (state === "paused" || s === "paused")
2464
- return "Paused";
2465
- if (state === "working")
2466
- return "Working";
2467
- if (state === "idle")
2468
- return "Idle";
2469
- if (s === "running")
2470
- return idle === false ? "Busy" : "Idle";
2471
- return "Stopped";
2472
- };
2473
- /** Get display width accounting for fullwidth (CJK) characters */
2474
- const displayWidth = (s) => {
2475
- let w = 0;
2476
- for (const ch of s) {
2477
- const cp = ch.codePointAt(0);
2478
- w += (cp > 0x7f && cp !== 0x200b) ? 2 : 1;
2479
- }
2480
- return w;
2481
- };
2568
+ // Merged State column (#1052): one glyph per lifecycle/execution state,
2569
+ // shared with /status via ls-rows (○ paused, ✗ stopped).
2570
+ const statusIcon = lsStatusIcon;
2571
+ const statusLabel = lsStatusLabel;
2482
2572
  const padDisplay = (s, width) => s + " ".repeat(Math.max(0, width - displayWidth(s)));
2483
2573
  const displayName = (row) => row.name;
2484
2574
  const nameW = Math.max(20, ...rows.map(r => displayWidth(displayName(r)) + 2));