@songsid/agend 2.1.10-beta.1 → 2.1.10-beta.3

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 (57) hide show
  1. package/README.md +1 -1
  2. package/README.zh-TW.md +1 -1
  3. package/dist/backend/antigravity.js +2 -1
  4. package/dist/backend/antigravity.js.map +1 -1
  5. package/dist/backend/kiro.d.ts +81 -3
  6. package/dist/backend/kiro.js +362 -29
  7. package/dist/backend/kiro.js.map +1 -1
  8. package/dist/backend/types.d.ts +10 -0
  9. package/dist/backend/types.js +13 -0
  10. package/dist/backend/types.js.map +1 -1
  11. package/dist/channel/ipc-bridge.js +10 -3
  12. package/dist/channel/ipc-bridge.js.map +1 -1
  13. package/dist/channel/mcp-server.d.ts +1 -8
  14. package/dist/channel/mcp-server.js +1 -0
  15. package/dist/channel/mcp-server.js.map +1 -1
  16. package/dist/cli.d.ts +1 -1
  17. package/dist/cli.js +141 -36
  18. package/dist/cli.js.map +1 -1
  19. package/dist/coredump-filter.d.ts +21 -0
  20. package/dist/coredump-filter.js +64 -0
  21. package/dist/coredump-filter.js.map +1 -0
  22. package/dist/daemon.d.ts +45 -0
  23. package/dist/daemon.js +153 -22
  24. package/dist/daemon.js.map +1 -1
  25. package/dist/fleet-manager.d.ts +25 -1
  26. package/dist/fleet-manager.js +87 -13
  27. package/dist/fleet-manager.js.map +1 -1
  28. package/dist/general-knowledge/skills/fleet-config/SKILL.md +1 -1
  29. package/dist/instance-lifecycle.d.ts +16 -5
  30. package/dist/instance-lifecycle.js +31 -20
  31. package/dist/instance-lifecycle.js.map +1 -1
  32. package/dist/locale.js +8 -0
  33. package/dist/locale.js.map +1 -1
  34. package/dist/logger.js +19 -16
  35. package/dist/logger.js.map +1 -1
  36. package/dist/muse-usage-relay.js +3 -2
  37. package/dist/muse-usage-relay.js.map +1 -1
  38. package/dist/node-warnings.d.ts +1 -0
  39. package/dist/node-warnings.js +26 -0
  40. package/dist/node-warnings.js.map +1 -0
  41. package/dist/paths.d.ts +15 -3
  42. package/dist/paths.js +45 -25
  43. package/dist/paths.js.map +1 -1
  44. package/dist/pause-marker.js +3 -2
  45. package/dist/pause-marker.js.map +1 -1
  46. package/dist/private-dir.d.ts +54 -0
  47. package/dist/private-dir.js +140 -0
  48. package/dist/private-dir.js.map +1 -0
  49. package/dist/service-installer.d.ts +46 -0
  50. package/dist/service-installer.js +224 -4
  51. package/dist/service-installer.js.map +1 -1
  52. package/dist/ui/settings.html +2 -2
  53. package/dist/update-check.d.ts +22 -0
  54. package/dist/update-check.js +31 -0
  55. package/dist/update-check.js.map +1 -1
  56. package/package.json +1 -1
  57. package/templates/systemd.service.ejs +17 -1
@@ -184,6 +184,8 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
184
184
  /** Reasons that describe the adapter, not one topic — counted once per adapter. */
185
185
  private static readonly TOPIC_PROBE_ADAPTER_SCOPED_REASONS;
186
186
  logger: Logger;
187
+ /** Report each bundled skill collision once per fleet, across instance starts. */
188
+ private skippedBundledSkills;
187
189
  private topicCommands;
188
190
  sessionRegistry: Map<string, string>;
189
191
  eventLog: EventLog | null;
@@ -246,6 +248,8 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
246
248
  private stopsInFlight;
247
249
  /** Aggregation window for the "N instances failed to start" notice. */
248
250
  private startupRetryNotices;
251
+ /** Unsupported-CLI refusals waiting to go out, grouped by reason (#1109). */
252
+ private unsupportedCliNotices;
249
253
  /** Backoff between automatic startup retries; the last step repeats while the backend is down. */
250
254
  static readonly STARTUP_RETRY_BACKOFF_MS: number[];
251
255
  /** Hard cap on automatic startup retries (3 backoff steps + up to 3 more during a backend outage). */
@@ -479,6 +483,13 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
479
483
  */
480
484
  private reregisterClassicChannels;
481
485
  getInstanceDir(name: string): string;
486
+ /**
487
+ * One-time repair of what earlier versions left behind: instance directories (agent.token and the IPC
488
+ * socket live in them) created with the process umask — typically 0775, group-writable and traversable
489
+ * by everyone. They are made 0700 here, once, and said so once; new ones are born 0700 (ensureInstanceDir).
490
+ * Idempotent, so a restart that finds nothing to do logs nothing. Never throws.
491
+ */
492
+ private tightenInstanceDirectories;
482
493
  /** AgEnD package version (for the Settings "current version" / What's New). */
483
494
  get currentVersion(): string;
484
495
  /**
@@ -746,6 +757,17 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
746
757
  * fails many instances within the same second. Two notices per incident at
747
758
  * most — "N failed, retrying in X" and, if it comes to that, "gave up on N".
748
759
  */
760
+ /**
761
+ * The CLI refuses this instance as configured (#1109): the same binary would
762
+ * refuse every retry, so say why once and leave it stopped. Shared by the
763
+ * first unattended start and every scheduled retry.
764
+ */
765
+ private stopOnUnsupportedCli;
766
+ /**
767
+ * One notice per refusal reason: a kiro-cli that dropped the legacy UI
768
+ * refuses every kiro instance at once, and that is one message, not ten.
769
+ */
770
+ private queueUnsupportedCliNotice;
749
771
  private queueStartupRetryNotice;
750
772
  private runnableStartupCount;
751
773
  private configuredStartupInstanceNames;
@@ -1317,7 +1339,8 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
1317
1339
  * WatchdogSec and have systemd SIGABRT the fleet.
1318
1340
  */
1319
1341
  private runBackendDoctor;
1320
- /** Drop event/activity rows older than the retention window. Best-effort. */
1342
+ /** Cap both structured runtime logs and service bootstrap diagnostics. */
1343
+ private rotateFleetLogs;
1321
1344
  /**
1322
1345
  * Cap every instance's pipe-pane log, walking the instances **directory** rather
1323
1346
  * than the config.
@@ -1337,6 +1360,7 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
1337
1360
  * fleet down rather than one instance.
1338
1361
  */
1339
1362
  private rotateAllInstanceLogs;
1363
+ /** Drop event/activity rows older than the retention window. Best-effort. */
1340
1364
  private pruneEventLog;
1341
1365
  private openEventLog;
1342
1366
  /**
@@ -29,7 +29,7 @@ import { IpcClient } from "./channel/ipc-bridge.js";
29
29
  import { createAdapter } from "./channel/factory.js";
30
30
  import { TelegramAdapter } from "./channel/adapters/telegram.js";
31
31
  import { createBackend } from "./backend/factory.js";
32
- import { isModelCompatible, SYSINFO_BACKEND_IDS } from "./backend/types.js";
32
+ import { isModelCompatible, SYSINFO_BACKEND_IDS, UnsupportedCliError } from "./backend/types.js";
33
33
  import { createLogger, rotateLogIfNeeded } from "./logger.js";
34
34
  import { processAttachments } from "./channel/attachment-handler.js";
35
35
  import { routeToolCall } from "./channel/tool-router.js";
@@ -56,6 +56,7 @@ import { handleViewRequest, isViewPath } from "./view-api.js";
56
56
  import { filterUsageProviders, formatDiscordUsageActivity, getUsageSnapshot, handleUsageRequest, isUsagePath, usageProviderIdForBackend } from "./usage/usage-api.js";
57
57
  import { LOGIN_FLOWS, LOGIN_BACKEND_ALIASES, checkAuthStatus } from "./login-flows.js";
58
58
  import { LoginSession } from "./login-manager.js";
59
+ import { tightenInstanceDirs } from "./private-dir.js";
59
60
  import { LoginController, LOGIN_TOKEN_RESEND_PREFIX, POST_LOGIN_RECOVERY_DEADLINE_MS, announcePostLoginRecovery } from "./login-controller.js";
60
61
  import { runBeforeDeadline } from "./deadline.js";
61
62
  import { LoginWindowLock } from "./login-window-lock.js";
@@ -363,6 +364,8 @@ export class FleetManager {
363
364
  "provider-unavailable",
364
365
  ]);
365
366
  logger = createLogger("info");
367
+ /** Report each bundled skill collision once per fleet, across instance starts. */
368
+ skippedBundledSkills = new Set();
366
369
  topicCommands;
367
370
  // sessionName → instanceName mapping for external sessions
368
371
  sessionRegistry = new Map();
@@ -431,6 +434,8 @@ export class FleetManager {
431
434
  stopsInFlight = new Map();
432
435
  /** Aggregation window for the "N instances failed to start" notice. */
433
436
  startupRetryNotices = new Map();
437
+ /** Unsupported-CLI refusals waiting to go out, grouped by reason (#1109). */
438
+ unsupportedCliNotices = new Map();
434
439
  /** Backoff between automatic startup retries; the last step repeats while the backend is down. */
435
440
  static STARTUP_RETRY_BACKOFF_MS = [60_000, 5 * 60_000, 15 * 60_000];
436
441
  /** Hard cap on automatic startup retries (3 backoff steps + up to 3 more during a backend outage). */
@@ -1053,6 +1058,7 @@ export class FleetManager {
1053
1058
  // around to notice. Never throws: a lease that cannot be resolved blocks
1054
1059
  // the next tunnel and says so, it does not block the fleet.
1055
1060
  this.announceToolPermissionsChange();
1061
+ this.tightenInstanceDirectories();
1056
1062
  void reapStaleTunnel(this.dataDir)
1057
1063
  .then(outcome => {
1058
1064
  if (outcome.kind === "manual")
@@ -1277,6 +1283,29 @@ export class FleetManager {
1277
1283
  getInstanceDir(name) {
1278
1284
  return join(this.dataDir, "instances", name);
1279
1285
  }
1286
+ /**
1287
+ * One-time repair of what earlier versions left behind: instance directories (agent.token and the IPC
1288
+ * socket live in them) created with the process umask — typically 0775, group-writable and traversable
1289
+ * by everyone. They are made 0700 here, once, and said so once; new ones are born 0700 (ensureInstanceDir).
1290
+ * Idempotent, so a restart that finds nothing to do logs nothing. Never throws.
1291
+ */
1292
+ tightenInstanceDirectories() {
1293
+ try {
1294
+ const report = tightenInstanceDirs(this.dataDir);
1295
+ if (report.tightened.length > 0) {
1296
+ this.logger.info({
1297
+ count: report.tightened.length,
1298
+ modes: [...new Set(report.tightened.map(t => t.from))],
1299
+ }, "Instance directories were open to other users; set to 0700 (they hold agent.token and the IPC socket)");
1300
+ }
1301
+ if (report.skipped.length > 0) {
1302
+ this.logger.warn({ dirs: report.skipped.map(s => `${s.dir} (${s.why})`) }, "Could not tighten these instance directories — fix their owner/permissions by hand (chmod 700)");
1303
+ }
1304
+ }
1305
+ catch (err) {
1306
+ this.logger.warn({ err }, "Instance directory permission check failed");
1307
+ }
1308
+ }
1280
1309
  /** AgEnD package version (for the Settings "current version" / What's New). */
1281
1310
  get currentVersion() {
1282
1311
  try {
@@ -2432,6 +2461,8 @@ export class FleetManager {
2432
2461
  // outcome. Retrying would start an instance the operator just stopped.
2433
2462
  if (err instanceof SupersededStartError)
2434
2463
  return false;
2464
+ if (this.stopOnUnsupportedCli(name, err, what))
2465
+ return false;
2435
2466
  this.logger.error({ err, name }, `Failed to start ${what}`);
2436
2467
  this.scheduleStartupRetry(name, 0);
2437
2468
  return false;
@@ -2556,6 +2587,9 @@ export class FleetManager {
2556
2587
  // Superseded by a stop/restart: that transition owns the outcome.
2557
2588
  if (err instanceof SupersededStartError)
2558
2589
  return;
2590
+ // An earlier attempt could not tell (unknown probe); this one could.
2591
+ if (this.stopOnUnsupportedCli(name, err, "instance"))
2592
+ return;
2559
2593
  this.logger.error({ err, name, attempt: attempt + 1 }, "Automatic startup retry failed");
2560
2594
  this.scheduleStartupRetry(name, attempt + 1);
2561
2595
  }
@@ -2651,6 +2685,38 @@ export class FleetManager {
2651
2685
  * fails many instances within the same second. Two notices per incident at
2652
2686
  * most — "N failed, retrying in X" and, if it comes to that, "gave up on N".
2653
2687
  */
2688
+ /**
2689
+ * The CLI refuses this instance as configured (#1109): the same binary would
2690
+ * refuse every retry, so say why once and leave it stopped. Shared by the
2691
+ * first unattended start and every scheduled retry.
2692
+ */
2693
+ stopOnUnsupportedCli(name, err, what) {
2694
+ if (!(err instanceof UnsupportedCliError))
2695
+ return false;
2696
+ this.logger.error({ name, reason: err.message }, `Not starting ${what}: the installed CLI cannot run it as configured`);
2697
+ this.queueUnsupportedCliNotice(name, err.message);
2698
+ return true;
2699
+ }
2700
+ /**
2701
+ * One notice per refusal reason: a kiro-cli that dropped the legacy UI
2702
+ * refuses every kiro instance at once, and that is one message, not ten.
2703
+ */
2704
+ queueUnsupportedCliNotice(name, reason) {
2705
+ const pending = this.unsupportedCliNotices.get(reason);
2706
+ if (pending) {
2707
+ if (!pending.names.includes(name))
2708
+ pending.names.push(name);
2709
+ return;
2710
+ }
2711
+ const timer = setTimeout(() => {
2712
+ const entry = this.unsupportedCliNotices.get(reason);
2713
+ this.unsupportedCliNotices.delete(reason);
2714
+ if (entry)
2715
+ this.notifyFleetError(t("fleet.cli_unsupported", entry.names.join(", "), reason));
2716
+ }, FleetManager.STARTUP_RETRY_NOTICE_AGGREGATE_MS);
2717
+ timer.unref?.();
2718
+ this.unsupportedCliNotices.set(reason, { names: [name], timer });
2719
+ }
2654
2720
  queueStartupRetryNotice(kind, name, delayMs) {
2655
2721
  const pending = this.startupRetryNotices.get(kind);
2656
2722
  if (pending) {
@@ -3162,8 +3228,7 @@ export class FleetManager {
3162
3228
  this.configPath = configPath;
3163
3229
  this.loadEnvFile();
3164
3230
  this.ensureDeliveryOutbox();
3165
- // Rotate fleet.log if oversized (before any logging)
3166
- rotateLogIfNeeded(join(this.dataDir, "fleet.log"));
3231
+ this.rotateFleetLogs();
3167
3232
  const fleet = this.loadConfig(configPath);
3168
3233
  this.slimFleetConfigAtStartup();
3169
3234
  setLocale(detectLocale(fleet)); // user-facing text language (fleet.yaml defaults.locale / timezone)
@@ -3277,9 +3342,7 @@ export class FleetManager {
3277
3342
  // Rotate classic channel chat logs daily
3278
3343
  this.classicChannels?.rotateLogs();
3279
3344
  this.rotateInboxes();
3280
- // Rotate fleet.log daily too (besides the startup size check above), so a
3281
- // long-running fleet doesn't accumulate an unbounded log.
3282
- rotateLogIfNeeded(join(this.dataDir, "fleet.log"));
3345
+ this.rotateFleetLogs();
3283
3346
  // Instance output.log is pipe-pane (TUI ANSI). Daemon health ticks rotate a
3284
3347
  // running instance's own log; this sweep is the safety net for every other
3285
3348
  // kind. One implementation, so the two cannot cover different sets.
@@ -3495,11 +3558,14 @@ export class FleetManager {
3495
3558
  this.pruneEventLog();
3496
3559
  this.eventLogPruneTimer = setInterval(() => this.pruneEventLog(), 24 * 60 * 60_000);
3497
3560
  this.eventLogPruneTimer.unref?.();
3498
- // Same shape for pipe-pane logs, and for the same reason: the only sweep that
3561
+ // Same shape for logs, and for the same reason: the only sweep that
3499
3562
  // covered them lived inside the daily-summary callback, so it did not run at
3500
3563
  // all when summaries were off.
3501
3564
  this.rotateAllInstanceLogs();
3502
- this.logRotateTimer = setInterval(() => this.rotateAllInstanceLogs(), 24 * 60 * 60_000);
3565
+ this.logRotateTimer = setInterval(() => {
3566
+ this.rotateFleetLogs();
3567
+ this.rotateAllInstanceLogs();
3568
+ }, 24 * 60 * 60_000);
3503
3569
  this.logRotateTimer.unref?.();
3504
3570
  // Phase 2: Start remaining instances with staggered concurrency
3505
3571
  if (others.length > 0) {
@@ -7769,7 +7835,11 @@ export class FleetManager {
7769
7835
  return stripAnsi(e.message ?? "").trim() || t("doctor.failed");
7770
7836
  }
7771
7837
  }
7772
- /** Drop event/activity rows older than the retention window. Best-effort. */
7838
+ /** Cap both structured runtime logs and service bootstrap diagnostics. */
7839
+ rotateFleetLogs() {
7840
+ rotateLogIfNeeded(join(this.dataDir, "daemon.log"));
7841
+ rotateLogIfNeeded(join(this.dataDir, "fleet.log"));
7842
+ }
7773
7843
  /**
7774
7844
  * Cap every instance's pipe-pane log, walking the instances **directory** rather
7775
7845
  * than the config.
@@ -7805,6 +7875,7 @@ export class FleetManager {
7805
7875
  rotateLogIfNeeded(join(root, entry.name, "output.log"));
7806
7876
  }
7807
7877
  }
7878
+ /** Drop event/activity rows older than the retention window. Best-effort. */
7808
7879
  pruneEventLog() {
7809
7880
  try {
7810
7881
  this.eventLog?.prune(FleetManager.EVENT_LOG_RETENTION_DAYS);
@@ -10583,8 +10654,11 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
10583
10654
  const dest = join(destDir, "SKILL.md");
10584
10655
  const isOurs = previouslyManaged.includes(name) || !existsSync(destDir);
10585
10656
  if (!isOurs) {
10586
- // Name collision with a user-authored skill: theirs wins, loudly.
10587
- this.logger.warn({ skill: name, destSkills }, "Skipping bundled skill — a skill of this name exists but was not published by AgEnD");
10657
+ // The user's skill still wins on every sync; only the warning is deduped.
10658
+ if (!this.skippedBundledSkills.has(name)) {
10659
+ this.skippedBundledSkills.add(name);
10660
+ this.logger.warn({ skill: name, destSkills }, "Skipping bundled skill — a skill of this name exists but was not published by AgEnD");
10661
+ }
10588
10662
  continue;
10589
10663
  }
10590
10664
  managed.push(name);
@@ -12898,7 +12972,7 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
12898
12972
  return {
12899
12973
  ok: false,
12900
12974
  status: 409,
12901
- error: `fleet.yaml and the running configuration disagree on ${this.fleetSignatureMismatch.join(", ")} — check fleet.log before restarting`,
12975
+ error: `fleet.yaml and the running configuration disagree on ${this.fleetSignatureMismatch.join(", ")} — check daemon.log before restarting`,
12902
12976
  };
12903
12977
  }
12904
12978
  const job = this.applyJobs.get(jobId);
@@ -12973,7 +13047,7 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
12973
13047
  this.applyJobs.setTargetStatus(jobId, APPLY_FLEET_TARGET, "failed", "restart could not be launched");
12974
13048
  this.applyJobs.finish(jobId, "restart could not be launched");
12975
13049
  this.emitSseEvent("apply_progress", viewOf(this.applyJobs.get(jobId)));
12976
- return { ok: false, status: 409, error: "the restart could not be launched — see fleet.log" };
13050
+ return { ok: false, status: 409, error: "the restart could not be launched — see daemon.log" };
12977
13051
  }
12978
13052
  return { ok: true, jobId };
12979
13053
  }