@songsid/agend 2.1.2-beta.8 → 2.1.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/dist/access-path.js +15 -6
- package/dist/access-path.js.map +1 -1
- package/dist/agent-cli-instructions.md +1 -0
- package/dist/agent-cli.js +14 -0
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.d.ts +2 -0
- package/dist/agent-endpoint.js +25 -1
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/backend/antigravity.d.ts +20 -0
- package/dist/backend/antigravity.js +51 -4
- package/dist/backend/antigravity.js.map +1 -1
- package/dist/backend/claude-code.d.ts +59 -0
- package/dist/backend/claude-code.js +67 -1
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/codex.d.ts +26 -0
- package/dist/backend/codex.js +100 -0
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/grok.d.ts +25 -0
- package/dist/backend/grok.js +51 -0
- package/dist/backend/grok.js.map +1 -1
- package/dist/backend/kiro.d.ts +23 -0
- package/dist/backend/kiro.js +120 -2
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/types.d.ts +58 -0
- package/dist/backend/types.js +13 -1
- package/dist/backend/types.js.map +1 -1
- package/dist/channel/adapters/discord.d.ts +15 -0
- package/dist/channel/adapters/discord.js +120 -8
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +21 -0
- package/dist/channel/adapters/telegram.js +86 -0
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/ipc-bridge.d.ts +9 -1
- package/dist/channel/ipc-bridge.js +12 -3
- package/dist/channel/ipc-bridge.js.map +1 -1
- package/dist/channel/ipc-timeouts.d.ts +46 -0
- package/dist/channel/ipc-timeouts.js +65 -0
- package/dist/channel/ipc-timeouts.js.map +1 -0
- package/dist/channel/mcp-server.js +25 -14
- package/dist/channel/mcp-server.js.map +1 -1
- package/dist/channel/mcp-tools.js +30 -1
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/reconnect-backoff.d.ts +17 -0
- package/dist/channel/reconnect-backoff.js +21 -0
- package/dist/channel/reconnect-backoff.js.map +1 -0
- package/dist/channel/types.d.ts +29 -0
- package/dist/classic-channel-manager.js +1 -5
- package/dist/classic-channel-manager.js.map +1 -1
- package/dist/cli.js +242 -91
- package/dist/cli.js.map +1 -1
- package/dist/completion.d.ts +27 -0
- package/dist/completion.js +121 -0
- package/dist/completion.js.map +1 -0
- package/dist/config-validator.js +21 -0
- package/dist/config-validator.js.map +1 -1
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -1
- package/dist/cost-guard.d.ts +3 -1
- package/dist/cost-guard.js +3 -1
- package/dist/cost-guard.js.map +1 -1
- package/dist/daemon.d.ts +299 -7
- package/dist/daemon.js +1152 -311
- package/dist/daemon.js.map +1 -1
- package/dist/event-log.d.ts +31 -0
- package/dist/event-log.js +96 -0
- package/dist/event-log.js.map +1 -1
- package/dist/fleet-context.d.ts +14 -0
- package/dist/fleet-lock.d.ts +28 -0
- package/dist/fleet-lock.js +130 -0
- package/dist/fleet-lock.js.map +1 -0
- package/dist/fleet-manager.d.ts +348 -6
- package/dist/fleet-manager.js +1860 -262
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-instance.d.ts +9 -0
- package/dist/general-instance.js +11 -0
- package/dist/general-instance.js.map +1 -0
- package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +22 -0
- package/dist/general-knowledge/skills/model-discovery/SKILL.md +14 -23
- package/dist/general-knowledge/skills/session-management/SKILL.md +15 -34
- package/dist/hang-detector.d.ts +19 -13
- package/dist/hang-detector.js +19 -49
- package/dist/hang-detector.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +58 -2
- package/dist/instance-lifecycle.js +231 -46
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/locale.js +7 -1
- package/dist/locale.js.map +1 -1
- package/dist/logger.js +14 -0
- package/dist/logger.js.map +1 -1
- package/dist/mcp-liveness.d.ts +21 -0
- package/dist/mcp-liveness.js +27 -0
- package/dist/mcp-liveness.js.map +1 -0
- package/dist/outbound-handlers.d.ts +9 -0
- package/dist/outbound-handlers.js +194 -31
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +11 -3
- package/dist/outbound-schemas.js +16 -2
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/pane-write-lock.d.ts +48 -0
- package/dist/pane-write-lock.js +73 -0
- package/dist/pane-write-lock.js.map +1 -0
- package/dist/process-memory.d.ts +31 -0
- package/dist/process-memory.js +79 -0
- package/dist/process-memory.js.map +1 -0
- package/dist/quickstart.js +17 -16
- package/dist/quickstart.js.map +1 -1
- package/dist/reply-dedup.d.ts +41 -0
- package/dist/reply-dedup.js +0 -0
- package/dist/reply-dedup.js.map +1 -0
- package/dist/restart-progress.d.ts +41 -0
- package/dist/restart-progress.js +97 -0
- package/dist/restart-progress.js.map +1 -0
- package/dist/scheduler/db.js +3 -0
- package/dist/scheduler/db.js.map +1 -1
- package/dist/sd-notify.d.ts +27 -0
- package/dist/sd-notify.js +33 -1
- package/dist/sd-notify.js.map +1 -1
- package/dist/secret-file.d.ts +33 -0
- package/dist/secret-file.js +36 -0
- package/dist/secret-file.js.map +1 -0
- package/dist/service-installer.d.ts +28 -0
- package/dist/service-installer.js +72 -1
- package/dist/service-installer.js.map +1 -1
- package/dist/settings-api.js +5 -3
- package/dist/settings-api.js.map +1 -1
- package/dist/setup-wizard.js +9 -7
- package/dist/setup-wizard.js.map +1 -1
- package/dist/tmux-control.d.ts +58 -5
- package/dist/tmux-control.js +102 -14
- package/dist/tmux-control.js.map +1 -1
- package/dist/tmux-manager.d.ts +31 -1
- package/dist/tmux-manager.js +40 -11
- package/dist/tmux-manager.js.map +1 -1
- package/dist/topic-commands.d.ts +49 -9
- package/dist/topic-commands.js +270 -109
- package/dist/topic-commands.js.map +1 -1
- package/dist/tui-glyphs.d.ts +5 -1
- package/dist/tui-glyphs.js +6 -2
- package/dist/tui-glyphs.js.map +1 -1
- package/dist/types.d.ts +19 -2
- package/dist/ui/settings.html +1 -1
- package/dist/ui/view.html +123 -2
- package/dist/update-marker.d.ts +15 -0
- package/dist/update-marker.js +69 -0
- package/dist/update-marker.js.map +1 -0
- package/dist/usage/format-rich.d.ts +11 -0
- package/dist/usage/format-rich.js +121 -0
- package/dist/usage/format-rich.js.map +1 -0
- package/dist/usage/provider-alerts.d.ts +33 -0
- package/dist/usage/provider-alerts.js +57 -0
- package/dist/usage/provider-alerts.js.map +1 -0
- package/dist/usage/providers.d.ts +130 -0
- package/dist/usage/providers.js +1063 -0
- package/dist/usage/providers.js.map +1 -0
- package/dist/usage/statusline-usage.d.ts +20 -0
- package/dist/usage/statusline-usage.js +120 -0
- package/dist/usage/statusline-usage.js.map +1 -0
- package/dist/usage/usage-api.d.ts +50 -0
- package/dist/usage/usage-api.js +197 -0
- package/dist/usage/usage-api.js.map +1 -0
- package/dist/view-api.js +8 -3
- package/dist/view-api.js.map +1 -1
- package/dist/web-api.js +7 -3
- package/dist/web-api.js.map +1 -1
- package/dist/web-auth.d.ts +6 -0
- package/dist/web-auth.js +54 -0
- package/dist/web-auth.js.map +1 -0
- package/package.json +4 -1
- package/dist/channel/tool-tracker.d.ts +0 -13
- package/dist/channel/tool-tracker.js +0 -58
- package/dist/channel/tool-tracker.js.map +0 -1
- package/dist/daemon-entry.d.ts +0 -1
- package/dist/daemon-entry.js +0 -30
- package/dist/daemon-entry.js.map +0 -1
package/dist/fleet-manager.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { type FleetMemory } from "./process-memory.js";
|
|
2
|
+
import { ReplyDeduper } from "./reply-dedup.js";
|
|
1
3
|
import type { FleetConfig, RawFleetConfig, InstanceConfig } from "./types.js";
|
|
2
4
|
import { type RouteTarget } from "./fleet-context.js";
|
|
3
5
|
import { EventLog } from "./event-log.js";
|
|
@@ -48,7 +50,7 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
48
50
|
private static sighupHandlerInstalled;
|
|
49
51
|
private children;
|
|
50
52
|
readonly lifecycle: InstanceLifecycle;
|
|
51
|
-
/**
|
|
53
|
+
/** Live view of lifecycle.daemons — used throughout; not deprecated. */
|
|
52
54
|
get daemons(): Map<string, import("./daemon.js").Daemon>;
|
|
53
55
|
fleetConfig: FleetConfig | null;
|
|
54
56
|
private rawFleetConfig;
|
|
@@ -61,7 +63,7 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
61
63
|
private instanceWorldBinding;
|
|
62
64
|
private recentMessageIds;
|
|
63
65
|
private accessManager;
|
|
64
|
-
/** Primary world (
|
|
66
|
+
/** Primary world (channels[0]), independent of concurrent adapter startup order. */
|
|
65
67
|
get primaryWorld(): AdapterWorld | undefined;
|
|
66
68
|
readonly routing: RoutingEngine;
|
|
67
69
|
get routingTable(): Map<string, RouteTarget>;
|
|
@@ -98,15 +100,31 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
98
100
|
private instanceIdleWaiters;
|
|
99
101
|
private lastInboundUser;
|
|
100
102
|
private cancelButtons;
|
|
103
|
+
/** Pending idle-edge retirement, one timer per instance. */
|
|
104
|
+
private cancelButtonIdleRetireTimers;
|
|
105
|
+
/** Duplicate-reply suppression across both the MCP and HTTP reply paths. */
|
|
106
|
+
readonly replyDeduper: ReplyDeduper;
|
|
107
|
+
/** instanceName → what it is doing right now, when the backend can tell us. */
|
|
108
|
+
private instanceActivity;
|
|
109
|
+
/** instanceName → tail of deliveries waiting for its IPC to come back. */
|
|
110
|
+
private ipcWaitTails;
|
|
111
|
+
/** instanceName → restart currently executing; concurrent callers join it. */
|
|
112
|
+
private restartsInFlight;
|
|
101
113
|
private lastInboundMsg;
|
|
102
114
|
private topicArchiver;
|
|
103
115
|
controlClient: TmuxControlClient | null;
|
|
104
116
|
classicChannels: ClassicChannelManager | null;
|
|
105
117
|
private pendingClassicStarts;
|
|
106
118
|
/** In-flight /model selections, keyed by nonce (see handleModelSelection). */
|
|
119
|
+
/** In-flight /effort selections, same coordinator shape as pendingModelSelects. */
|
|
120
|
+
private pendingEffortSelects;
|
|
107
121
|
private pendingModelSelects;
|
|
108
122
|
private failoverActive;
|
|
123
|
+
/** instance → when a click with no live button entry last fired a cancel. */
|
|
124
|
+
private staleCancelClickAt;
|
|
109
125
|
readonly ipcStoppingInstances: Set<string>;
|
|
126
|
+
/** Set the moment a graceful stop begins — see isPlannedRestart(). */
|
|
127
|
+
private shuttingDown;
|
|
110
128
|
/** Coalesce concurrent connection attempts for the same daemon socket. */
|
|
111
129
|
private ipcConnectInFlight;
|
|
112
130
|
/** At most one reconnect/backoff loop may exist per instance. */
|
|
@@ -117,6 +135,10 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
117
135
|
private healthServer;
|
|
118
136
|
private healthPortRetried;
|
|
119
137
|
private updateCheckTimer;
|
|
138
|
+
private eventLogPruneTimer;
|
|
139
|
+
private logRotateTimer;
|
|
140
|
+
/** Days of event/activity history to keep. */
|
|
141
|
+
private static readonly EVENT_LOG_RETENTION_DAYS;
|
|
120
142
|
private watchdogTimer;
|
|
121
143
|
private startedAt;
|
|
122
144
|
private mirrorBuffer;
|
|
@@ -124,12 +146,42 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
124
146
|
private sseClients;
|
|
125
147
|
private webToken;
|
|
126
148
|
private viewToken;
|
|
149
|
+
private healthServerListening;
|
|
127
150
|
constructor(dataDir: string);
|
|
128
151
|
private handleSighup;
|
|
129
152
|
private scheduleReconcile;
|
|
153
|
+
/**
|
|
154
|
+
* Is the fleet going down (or coming back up) on purpose?
|
|
155
|
+
*
|
|
156
|
+
* Instances dying during a planned restart is the restart working, not an
|
|
157
|
+
* incident — but the code that notices a dead pane or a dead MCP server
|
|
158
|
+
* cannot tell the difference on its own. Two sources, because the noise
|
|
159
|
+
* starts before this process is even told to stop: `agend update` replaces
|
|
160
|
+
* the package on disk while this daemon is still running and still watching.
|
|
161
|
+
*/
|
|
162
|
+
isPlannedRestart(): boolean;
|
|
130
163
|
private finishStartup;
|
|
131
164
|
lastActivityMs(name: string): number;
|
|
165
|
+
/**
|
|
166
|
+
* Is the instance between turns?
|
|
167
|
+
*
|
|
168
|
+
* Prefers the daemon's pane state machine (debounced, busy-pattern aware) over
|
|
169
|
+
* the control client's raw 2-second output-silence heuristic. The raw heuristic
|
|
170
|
+
* reads every >2s output lull as idle — and long silent tools (a build, a test
|
|
171
|
+
* run) or an LLM pause produce those constantly mid-turn. That misreading is
|
|
172
|
+
* what retired cancel buttons in the middle of long work (the 5-minute backstop
|
|
173
|
+
* fired during a lull) and froze their progress text (ticker skipped "idle"
|
|
174
|
+
* ticks). The silence heuristic remains only as the fallback for instances
|
|
175
|
+
* whose daemon has not reported a state yet.
|
|
176
|
+
*/
|
|
132
177
|
private getInstanceIdle;
|
|
178
|
+
/**
|
|
179
|
+
* True when the instance claims working/stuck but nothing has refreshed that
|
|
180
|
+
* claim for STATE_REPORT_STALE_MS despite the backstop's per-tick queries.
|
|
181
|
+
* Measures the CACHE's age, not the button's — a healthy multi-hour run
|
|
182
|
+
* answers every query and never trips this.
|
|
183
|
+
*/
|
|
184
|
+
private stateReportDead;
|
|
133
185
|
webhookEmit(event: string, name: string, data?: Record<string, unknown>): void;
|
|
134
186
|
getSysInfo(): import("./fleet-context.js").SysInfo;
|
|
135
187
|
/** Load fleet.yaml and build routing table */
|
|
@@ -172,18 +224,30 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
172
224
|
getChannelConfig(adapterId?: string): import("./types.js").ChannelConfig | undefined;
|
|
173
225
|
/** Get the group_id for an instance's bound adapter */
|
|
174
226
|
getGroupIdForInstance(name: string): string;
|
|
227
|
+
/** Configured primary adapter id. Never infer this from Map insertion order. */
|
|
228
|
+
private getPrimaryAdapterId;
|
|
229
|
+
/** Warn when a coordinator's adapter identity is ambiguous to the operator. */
|
|
230
|
+
private warnUnboundGeneralChannelIds;
|
|
231
|
+
/**
|
|
232
|
+
* Resolve the authoritative adapter identity for an instance.
|
|
233
|
+
* Fleet instances without channel_id and legacy Classic entries both belong
|
|
234
|
+
* to channels[0]. Runtime bindings remain available for external sessions.
|
|
235
|
+
*/
|
|
236
|
+
private getInstanceAdapterId;
|
|
175
237
|
/**
|
|
176
238
|
* Bind an instance to a specific world (the bot that answers for it).
|
|
177
239
|
* fromInbound=true (binding inferred from which adapter received a message)
|
|
178
|
-
* must not override a configured identity
|
|
179
|
-
*
|
|
180
|
-
*
|
|
240
|
+
* must not override a configured identity. Fleet instances use channel_id or
|
|
241
|
+
* channels[0]; Classic instances use their persisted adapter (or channels[0]
|
|
242
|
+
* for a legacy entry). Only external sessions may bind from inbound traffic.
|
|
181
243
|
*/
|
|
182
244
|
bindInstanceAdapter(name: string, adapterId: string, fromInbound?: boolean): void;
|
|
183
245
|
getInstanceStatus(name: string): "running" | "paused" | "stopped" | "crashed";
|
|
184
246
|
getInstanceExecutionState(name: string): InstanceState | null;
|
|
185
247
|
isClassicInstance(name: string): boolean;
|
|
186
248
|
private cacheInstanceExecutionState;
|
|
249
|
+
private cancelIdleButtonRetirement;
|
|
250
|
+
private scheduleIdleButtonRetirement;
|
|
187
251
|
private cacheInstanceProcessStatus;
|
|
188
252
|
/**
|
|
189
253
|
* Fleet-wide warm cap: if more than `defaults.warm_cap` instances are running,
|
|
@@ -197,6 +261,25 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
197
261
|
private enforceWarmCap;
|
|
198
262
|
private waitForInstanceIdle;
|
|
199
263
|
private deliverWithIdleGate;
|
|
264
|
+
/**
|
|
265
|
+
* Hand a payload to an instance's IPC, waiting out a *transient* disconnect.
|
|
266
|
+
*
|
|
267
|
+
* A daemon that is restarting — `/restart`, crash recovery, a model switch —
|
|
268
|
+
* drops its socket for a few seconds. Any message arriving in that window used
|
|
269
|
+
* to fail instantly: the caller logged a warning, put ❌ on the user's message,
|
|
270
|
+
* and the message was gone. The user had to notice the ❌ and retype it. That is
|
|
271
|
+
* the "instance 訊息不容易掉" goal failing on the most predictable event there is.
|
|
272
|
+
*
|
|
273
|
+
* The wait is bounded. If the instance is genuinely down, this still throws and
|
|
274
|
+
* the ❌ still appears — just for a real failure rather than a restart.
|
|
275
|
+
*
|
|
276
|
+
* Ordering is preserved by serialising behind any waiter already queued for this
|
|
277
|
+
* instance, *including* when the socket happens to be up: otherwise a message
|
|
278
|
+
* arriving after the reconnect could overtake one that has been waiting for it.
|
|
279
|
+
*/
|
|
280
|
+
private sendWhenConnected;
|
|
281
|
+
/** Poll for the instance's IPC to come back, then send. Throws if it does not. */
|
|
282
|
+
private sendAfterIpcReturns;
|
|
200
283
|
/** Single delivery facade: wake paused CLIs and serialize non-user work behind idle. */
|
|
201
284
|
deliverToInstance(instanceName: string, payload: Record<string, unknown>, options?: DeliveryOptions): Promise<void>;
|
|
202
285
|
/** Fleet admin is an explicit config allowlist entry, not merely an open/paired user. */
|
|
@@ -204,7 +287,13 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
204
287
|
changeInstancePauseState(name: string, action: "pause" | "wake"): Promise<"paused" | "awake" | "not_idle">;
|
|
205
288
|
/** Apply a Settings edit to a ClassicBot channel without waiting for the poller. */
|
|
206
289
|
restartClassicInstanceFromSettings(instanceName: string): Promise<void>;
|
|
207
|
-
startInstance(name: string, config: InstanceConfig, topicMode: boolean, kind?: "fleet-topic" | "classic"
|
|
290
|
+
startInstance(name: string, config: InstanceConfig, topicMode: boolean, kind?: "fleet-topic" | "classic",
|
|
291
|
+
/**
|
|
292
|
+
* Explicit starts (CLI/API) may resume a paused or failed daemon. Startup
|
|
293
|
+
* and reconcile calls leave this false so a persisted pause remains paused
|
|
294
|
+
* across a fleet restart.
|
|
295
|
+
*/
|
|
296
|
+
resumePaused?: boolean): Promise<void>;
|
|
208
297
|
/** Recreate a daemon for a marker-only paused instance after an explicit wake/delivery. */
|
|
209
298
|
startPersistedPausedInstance(name: string): Promise<void>;
|
|
210
299
|
/**
|
|
@@ -214,11 +303,15 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
214
303
|
* TODO: per-instance startup timeout (existing issue, not introduced here)
|
|
215
304
|
*/
|
|
216
305
|
private startInstancesWithConcurrency;
|
|
306
|
+
private runnableStartupCount;
|
|
307
|
+
private configuredStartupInstanceNames;
|
|
308
|
+
private restartProgressTarget;
|
|
217
309
|
stopInstance(name: string): Promise<void>;
|
|
218
310
|
/** Restart a single instance, reloading fleet.yaml first to pick up config changes. */
|
|
219
311
|
restartSingleInstance(name: string, opts?: {
|
|
220
312
|
freshStart?: boolean;
|
|
221
313
|
}): Promise<void>;
|
|
314
|
+
private doRestartSingleInstance;
|
|
222
315
|
/**
|
|
223
316
|
* Mark an instance so its next daemon start skips session resume. Written AFTER
|
|
224
317
|
* stop (survives the old daemon's cleanup) and BEFORE start so the respawn reads
|
|
@@ -228,6 +321,12 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
228
321
|
private writeFreshStartMarker;
|
|
229
322
|
/** Load .env file from data dir into process.env */
|
|
230
323
|
private loadEnvFile;
|
|
324
|
+
/** Initialize auth before any adapter can answer /dashboard. */
|
|
325
|
+
private initializeWebAuthTokens;
|
|
326
|
+
getDashboardAccess(): {
|
|
327
|
+
ready: boolean;
|
|
328
|
+
token: string | null;
|
|
329
|
+
};
|
|
231
330
|
/** Start all instances from fleet config */
|
|
232
331
|
startAll(configPath: string): Promise<void>;
|
|
233
332
|
/**
|
|
@@ -251,6 +350,42 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
251
350
|
retryCount: number;
|
|
252
351
|
lastError?: string;
|
|
253
352
|
}>;
|
|
353
|
+
/**
|
|
354
|
+
* Real, checkable fleet health for `/health` and the operator.
|
|
355
|
+
*
|
|
356
|
+
* `status` is:
|
|
357
|
+
* - `ok` — at least one adapter connected and every configured instance
|
|
358
|
+
* that should be running is running
|
|
359
|
+
* - `degraded` — reachable, but something the operator should look at (an
|
|
360
|
+
* adapter retrying, an instance crashed or stopped)
|
|
361
|
+
* - `down` — the fleet cannot do its job: no adapter is connected, so no
|
|
362
|
+
* message can arrive or be answered
|
|
363
|
+
*
|
|
364
|
+
* Deliberately does NOT gate the systemd watchdog — see the comment at the
|
|
365
|
+
* WATCHDOG timer for why.
|
|
366
|
+
*/
|
|
367
|
+
getFleetHealth(): {
|
|
368
|
+
status: "ok" | "degraded" | "down";
|
|
369
|
+
uptime: number;
|
|
370
|
+
instances: {
|
|
371
|
+
configured: number;
|
|
372
|
+
running: number;
|
|
373
|
+
crashed: number;
|
|
374
|
+
paused: number;
|
|
375
|
+
stopped: number;
|
|
376
|
+
};
|
|
377
|
+
adapters: {
|
|
378
|
+
total: number;
|
|
379
|
+
connected: number;
|
|
380
|
+
states: Record<string, string>;
|
|
381
|
+
};
|
|
382
|
+
startupComplete: boolean;
|
|
383
|
+
/** See process-memory.ts: the fleet process and the whole service cgroup are
|
|
384
|
+
* reported separately because they differ by ~60x and only one of them can
|
|
385
|
+
* show a fleet-manager leak. */
|
|
386
|
+
memory: FleetMemory;
|
|
387
|
+
problems: string[];
|
|
388
|
+
};
|
|
254
389
|
/** Start the primary adapter (backward-compatible, sets this.adapter) */
|
|
255
390
|
private startSingleAdapter;
|
|
256
391
|
/** Start an additional (non-primary) adapter */
|
|
@@ -265,6 +400,23 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
265
400
|
private restartAdapter;
|
|
266
401
|
/** Handle inbound message — transcribe voice if present, then route */
|
|
267
402
|
private findGeneralInstance;
|
|
403
|
+
/**
|
|
404
|
+
* A user reacted to one of the bot's messages (#408).
|
|
405
|
+
*
|
|
406
|
+
* A reaction is context, not a message (#432, reworking #413): it never triggers
|
|
407
|
+
* an agent turn and never wakes anything. It is queued in the event log and rides
|
|
408
|
+
* into the instance's NEXT real message as one compact leading line —
|
|
409
|
+
* `[Recent reactions: 👍×2 from hanhanv]` — after which it is marked consumed.
|
|
410
|
+
* No pending reactions → no line → zero context spent, which is the common case.
|
|
411
|
+
*/
|
|
412
|
+
private handleInboundReaction;
|
|
413
|
+
/**
|
|
414
|
+
* The queued-reaction summary for an instance's next real message, or {} when
|
|
415
|
+
* nothing is pending (the common case must add zero context). The consume
|
|
416
|
+
* callback is separate from the fetch so reactions are only marked once the
|
|
417
|
+
* message actually went out — a failed delivery keeps them queued.
|
|
418
|
+
*/
|
|
419
|
+
private pendingReactionsMeta;
|
|
268
420
|
private handleInboundMessage;
|
|
269
421
|
/** Handle outbound tool calls from a daemon instance */
|
|
270
422
|
/** Warn (but don't block) when rate limits are high. 30-min debounce per instance. */
|
|
@@ -272,6 +424,8 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
272
424
|
private warnIfRateLimited;
|
|
273
425
|
/** Handle outbound tool calls from a daemon instance */
|
|
274
426
|
private handleOutboundFromInstance;
|
|
427
|
+
/** Side effects of a routed reply: cancel-button lifecycle, logs, SSE, chat log. */
|
|
428
|
+
private afterReplyRouted;
|
|
275
429
|
/** Handle tool status update from a daemon instance */
|
|
276
430
|
private handleToolStatusFromInstance;
|
|
277
431
|
private handleScheduleTrigger;
|
|
@@ -313,6 +467,68 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
313
467
|
private static FAILOVER_RECOVER_PCT;
|
|
314
468
|
checkModelFailover(name: string, fiveHourPct: number): void;
|
|
315
469
|
toggleFleetCollab(instanceName: string): boolean;
|
|
470
|
+
/**
|
|
471
|
+
* Open the event log, tolerating a corrupt file.
|
|
472
|
+
*
|
|
473
|
+
* `events.db` holds history only — event rows and the activity feed. Nothing the
|
|
474
|
+
* fleet needs to run depends on it, and every consumer already uses
|
|
475
|
+
* `this.eventLog?.`. An unguarded `new EventLog(...)` here meant a corrupt or
|
|
476
|
+
* unreadable history file (a truncated WAL after a hard kill, a full disk)
|
|
477
|
+
* threw during startAll and the WHOLE FLEET FAILED TO BOOT — trading every
|
|
478
|
+
* running agent for a file whose only job is reporting.
|
|
479
|
+
*
|
|
480
|
+
* So: try, move a bad file aside and retry once with a fresh one, and if even
|
|
481
|
+
* that fails carry on without an event log.
|
|
482
|
+
*/
|
|
483
|
+
/**
|
|
484
|
+
* Run `agend backend doctor` for the fleet's default backend and return its
|
|
485
|
+
* cleaned output.
|
|
486
|
+
*
|
|
487
|
+
* Async on purpose: this was `execSync` with a 30s timeout, reachable by any
|
|
488
|
+
* allowlisted user through `/doctor`. While it ran, the entire fleet event loop
|
|
489
|
+
* was frozen — no IPC, no adapter, no message delivery, no health responses,
|
|
490
|
+
* and critically no WATCHDOG ping, so a slow doctor could push past
|
|
491
|
+
* WatchdogSec and have systemd SIGABRT the fleet.
|
|
492
|
+
*/
|
|
493
|
+
private runBackendDoctor;
|
|
494
|
+
/** Drop event/activity rows older than the retention window. Best-effort. */
|
|
495
|
+
/**
|
|
496
|
+
* Cap every instance's pipe-pane log, walking the instances **directory** rather
|
|
497
|
+
* than the config.
|
|
498
|
+
*
|
|
499
|
+
* A running instance rotates its own log on each health tick, so the ones that
|
|
500
|
+
* need this are the ones nothing else looks at:
|
|
501
|
+
*
|
|
502
|
+
* - deleted instances, whose directory outlives the config entry. Nothing ever
|
|
503
|
+
* touched these again. On the machine this was found on, one held 122 MB and
|
|
504
|
+
* another 74 MB, out of 622 MB of pipe-pane logs in total.
|
|
505
|
+
* - classic instances, which live in classicChannels, not fleetConfig.instances,
|
|
506
|
+
* and so were never in the old config-driven loop at all.
|
|
507
|
+
* - stopped instances, which have no health tick running.
|
|
508
|
+
*
|
|
509
|
+
* pipe-pane writes raw TUI output, so a wedged splash screen can emit ANSI frames
|
|
510
|
+
* at animation rate. Unbounded growth here fills the disk, which takes the whole
|
|
511
|
+
* fleet down rather than one instance.
|
|
512
|
+
*/
|
|
513
|
+
private rotateAllInstanceLogs;
|
|
514
|
+
private pruneEventLog;
|
|
515
|
+
private openEventLog;
|
|
516
|
+
/**
|
|
517
|
+
* Report a fleet-level fault (not attributable to one instance) to the General
|
|
518
|
+
* topic, so the operator learns about it without reading daemon.log.
|
|
519
|
+
*
|
|
520
|
+
* Throttled per distinct message: an unhandled rejection typically comes from a
|
|
521
|
+
* loop (a poller, a repeating timer), and one channel message per occurrence
|
|
522
|
+
* would bury the topic — which is worse than silence. First occurrence goes out
|
|
523
|
+
* immediately, repeats are suppressed for THROTTLE_MS and then re-sent with a
|
|
524
|
+
* count.
|
|
525
|
+
*
|
|
526
|
+
* The log line is written by the caller regardless: if every adapter is down,
|
|
527
|
+
* the only notification path is the one that is broken.
|
|
528
|
+
*/
|
|
529
|
+
notifyFleetError(text: string): void;
|
|
530
|
+
private static readonly FLEET_ERROR_THROTTLE_MS;
|
|
531
|
+
private fleetErrorNotices;
|
|
316
532
|
notifyInstanceTopic(instanceName: string, text: string, extraOpts?: import("./channel/types.js").SendOpts): void;
|
|
317
533
|
/** Send a "🛑 Cancel" button to the instance's topic/channel after delivery. */
|
|
318
534
|
/**
|
|
@@ -322,8 +538,66 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
322
538
|
*/
|
|
323
539
|
private handleSlashSave;
|
|
324
540
|
/** Whether the instance currently has at least one live cancel button. */
|
|
541
|
+
/**
|
|
542
|
+
* A click on a cancel button, whether or not the fleet still tracks it.
|
|
543
|
+
*
|
|
544
|
+
* The old rule was "act only while an entry is live", which made a click on a
|
|
545
|
+
* button the fleet had forgotten a silent no-op — no cancel, no message, not
|
|
546
|
+
* even a log line. That is indistinguishable from a broken button, and it is
|
|
547
|
+
* what the "按鈕點了沒反應" reports were: the entry is briefly absent while a
|
|
548
|
+
* button is being replaced, and a delete that fails leaves the message on
|
|
549
|
+
* screen with no entry at all.
|
|
550
|
+
*
|
|
551
|
+
* So: honour the click if the instance is actually running, and say so plainly
|
|
552
|
+
* if it is not. The stale-click path is rate-limited because the original
|
|
553
|
+
* concern was real — a second click must not fire a second interrupt key at an
|
|
554
|
+
* instance that has already started a new turn.
|
|
555
|
+
*/
|
|
556
|
+
private handleCancelClick;
|
|
325
557
|
private hasCancelButton;
|
|
326
558
|
sendCancelButton(instanceName: string, correlationId?: string): Promise<void>;
|
|
559
|
+
/**
|
|
560
|
+
* The cancel button's text for a given elapsed time.
|
|
561
|
+
*
|
|
562
|
+
* Below the threshold it keeps the original wording, so a normal quick answer
|
|
563
|
+
* looks exactly as it did before. Past it, the button doubles as the live
|
|
564
|
+
* progress indicator (#409) — the channel showed nothing at all during long work,
|
|
565
|
+
* and once the agent had replied once there was no sign it was still going.
|
|
566
|
+
*/
|
|
567
|
+
static progressText(elapsedMs: number, activity?: string | null, minElapsedMs?: number): string;
|
|
568
|
+
/**
|
|
569
|
+
* Make a tool summary safe to paste into a channel message.
|
|
570
|
+
*
|
|
571
|
+
* The text is agent-controlled (it is built from tool inputs — file paths,
|
|
572
|
+
* shell commands), so it gets flattened to one line, capped, and stripped of
|
|
573
|
+
* the two Discord mass-mention triggers. Neither channel renders it with a
|
|
574
|
+
* parse mode, so no markup escaping is needed beyond that.
|
|
575
|
+
*/
|
|
576
|
+
private static sanitizeActivity;
|
|
577
|
+
/**
|
|
578
|
+
* Remember what an instance is currently doing, for the progress line.
|
|
579
|
+
*
|
|
580
|
+
* Best-effort by design: only backends that expose a live activity feed report
|
|
581
|
+
* anything, and the progress line simply omits the detail for the rest. It is
|
|
582
|
+
* never used to decide anything — purely what the user is shown.
|
|
583
|
+
*/
|
|
584
|
+
private cacheInstanceActivity;
|
|
585
|
+
/**
|
|
586
|
+
* Refresh the button's text in place while the instance keeps working.
|
|
587
|
+
*
|
|
588
|
+
* Uses `editAlert`, NOT `editMessage`: on Telegram the latter omits reply_markup,
|
|
589
|
+
* and the Bot API treats that as "clear the keyboard" — so editing with it would
|
|
590
|
+
* delete the very cancel button this is trying to keep alive.
|
|
591
|
+
*/
|
|
592
|
+
/** Configured threshold before elapsed time appears, in ms. */
|
|
593
|
+
progressMinElapsedMs(): number;
|
|
594
|
+
private startProgressTicker;
|
|
595
|
+
/**
|
|
596
|
+
* After a reply: give the instance REPLY_RETIRE_GRACE_MS to resume working; if
|
|
597
|
+
* it has not, retire its button. Re-arming replaces the previous timer, so a
|
|
598
|
+
* burst of replies ends with exactly one pending check.
|
|
599
|
+
*/
|
|
600
|
+
private armReplyGrace;
|
|
327
601
|
/** Retire (delete) every cancel button belonging to an instance. */
|
|
328
602
|
private retireInstanceButtons;
|
|
329
603
|
/** Begin retiring one button (delete + bounded retry on failure). Idempotent:
|
|
@@ -333,6 +607,19 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
333
607
|
private attemptButtonDelete;
|
|
334
608
|
/** Clear an entry's timers (retry + idle-check) and drop it from the map. */
|
|
335
609
|
private discardButton;
|
|
610
|
+
/**
|
|
611
|
+
* Mirror the live buttons to disk. The map is memory-only, so before this a
|
|
612
|
+
* fleet restart orphaned every button on screen: frozen "處理中…" text and a
|
|
613
|
+
* click that did nothing, forever. The ledger is tiny (a handful of rows) and
|
|
614
|
+
* written on every add/remove — no debounce needed at that rate.
|
|
615
|
+
*/
|
|
616
|
+
private persistCancelButtons;
|
|
617
|
+
/**
|
|
618
|
+
* Delete the previous process's buttons. Runs once adapters are up: nothing
|
|
619
|
+
* from a previous fleet process can still be mid-turn from this process's
|
|
620
|
+
* point of view, so every ledger row is an orphan by definition.
|
|
621
|
+
*/
|
|
622
|
+
private sweepOrphanedCancelButtons;
|
|
336
623
|
/** Re-attempt a failed button delete up to CANCEL_BTN_MAX_RETRIES times. */
|
|
337
624
|
private scheduleButtonRetry;
|
|
338
625
|
/** Delete one button's message via its own adapter. Resolves on success,
|
|
@@ -416,6 +703,15 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
416
703
|
/** Best-effort model list for `/model`: cached CLI env first, else live probe. Never throws. */
|
|
417
704
|
private getModelOptions;
|
|
418
705
|
/** `/model` slash handler (admin only). No arg → DC menu; `/model <name>` → apply directly. */
|
|
706
|
+
/** Label an effort choice, marking the one currently configured. */
|
|
707
|
+
private effortChoiceLabel;
|
|
708
|
+
private effortMenuHeader;
|
|
709
|
+
/** `/effort` — DC Select Menu, or apply directly when a level is given. */
|
|
710
|
+
private handleEffortSlash;
|
|
711
|
+
/** TG inline-keyboard effort menu. Returns null on success, else a fallback string. */
|
|
712
|
+
promptEffortMenu(instanceName: string, userId: string, channelId: string, adapter: ChannelAdapter, chatId: string, threadId?: string): Promise<string | null>;
|
|
713
|
+
/** Consume an `/effort` selection callback. Mirrors handleModelSelection. */
|
|
714
|
+
private handleEffortSelection;
|
|
419
715
|
private handleModelSlash;
|
|
420
716
|
/**
|
|
421
717
|
* Show a TG inline-keyboard model-selection menu. Reuses the same
|
|
@@ -426,7 +722,43 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
426
722
|
/** Consume a `/model` selection callback. Returns true for all model-select ids (incl. stale). */
|
|
427
723
|
private handleModelSelection;
|
|
428
724
|
/** Apply a model to an instance: runtime paste (claude-code) or persist + restart (others). */
|
|
725
|
+
/** AgEnD's canonical effort ladder, low → max. Backends expose a subset. */
|
|
726
|
+
static readonly EFFORT_LEVELS: readonly ["low", "medium", "high", "xhigh", "max"];
|
|
727
|
+
/** How this instance's backend applies an effort change. */
|
|
728
|
+
effortStrategyFor(instanceName: string): "runtime" | "restart" | "unsupported";
|
|
729
|
+
/** Effort levels this instance's backend actually accepts (empty = unsupported). */
|
|
730
|
+
effortLevelsFor(instanceName: string): string[];
|
|
731
|
+
/** Configured effort for an instance: per-instance, else fleet default, else none. */
|
|
732
|
+
resolveInstanceEffort(instanceName: string): {
|
|
733
|
+
effort: string | null;
|
|
734
|
+
source: "instance" | "fleet-default" | "unset";
|
|
735
|
+
};
|
|
736
|
+
/**
|
|
737
|
+
* Clamp a canonical level to the nearest one this backend supports.
|
|
738
|
+
*
|
|
739
|
+
* Clamping DOWN the ladder, never up: asking for `max` on a CLI that stops at
|
|
740
|
+
* `high` should get high, not silently fall to low. The caller reports the
|
|
741
|
+
* clamp — a user who asks for max and quietly receives high has been told the
|
|
742
|
+
* request succeeded when it did not.
|
|
743
|
+
*/
|
|
744
|
+
static clampEffort(level: string, supported: string[]): string | null;
|
|
745
|
+
/**
|
|
746
|
+
* Apply a reasoning-effort level, mirroring applyModel's shape.
|
|
747
|
+
*
|
|
748
|
+
* runtime backends take `/effort <level>` in the pane and keep working;
|
|
749
|
+
* restart backends only read it at launch, so it is persisted and the
|
|
750
|
+
* instance respawns.
|
|
751
|
+
*/
|
|
752
|
+
applyEffort(instanceName: string, requested: string): Promise<string>;
|
|
429
753
|
applyModel(instanceName: string, model: string): Promise<string>;
|
|
754
|
+
/**
|
|
755
|
+
* The trailing "Current effort: …" line for a /model reply.
|
|
756
|
+
*
|
|
757
|
+
* Model and effort interact (a cheaper model at max effort is a different
|
|
758
|
+
* trade than a bigger one at low), so showing the effort in force right after
|
|
759
|
+
* a switch saves the round trip of asking. Empty when the backend has none.
|
|
760
|
+
*/
|
|
761
|
+
private effortSuffix;
|
|
430
762
|
/** Read recent chat log for agent context */
|
|
431
763
|
private getRecentChatLog;
|
|
432
764
|
/** Return a user-facing blocker without mutating ClassicBot state. */
|
|
@@ -447,7 +779,17 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
447
779
|
handleClassicStart(channelId: string, channelName: string, userId: string, guildId?: string, adapterId?: string, backend?: string): Promise<string>;
|
|
448
780
|
/** Handle /stop slash command — unregister classic channel */
|
|
449
781
|
handleClassicStop(channelId: string, adapterId?: string): Promise<string>;
|
|
782
|
+
/**
|
|
783
|
+
* Idempotent while in flight: SIGINT and SIGTERM share one handler and the
|
|
784
|
+
* uncaughtException path calls this too, so overlapping runs were possible —
|
|
785
|
+
* each snapshotting the daemon map and calling stop() on the same daemons
|
|
786
|
+
* concurrently. Deliberately NOT `async`, so callers receive the same promise
|
|
787
|
+
* object rather than a fresh wrapper around it. The latch clears when the run
|
|
788
|
+
* settles, so a later genuine stop (after a restart) still does the work.
|
|
789
|
+
*/
|
|
450
790
|
stopAll(): Promise<void>;
|
|
791
|
+
private stopAllInFlight;
|
|
792
|
+
private doStopAll;
|
|
451
793
|
/**
|
|
452
794
|
* Prune stale external sessions by re-querying each daemon for live sessions.
|
|
453
795
|
* Sessions in the registry that are no longer reported by any daemon are removed.
|