@songsid/agend 2.3.0-beta.11 → 2.3.0-beta.12
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/agent-whats-new.js +14 -9
- package/dist/agent-whats-new.js.map +1 -1
- package/dist/channel/adapters/discord-client-retirement.d.ts +5 -0
- package/dist/channel/adapters/discord-client-retirement.js +57 -0
- package/dist/channel/adapters/discord-client-retirement.js.map +1 -0
- package/dist/channel/adapters/discord.d.ts +2 -0
- package/dist/channel/adapters/discord.js +14 -4
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/cli.js +12 -79
- package/dist/cli.js.map +1 -1
- package/dist/daemon.d.ts +3 -0
- package/dist/daemon.js +25 -0
- package/dist/daemon.js.map +1 -1
- package/dist/fleet-manager.d.ts +8 -1
- package/dist/fleet-manager.js +101 -33
- package/dist/fleet-manager.js.map +1 -1
- package/dist/fleet-process-handlers.d.ts +18 -0
- package/dist/fleet-process-handlers.js +69 -0
- package/dist/fleet-process-handlers.js.map +1 -0
- package/dist/general-knowledge/skills/web-appearance/SKILL.md +1 -1
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +4 -0
- package/dist/instance-lifecycle.d.ts +2 -0
- package/dist/instance-lifecycle.js +11 -6
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/outbound-schemas.js +1 -1
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/settings-api.d.ts +4 -0
- package/dist/settings-api.js +23 -0
- package/dist/settings-api.js.map +1 -1
- package/dist/ui/app-effects.js +13 -1
- package/dist/ui/chat-store.js +32 -10
- package/dist/ui/chat-strings.js +16 -0
- package/dist/ui/chat-thread.js +5 -2
- package/dist/ui/panel-chat.js +24 -3
- package/dist/ui/panel-settings.js +24 -0
- package/dist/ui/settings-strings.js +2 -0
- package/dist/ui/shared/effect-owner-source.js +8 -1
- package/dist/ui/shared/effect-registry.js +10 -2
- package/dist/ui/shared/owner-css.js +30 -1
- package/dist/web-api.d.ts +6 -2
- package/dist/web-api.js +50 -14
- package/dist/web-api.js.map +1 -1
- package/dist/web-owner-css-store.d.ts +43 -1
- package/dist/web-owner-css-store.js +65 -9
- package/dist/web-owner-css-store.js.map +1 -1
- package/dist/web-settings-tools.js +7 -4
- package/dist/web-settings-tools.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Synchronous evidence before shutdown: neither a network nor logger worker is required. */
|
|
2
|
+
export declare function recordFleetFatal(dataDir: string, error: unknown): void;
|
|
3
|
+
interface FleetProcessOptions {
|
|
4
|
+
dataDir: string;
|
|
5
|
+
manager: {
|
|
6
|
+
stopAll(): Promise<void>;
|
|
7
|
+
notifyFleetError(message: string): void;
|
|
8
|
+
};
|
|
9
|
+
runtime: Pick<NodeJS.Process, "on" | "exit">;
|
|
10
|
+
describeSignal: () => string;
|
|
11
|
+
console?: Pick<Console, "log" | "error">;
|
|
12
|
+
writeFatal?: typeof recordFleetFatal;
|
|
13
|
+
}
|
|
14
|
+
/** Identical process policy for cold start and the legacy in-process reload. */
|
|
15
|
+
export declare function installFleetProcessHandlers(options: FleetProcessOptions): {
|
|
16
|
+
readonly stopping: boolean;
|
|
17
|
+
};
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { closeSync, fsyncSync, mkdirSync, openSync, writeSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
/** Synchronous evidence before shutdown: neither a network nor logger worker is required. */
|
|
4
|
+
export function recordFleetFatal(dataDir, error) {
|
|
5
|
+
mkdirSync(dataDir, { recursive: true });
|
|
6
|
+
const fd = openSync(join(dataDir, "fleet-fatal.log"), "a", 0o600);
|
|
7
|
+
try {
|
|
8
|
+
writeSync(fd, JSON.stringify({ timestamp: new Date().toISOString(), pid: process.pid,
|
|
9
|
+
argv: process.argv, reason: "uncaughtException", error: String(error),
|
|
10
|
+
stack: error instanceof Error ? error.stack : undefined }) + "\n");
|
|
11
|
+
fsyncSync(fd);
|
|
12
|
+
}
|
|
13
|
+
finally {
|
|
14
|
+
closeSync(fd);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/** Identical process policy for cold start and the legacy in-process reload. */
|
|
18
|
+
export function installFleetProcessHandlers(options) {
|
|
19
|
+
const { runtime, manager } = options;
|
|
20
|
+
const output = options.console ?? console;
|
|
21
|
+
let stopping = false;
|
|
22
|
+
const shutdown = async () => {
|
|
23
|
+
if (stopping)
|
|
24
|
+
return;
|
|
25
|
+
stopping = true;
|
|
26
|
+
output.log("\nStopping fleet...");
|
|
27
|
+
// stopAll fences new controls and drains CPU profiles before disposal.
|
|
28
|
+
await manager.stopAll().catch(error => output.error("Shutdown error:", error));
|
|
29
|
+
runtime.exit(0);
|
|
30
|
+
};
|
|
31
|
+
runtime.on("SIGINT", shutdown);
|
|
32
|
+
runtime.on("SIGTERM", () => {
|
|
33
|
+
output.log(options.describeSignal());
|
|
34
|
+
return shutdown();
|
|
35
|
+
});
|
|
36
|
+
runtime.on("uncaughtException", async (error) => {
|
|
37
|
+
output.error("Uncaught exception:", error);
|
|
38
|
+
try {
|
|
39
|
+
(options.writeFatal ?? recordFleetFatal)(options.dataDir, error);
|
|
40
|
+
}
|
|
41
|
+
catch (writeError) {
|
|
42
|
+
output.error("Failed to persist fleet fatal reason:", writeError);
|
|
43
|
+
}
|
|
44
|
+
if (stopping)
|
|
45
|
+
return;
|
|
46
|
+
stopping = true;
|
|
47
|
+
// Arbitrary uncaught exceptions still leave unknown state and remain fatal.
|
|
48
|
+
await manager.stopAll().catch(() => { });
|
|
49
|
+
runtime.exit(1);
|
|
50
|
+
});
|
|
51
|
+
runtime.on("unhandledRejection", error => {
|
|
52
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
53
|
+
if (message.includes("409") && message.includes("getUpdates")) {
|
|
54
|
+
output.error("Bot polling conflict (409) — retrying...");
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
output.error("Unhandled rejection (fleet continues):", error);
|
|
58
|
+
if (error instanceof Error && error.stack)
|
|
59
|
+
output.error(error.stack);
|
|
60
|
+
try {
|
|
61
|
+
manager.notifyFleetError(`⚠️ Fleet caught an unhandled error and kept running:\n\`${message}\`\nSee fleet.log for the stack trace.`);
|
|
62
|
+
}
|
|
63
|
+
catch (notifyError) {
|
|
64
|
+
output.error("Failed to report unhandled rejection:", notifyError);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
return { get stopping() { return stopping; } };
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=fleet-process-handlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fleet-process-handlers.js","sourceRoot":"","sources":["../src/fleet-process-handlers.ts"],"names":[],"mappings":"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"}
|
|
@@ -43,7 +43,7 @@ of this skill).
|
|
|
43
43
|
| `host` | `themes_shown`, `card_size` (s/m/l), `card_sizes` ({instance: s/m/l}), `colour_places` (name/bar/ring), `colours_off`, `sidebar_widget` ({spec, data}) — null puts one back to its default | `{ what: "host", set: { colour_places: ["name","ring"] } }` |
|
|
44
44
|
| `agent_colour` | one agent's colour (`preset:<name>` or `#rrggbb`) and places; null resets each | `{ what: "agent_colour", instance: "atlas", colour: "preset:teal" }` |
|
|
45
45
|
| `banner_focus` | which part of one agent's cropped card banner stays in view: `focus` 0 (top) … 100 (bottom), 50 the centre; it wins over the agent's own `card.bannerFocus`; null follows the agent's again | `{ what: "banner_focus", instance: "atlas", focus: 20 }` |
|
|
46
|
-
| `custom_css` | `save` (with `text`): goes **on trial
|
|
46
|
+
| `custom_css` | `save` (with `text`): goes **on trial**. It waits until they open the dashboard (up to 10 minutes; never opened, it goes back unseen), then goes back by itself 20 s after it is on their screen unless kept (#2198). `keep` (the trial; or, kept after that 20 s ran out, it is put back in force as `use` of it), `revert` (the trial), `use` (with `version`: an earlier saved one, at once), `off` | `{ what: "custom_css", action: "save", text: "…" }`; tell them to open the dashboard and look, then `keep` only when they say so — a `keep` that comes too late (`not_on_trial`) means use `use` with the version from `get_web_settings` |
|
|
47
47
|
| `mod` | an agent's proposal: `review` (instance → `stage`, `plan`, `plan_hash`), then `install` (stage, plan_hash, `include`: the parts, `target`: the agent); `cancel`, `dismiss`, `uninstall` (id) | review, show the user the plan, install only what they asked for — never custom CSS unasked. A person has one review open at a time: while theirs is open (`mod_review_open`) ask them to finish it; a mod they imported from a file is theirs to install (`mod_not_from_an_agent`) |
|
|
48
48
|
| `effect` | `apply` (instance, id, params?) or `remove` (instance, id?) an effect on a chat | |
|
|
49
49
|
| `ambient` | one agent's ambient effect, set for the user: `ambient` is the same spec as `set_web_ambient`'s (it replaces the one of its kind, and the agent can't change it after); `ambient: null` clears (`which`: `loop` or `click`, default both) | `{ what: "ambient", instance: "atlas", ambient: { preset: "fall", glyphs: ["🌸","❀","✿"] } }` |
|
|
@@ -98,6 +98,8 @@ Agents then see that browser's messages as `via web (verified)` and check their
|
|
|
98
98
|
|
|
99
99
|
## Chat
|
|
100
100
|
|
|
101
|
+
A message to a paused agent wakes it through the same delivery path as Discord/Telegram, including ClassicBot. The composer shows **Waking…**; a refused wake explains why, keeps the draft/files and offers **Retry send**. No automatic resend. Stopped agents still require Start. Upload/history/Cancel do not wake agents. Existing guest/public-link send permissions are unchanged.
|
|
102
|
+
|
|
101
103
|
**Sending.** Type and press Enter. A message sent while the agent works waits its turn; `/steer <text>` goes into
|
|
102
104
|
the current turn, `/btw <question>` asks a side question. **Stop reply** (or Esc) interrupts the reply and drops
|
|
103
105
|
queued messages. Text is capped at 16,384 characters; a paste over 4,000 characters becomes a `pasted-….txt`
|
|
@@ -285,6 +287,8 @@ running; kiro shows fleet events only; other backends say they have no readable
|
|
|
285
287
|
|
|
286
288
|
## Settings
|
|
287
289
|
|
|
290
|
+
**Connections → Retry now** retries a disconnected connection without a fleet restart; a running login is reused. Startup retries continue with a five-minute cap. Local admin only, unavailable over public links. It changes no token or binding; refresh to read the later result.
|
|
291
|
+
|
|
288
292
|
Tabs (`/settings/<tab>`): **Preferences**, **Web appearance**, **Agents**, **Connections**, **ClassicBot**,
|
|
289
293
|
**General**, **Web**, **Developer**. Header: version, **Setup wizard**, **Help**.
|
|
290
294
|
|
|
@@ -201,6 +201,8 @@ export interface IncidentEventSource {
|
|
|
201
201
|
/** Real daemons buffer `pty_error` raised by the startup scan until the lifecycle can handle it. */
|
|
202
202
|
holdStartupIncidents?(): void;
|
|
203
203
|
releaseStartupIncidents?(): void;
|
|
204
|
+
/** Reconsider a cooldown-suppressed MCP revival through daemon liveness/idle gates. */
|
|
205
|
+
deferMcpRestart?(delayMs: number): void;
|
|
204
206
|
}
|
|
205
207
|
/** Arguments accepted by handleCreate — mirrors CreateInstanceArgs in outbound-schemas.ts
|
|
206
208
|
* plus internal-only fields forwarded by deploy_template (profile-derived). */
|
|
@@ -696,8 +696,10 @@ export class InstanceLifecycle {
|
|
|
696
696
|
const incident = this.mcpIncident(name);
|
|
697
697
|
const generation = ++incident.gen;
|
|
698
698
|
incident.deathNoticeSent = false;
|
|
699
|
-
this.ctx.eventLog?.insert(name, "mcp_died", { pid: data.pid });
|
|
700
|
-
this.ctx.logger.error({ name, pid: data.pid
|
|
699
|
+
this.ctx.eventLog?.insert(name, "mcp_died", { pid: data.pid, ...(data.neverConnected ? { neverConnected: true } : {}) });
|
|
700
|
+
this.ctx.logger.error({ name, pid: data.pid, neverConnected: data.neverConnected === true }, data.neverConnected
|
|
701
|
+
? "MCP server never connected — instance cannot use agend tools"
|
|
702
|
+
: "MCP server died — instance cannot use agend tools");
|
|
701
703
|
this.ctx.webhookEmit("mcp_died", name, { pid: data.pid });
|
|
702
704
|
// Auth outranks MCP: when the CLI lost authentication, "MCP server died"
|
|
703
705
|
// is a symptom and the fix is /login, not a restart. The daemon flags a
|
|
@@ -804,13 +806,16 @@ export class InstanceLifecycle {
|
|
|
804
806
|
// The daemon object dies with the restart it asks for, so the loop guard
|
|
805
807
|
// lives here: if the previous auto-restart was under the cooldown, the new
|
|
806
808
|
// MCP server evidently died right back (broken install, OOM pressure) and
|
|
807
|
-
// another attempt would just cycle the instance.
|
|
808
|
-
//
|
|
809
|
+
// another immediate attempt would just cycle the instance. Keep a bounded
|
|
810
|
+
// reconsideration on the daemon: a one-shot suppression strands this pid
|
|
811
|
+
// forever because later health ticks dedupe the already reported death.
|
|
809
812
|
const last = this.mcpAutoRestartAt.get(name) ?? 0;
|
|
810
813
|
const now = Date.now();
|
|
811
814
|
if (now - last < InstanceLifecycle.MCP_AUTO_RESTART_COOLDOWN_MS) {
|
|
812
|
-
|
|
813
|
-
this.ctx.
|
|
815
|
+
const retryAfterMs = InstanceLifecycle.MCP_AUTO_RESTART_COOLDOWN_MS - (now - last);
|
|
816
|
+
this.ctx.eventLog?.insert(name, "mcp_auto_restart_suppressed", { trigger: data.trigger, retryAfterMs });
|
|
817
|
+
this.ctx.logger.warn({ name, trigger: data.trigger, retryAfterMs }, "MCP auto-restart deferred — will reconsider after the cooldown with liveness and idle checks");
|
|
818
|
+
daemon.deferMcpRestart?.(retryAfterMs);
|
|
814
819
|
return;
|
|
815
820
|
}
|
|
816
821
|
this.mcpAutoRestartAt.set(name, now);
|