@songsid/agend 2.1.10-beta.1 → 2.1.10-beta.2
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/README.md +1 -1
- package/README.zh-TW.md +1 -1
- package/dist/backend/antigravity.js +2 -1
- package/dist/backend/antigravity.js.map +1 -1
- package/dist/backend/kiro.d.ts +81 -3
- package/dist/backend/kiro.js +362 -29
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/types.d.ts +10 -0
- package/dist/backend/types.js +13 -0
- package/dist/backend/types.js.map +1 -1
- package/dist/channel/ipc-bridge.js +10 -3
- package/dist/channel/ipc-bridge.js.map +1 -1
- package/dist/channel/mcp-server.d.ts +1 -8
- package/dist/channel/mcp-server.js +1 -0
- package/dist/channel/mcp-server.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +81 -6
- package/dist/cli.js.map +1 -1
- package/dist/daemon.d.ts +45 -0
- package/dist/daemon.js +148 -21
- package/dist/daemon.js.map +1 -1
- package/dist/fleet-manager.d.ts +25 -1
- package/dist/fleet-manager.js +87 -13
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-knowledge/skills/fleet-config/SKILL.md +1 -1
- package/dist/instance-lifecycle.d.ts +16 -5
- package/dist/instance-lifecycle.js +31 -20
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/locale.js +8 -0
- package/dist/locale.js.map +1 -1
- package/dist/logger.js +19 -16
- package/dist/logger.js.map +1 -1
- package/dist/muse-usage-relay.js +3 -2
- package/dist/muse-usage-relay.js.map +1 -1
- package/dist/node-warnings.d.ts +1 -0
- package/dist/node-warnings.js +26 -0
- package/dist/node-warnings.js.map +1 -0
- package/dist/pause-marker.js +3 -2
- package/dist/pause-marker.js.map +1 -1
- package/dist/private-dir.d.ts +54 -0
- package/dist/private-dir.js +140 -0
- package/dist/private-dir.js.map +1 -0
- package/dist/service-installer.d.ts +46 -0
- package/dist/service-installer.js +224 -4
- package/dist/service-installer.js.map +1 -1
- package/dist/ui/settings.html +2 -2
- package/package.json +1 -1
- package/templates/systemd.service.ejs +17 -1
package/dist/fleet-manager.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
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
|
/**
|
package/dist/fleet-manager.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
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(() =>
|
|
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
|
-
/**
|
|
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
|
-
//
|
|
10587
|
-
this.
|
|
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
|
|
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
|
|
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
|
}
|