@songsid/agend 2.1.4-beta.5 → 2.1.4-beta.50

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 (175) hide show
  1. package/README.md +1 -1
  2. package/README.zh-TW.md +1 -1
  3. package/dist/agent-endpoint.js +1 -1
  4. package/dist/agent-endpoint.js.map +1 -1
  5. package/dist/backend/antigravity.d.ts +12 -8
  6. package/dist/backend/antigravity.js +189 -42
  7. package/dist/backend/antigravity.js.map +1 -1
  8. package/dist/backend/claude-code.d.ts +62 -23
  9. package/dist/backend/claude-code.js +296 -43
  10. package/dist/backend/claude-code.js.map +1 -1
  11. package/dist/backend/codex.d.ts +18 -0
  12. package/dist/backend/codex.js +148 -8
  13. package/dist/backend/codex.js.map +1 -1
  14. package/dist/backend/gemini-cli.js +2 -2
  15. package/dist/backend/gemini-cli.js.map +1 -1
  16. package/dist/backend/grok.js +2 -2
  17. package/dist/backend/grok.js.map +1 -1
  18. package/dist/backend/kiro.d.ts +52 -2
  19. package/dist/backend/kiro.js +267 -12
  20. package/dist/backend/kiro.js.map +1 -1
  21. package/dist/backend/opencode.d.ts +27 -11
  22. package/dist/backend/opencode.js +65 -41
  23. package/dist/backend/opencode.js.map +1 -1
  24. package/dist/backend/types.d.ts +97 -3
  25. package/dist/backend/types.js +12 -1
  26. package/dist/backend/types.js.map +1 -1
  27. package/dist/backend-outage.d.ts +61 -0
  28. package/dist/backend-outage.js +71 -0
  29. package/dist/backend-outage.js.map +1 -0
  30. package/dist/channel/adapters/discord.d.ts +44 -2
  31. package/dist/channel/adapters/discord.js +526 -78
  32. package/dist/channel/adapters/discord.js.map +1 -1
  33. package/dist/channel/adapters/telegram.d.ts +30 -0
  34. package/dist/channel/adapters/telegram.js +221 -19
  35. package/dist/channel/adapters/telegram.js.map +1 -1
  36. package/dist/channel/agy-mcp-launcher.d.ts +2 -0
  37. package/dist/channel/agy-mcp-launcher.js +28 -0
  38. package/dist/channel/agy-mcp-launcher.js.map +1 -0
  39. package/dist/channel/ipc-bridge.d.ts +2 -1
  40. package/dist/channel/ipc-bridge.js +33 -4
  41. package/dist/channel/ipc-bridge.js.map +1 -1
  42. package/dist/channel/mcp-server.js +28 -25
  43. package/dist/channel/mcp-server.js.map +1 -1
  44. package/dist/channel/mcp-tools.js +4 -2
  45. package/dist/channel/mcp-tools.js.map +1 -1
  46. package/dist/channel/message-queue.js +62 -1
  47. package/dist/channel/message-queue.js.map +1 -1
  48. package/dist/channel/types.d.ts +31 -1
  49. package/dist/classic-channel-manager.d.ts +61 -3
  50. package/dist/classic-channel-manager.js +256 -31
  51. package/dist/classic-channel-manager.js.map +1 -1
  52. package/dist/cli.js +109 -37
  53. package/dist/cli.js.map +1 -1
  54. package/dist/config-validator.js +48 -7
  55. package/dist/config-validator.js.map +1 -1
  56. package/dist/config.d.ts +4 -0
  57. package/dist/config.js +12 -1
  58. package/dist/config.js.map +1 -1
  59. package/dist/cross-instance-envelope.d.ts +6 -0
  60. package/dist/cross-instance-envelope.js +30 -0
  61. package/dist/cross-instance-envelope.js.map +1 -0
  62. package/dist/daemon.d.ts +429 -9
  63. package/dist/daemon.js +1843 -289
  64. package/dist/daemon.js.map +1 -1
  65. package/dist/doctor.d.ts +41 -0
  66. package/dist/doctor.js +267 -0
  67. package/dist/doctor.js.map +1 -0
  68. package/dist/fleet-context.d.ts +54 -0
  69. package/dist/fleet-manager.d.ts +398 -11
  70. package/dist/fleet-manager.js +2756 -499
  71. package/dist/fleet-manager.js.map +1 -1
  72. package/dist/fleet-yaml-slim.d.ts +10 -0
  73. package/dist/fleet-yaml-slim.js +59 -0
  74. package/dist/fleet-yaml-slim.js.map +1 -0
  75. package/dist/general-knowledge/skills/backend-providers/SKILL.md +114 -0
  76. package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +3 -1
  77. package/dist/general-knowledge/skills/delegation-playbook/SKILL.md +52 -0
  78. package/dist/general-knowledge/skills/development-workflow/SKILL.md +28 -0
  79. package/dist/general-knowledge/skills/fleet-config/SKILL.md +1 -0
  80. package/dist/general-knowledge/skills/fleet-health/SKILL.md +1 -0
  81. package/dist/general-knowledge/skills/fleet-restart/SKILL.md +1 -0
  82. package/dist/general-knowledge/skills/instance-lifecycle/SKILL.md +1 -0
  83. package/dist/general-knowledge/skills/model-discovery/SKILL.md +64 -16
  84. package/dist/general-knowledge/skills/multi-channel/SKILL.md +1 -0
  85. package/dist/general-knowledge/skills/scheduling/SKILL.md +1 -0
  86. package/dist/general-knowledge/skills/session-management/SKILL.md +172 -8
  87. package/dist/general-knowledge/skills/tui-effort/SKILL.md +1 -0
  88. package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +30 -0
  89. package/dist/instance-lifecycle.d.ts +99 -0
  90. package/dist/instance-lifecycle.js +339 -11
  91. package/dist/instance-lifecycle.js.map +1 -1
  92. package/dist/instructions.d.ts +12 -0
  93. package/dist/instructions.js +35 -1
  94. package/dist/instructions.js.map +1 -1
  95. package/dist/locale.js +1004 -288
  96. package/dist/locale.js.map +1 -1
  97. package/dist/login-flows.d.ts +91 -0
  98. package/dist/login-flows.js +170 -0
  99. package/dist/login-flows.js.map +1 -0
  100. package/dist/login-manager.d.ts +63 -0
  101. package/dist/login-manager.js +134 -0
  102. package/dist/login-manager.js.map +1 -0
  103. package/dist/network-family.d.ts +18 -0
  104. package/dist/network-family.js +20 -0
  105. package/dist/network-family.js.map +1 -0
  106. package/dist/outbound-handlers.d.ts +8 -0
  107. package/dist/outbound-handlers.js +101 -18
  108. package/dist/outbound-handlers.js.map +1 -1
  109. package/dist/outbound-schemas.d.ts +7 -1
  110. package/dist/outbound-schemas.js +9 -0
  111. package/dist/outbound-schemas.js.map +1 -1
  112. package/dist/pane-input-residue.d.ts +52 -0
  113. package/dist/pane-input-residue.js +107 -0
  114. package/dist/pane-input-residue.js.map +1 -0
  115. package/dist/reply-dedup.d.ts +7 -8
  116. package/dist/reply-dedup.js +0 -0
  117. package/dist/reply-dedup.js.map +1 -1
  118. package/dist/restart-progress.d.ts +2 -0
  119. package/dist/restart-progress.js +3 -0
  120. package/dist/restart-progress.js.map +1 -1
  121. package/dist/scheduler/db.d.ts +12 -0
  122. package/dist/scheduler/db.js +59 -0
  123. package/dist/scheduler/db.js.map +1 -1
  124. package/dist/service-installer.d.ts +11 -0
  125. package/dist/service-installer.js +84 -18
  126. package/dist/service-installer.js.map +1 -1
  127. package/dist/settings-api.js +1 -1
  128. package/dist/settings-api.js.map +1 -1
  129. package/dist/setup-wizard.js +2 -2
  130. package/dist/setup-wizard.js.map +1 -1
  131. package/dist/spawn-gate.d.ts +30 -0
  132. package/dist/spawn-gate.js +79 -0
  133. package/dist/spawn-gate.js.map +1 -0
  134. package/dist/storm-window.d.ts +83 -0
  135. package/dist/storm-window.js +251 -0
  136. package/dist/storm-window.js.map +1 -0
  137. package/dist/tips.d.ts +44 -0
  138. package/dist/tips.js +355 -0
  139. package/dist/tips.js.map +1 -0
  140. package/dist/tmux-manager.d.ts +16 -0
  141. package/dist/tmux-manager.js +110 -25
  142. package/dist/tmux-manager.js.map +1 -1
  143. package/dist/tool-progress.d.ts +40 -0
  144. package/dist/tool-progress.js +289 -0
  145. package/dist/tool-progress.js.map +1 -0
  146. package/dist/topic-commands.d.ts +42 -4
  147. package/dist/topic-commands.js +372 -67
  148. package/dist/topic-commands.js.map +1 -1
  149. package/dist/transcript-monitor.d.ts +15 -2
  150. package/dist/transcript-monitor.js +63 -17
  151. package/dist/transcript-monitor.js.map +1 -1
  152. package/dist/transcript-sources.d.ts +131 -0
  153. package/dist/transcript-sources.js +580 -0
  154. package/dist/transcript-sources.js.map +1 -0
  155. package/dist/types.d.ts +13 -0
  156. package/dist/ui/dashboard.html +55 -32
  157. package/dist/ui/settings.html +200 -60
  158. package/dist/ui/view.html +147 -30
  159. package/dist/usage/format-rich.d.ts +1 -1
  160. package/dist/usage/format-rich.js +28 -24
  161. package/dist/usage/format-rich.js.map +1 -1
  162. package/dist/usage/i18n-keys.d.ts +7 -0
  163. package/dist/usage/i18n-keys.js +34 -0
  164. package/dist/usage/i18n-keys.js.map +1 -0
  165. package/dist/usage/i18n.d.ts +5 -0
  166. package/dist/usage/i18n.js +27 -0
  167. package/dist/usage/i18n.js.map +1 -0
  168. package/dist/usage/providers.d.ts +21 -0
  169. package/dist/usage/providers.js +153 -75
  170. package/dist/usage/providers.js.map +1 -1
  171. package/dist/usage/usage-api.d.ts +11 -3
  172. package/dist/usage/usage-api.js +61 -24
  173. package/dist/usage/usage-api.js.map +1 -1
  174. package/dist/workflow-templates/default.md +2 -1
  175. package/package.json +2 -2
@@ -6,7 +6,7 @@ import { EventLog } from "./event-log.js";
6
6
  import { AdapterWorld } from "./adapter-world.js";
7
7
  import { CostGuard } from "./cost-guard.js";
8
8
  import { IpcClient } from "./channel/ipc-bridge.js";
9
- import type { ChannelAdapter } from "./channel/types.js";
9
+ import type { AdapterHealthSnapshot, ChannelAdapter } from "./channel/types.js";
10
10
  import { type Logger } from "./logger.js";
11
11
  import { Scheduler } from "./scheduler/index.js";
12
12
  import type { FleetContext } from "./fleet-context.js";
@@ -20,6 +20,9 @@ import { type RawConfigPatch } from "./settings-api.js";
20
20
  import { type AgentEndpointContext } from "./agent-endpoint.js";
21
21
  import { ClassicChannelManager } from "./classic-channel-manager.js";
22
22
  import type { InstanceState } from "./backend/types.js";
23
+ import { StormWindow } from "./storm-window.js";
24
+ import { SpawnGate } from "./spawn-gate.js";
25
+ import { BackendOutageTracker } from "./backend-outage.js";
23
26
  export declare function resolveReplyThreadId(argsThreadId: unknown, instanceConfig?: InstanceConfig): string | undefined;
24
27
  /**
25
28
  * Pure warm-cap victim selection (extracted for testability). Given the current
@@ -36,6 +39,25 @@ export declare function selectLruEvictions(warm: string[], cap: number, opts: {
36
39
  isIdle: (name: string) => boolean;
37
40
  lastInboundAt: (name: string) => number;
38
41
  }): string[];
42
+ /**
43
+ * Answer shape for `list_models`. `scope` reports where the LIST came from —
44
+ * "instance" only when it was read through that instance's own backend config,
45
+ * "global" for the account/CLI catalog — so a caller can tell an authoritative
46
+ * per-instance list from a best-effort account-wide one.
47
+ */
48
+ export interface ModelCatalog {
49
+ backend: string;
50
+ scope: "instance" | "global";
51
+ /** Set whenever an instance was asked about, even if the list is global. */
52
+ instance?: string;
53
+ current_model: string | null;
54
+ models: import("./backend/types.js").ModelOption[];
55
+ /** "cache" = startup probe cache, "live" = probed now, "fallback" = none available. */
56
+ source: "cache" | "live" | "fallback";
57
+ probed_at?: string;
58
+ /** Caveat the caller should read before trusting the list. */
59
+ note?: string;
60
+ }
39
61
  export interface DeliveryOptions {
40
62
  /** Explicitly identify agent-to-agent delivery when metadata is unavailable. */
41
63
  isCrossInstance?: boolean;
@@ -50,6 +72,10 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
50
72
  private static sighupHandlerInstalled;
51
73
  private children;
52
74
  readonly lifecycle: InstanceLifecycle;
75
+ readonly stormWindow: StormWindow;
76
+ readonly spawnGate: SpawnGate;
77
+ /** Fleet-level backend reachability memory (fed by pty_error / startup panes). */
78
+ readonly backendOutage: BackendOutageTracker;
53
79
  /** Live view of lifecycle.daemons — used throughout; not deprecated. */
54
80
  get daemons(): Map<string, import("./daemon.js").Daemon>;
55
81
  fleetConfig: FleetConfig | null;
@@ -83,6 +109,7 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
83
109
  costGuard: CostGuard | null;
84
110
  private statuslineWatcher;
85
111
  private dailySummary;
112
+ private dailyTipScheduler;
86
113
  private webhookEmitter;
87
114
  private topicIcons;
88
115
  private lastActivity;
@@ -94,6 +121,13 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
94
121
  private warmCapEvicting;
95
122
  /** Per-instance tail keeps cross-instance and scheduled deliveries FIFO. */
96
123
  private idleGatedDeliveryTails;
124
+ /**
125
+ * Per-instance cancellation epoch for deliveries which have not reached the
126
+ * daemon yet. A cancel advances the epoch; queued work captures the old value
127
+ * and becomes a no-op, while messages arriving after the click capture the new
128
+ * value and remain deliverable.
129
+ */
130
+ private deliveryEpochs;
97
131
  /** Non-user work must observe a fresh idle snapshot after the latest delivery. */
98
132
  private lastDeliveryAt;
99
133
  /** State-cache updates wake event-driven idle waiters without busy polling. */
@@ -106,10 +140,34 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
106
140
  readonly replyDeduper: ReplyDeduper;
107
141
  /** instanceName → what it is doing right now, when the backend can tell us. */
108
142
  private instanceActivity;
143
+ /** instanceName → this turn's tool list (multi-line), for the bubble. */
144
+ private instanceProgress;
109
145
  /** instanceName → tail of deliveries waiting for its IPC to come back. */
110
146
  private ipcWaitTails;
111
147
  /** instanceName → restart currently executing; concurrent callers join it. */
112
148
  private restartsInFlight;
149
+ /**
150
+ * Delayed automatic retries for instances whose startup failed. Before this a
151
+ * failed start was logged once and the instance stayed `stopped` until an
152
+ * operator noticed (2026-09-03: 4 kiro instances, all victims of the same
153
+ * backend outage during a post-update herd). instanceName → pending retry.
154
+ */
155
+ private startupRetries;
156
+ /** Bumped by every explicit stop (incl. the stop half of a restart); fences the outage hand-off. */
157
+ private explicitStopGeneration;
158
+ /** instanceName → outage hand-off currently executing; a repeat joins it. */
159
+ private handOffsInFlight;
160
+ /** instanceName → explicit stop currently executing; the outage hand-off waits for it. */
161
+ private stopsInFlight;
162
+ /** Aggregation window for the "N instances failed to start" notice. */
163
+ private startupRetryNotices;
164
+ /** Backoff between automatic startup retries; the last step repeats while the backend is down. */
165
+ static readonly STARTUP_RETRY_BACKOFF_MS: number[];
166
+ /** Hard cap on automatic startup retries (3 backoff steps + up to 3 more during a backend outage). */
167
+ static readonly STARTUP_RETRY_MAX_ATTEMPTS = 6;
168
+ /** Re-check interval when a retry is due but the tmux storm window still blocks spawns. */
169
+ static readonly STARTUP_RETRY_STORM_DEFER_MS = 60000;
170
+ static readonly STARTUP_RETRY_NOTICE_AGGREGATE_MS = 1000;
113
171
  private lastInboundMsg;
114
172
  private topicArchiver;
115
173
  controlClient: TmuxControlClient | null;
@@ -140,12 +198,14 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
140
198
  private updateProgressTimer;
141
199
  private updateProgressEditRunning;
142
200
  private lastUpdateProgressText;
201
+ private updateCompletionTipText;
143
202
  private eventLogPruneTimer;
144
203
  private logRotateTimer;
145
204
  /** Days of event/activity history to keep. */
146
205
  private static readonly EVENT_LOG_RETENTION_DAYS;
147
206
  private watchdogTimer;
148
207
  private startedAt;
208
+ private stormOpenNotifyTimer;
149
209
  private mirrorBuffer;
150
210
  private mirrorTimer;
151
211
  private sseClients;
@@ -153,6 +213,11 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
153
213
  private viewToken;
154
214
  private healthServerListening;
155
215
  constructor(dataDir: string);
216
+ private spawnConcurrency;
217
+ /** Wire the one fleet-wide storm into notification and recovery surfaces. */
218
+ private bindStormWindowEvents;
219
+ private formatStormDelay;
220
+ stormSuppressed(kind: string): boolean;
156
221
  private handleSighup;
157
222
  private scheduleReconcile;
158
223
  /**
@@ -222,6 +287,7 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
222
287
  private isModelAdmin;
223
288
  private handlePauseWakeSlash;
224
289
  private handleUpdateSlash;
290
+ private handleTipsSlash;
225
291
  /** Admin-only full conversation reset for fleet-topic and Classic instances. */
226
292
  private handleClearSlash;
227
293
  /** Get the adapter bound to an instance, falling back to primary adapter */
@@ -252,6 +318,14 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
252
318
  bindInstanceAdapter(name: string, adapterId: string, fromInbound?: boolean): void;
253
319
  getInstanceStatus(name: string): "running" | "paused" | "stopped" | "crashed";
254
320
  getInstanceExecutionState(name: string): InstanceState | null;
321
+ /**
322
+ * Subscription providers currently relevant to this fleet. Stopped/crashed
323
+ * rows do not keep a usage card alive, while persisted paused instances do.
324
+ * Classic channels use their effective backend merge chain, not the raw row.
325
+ */
326
+ getActiveUsageProviderIds(): ReadonlySet<string>;
327
+ /** Effective backends with a running or persisted-paused fleet/Classic instance. */
328
+ getActiveBackendIds(): ReadonlySet<string>;
255
329
  isClassicInstance(name: string): boolean;
256
330
  private cacheInstanceExecutionState;
257
331
  private cancelIdleButtonRetirement;
@@ -268,7 +342,15 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
268
342
  */
269
343
  private enforceWarmCap;
270
344
  private waitForInstanceIdle;
345
+ /**
346
+ * Ask the daemon to capture the pane before answering, then wait for any state
347
+ * report produced after this request. The ordinary query is intentionally
348
+ * cache-only (idle gates call it frequently); this opt-in refresh is reserved
349
+ * for lifecycle decisions where a stale state would strand UI.
350
+ */
351
+ private refreshInstanceExecutionState;
271
352
  private deliverWithIdleGate;
353
+ private holdDeliveryForStorm;
272
354
  /**
273
355
  * Hand a payload to an instance's IPC, waiting out a *transient* disconnect.
274
356
  *
@@ -311,6 +393,65 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
311
393
  * TODO: per-instance startup timeout (existing issue, not introduced here)
312
394
  */
313
395
  private startInstancesWithConcurrency;
396
+ /**
397
+ * Start from an UNATTENDED path (fleet startup, full restart, config
398
+ * reconcile): nobody is watching the result, so a failure is logged and
399
+ * handed to the delayed automatic retry instead of leaving the instance
400
+ * `stopped` forever. Explicit operator/API starts call startInstance directly
401
+ * and keep their synchronous error. Returns whether the instance is up.
402
+ */
403
+ private startInstanceUnattended;
404
+ /**
405
+ * Schedule attempt `attempt` (0-based) of the automatic startup retry for an
406
+ * instance whose start just failed. Backoff 1 → 5 → 15 min; while the
407
+ * instance's backend is known to be unreachable the 15-min step repeats up to
408
+ * STARTUP_RETRY_MAX_ATTEMPTS, after which we give up with one notice. Each
409
+ * retry re-checks the world (still configured, not running, not paused, no
410
+ * tmux storm) and runs through the SpawnGate as "recovery" — so a herd of
411
+ * failed instances comes back at the gate's concurrency, never all at once.
412
+ */
413
+ scheduleStartupRetry(name: string, attempt: number): void;
414
+ /** Drop a pending automatic retry (an operator start/stop/restart supersedes it). */
415
+ cancelStartupRetry(name: string): void;
416
+ /** Pending automatic retry, if any (status display / tests). */
417
+ pendingStartupRetry(name: string): {
418
+ attempt: number;
419
+ } | null;
420
+ /**
421
+ * A daemon's crash-respawn hit the backend outage (startup_backend_unreachable):
422
+ * stop THAT daemon and schedule the delayed retry. Serialized against the
423
+ * operator paths: an in-flight restart is awaited first (it replaces the
424
+ * daemon itself), the stop is identity-checked so a fresh daemon registered
425
+ * meanwhile is never deleted, and an explicit stop/restart that began during
426
+ * the hand-off (generation bump) owns the outcome — no retry is scheduled
427
+ * behind an operator's back.
428
+ */
429
+ handOffToStartupRetry(name: string, daemon: unknown): Promise<void>;
430
+ private runStartupRetry;
431
+ /** Fleet-topic (fleet.yaml) or ClassicBot (classic channel) — both are retryable; anything else is gone. */
432
+ private isConfiguredInstance;
433
+ /**
434
+ * Kind-aware start for the automatic retry: fleet-topic instances come from
435
+ * fleet.yaml, ClassicBot instances exist only in the classic channel manager
436
+ * and must be rebuilt through startClassicInstance (a fleet.yaml lookup alone
437
+ * silently dropped them — the retry timer fired and nothing happened).
438
+ */
439
+ private startConfiguredInstance;
440
+ /**
441
+ * Unattended ClassicBot start (fleet startup batch, full-restart batch, the
442
+ * classicBot.yaml reconcile): same contract as startInstanceUnattended —
443
+ * failures are logged and handed to the delayed automatic retry, whose
444
+ * kind-aware startConfiguredInstance rebuilds the Classic instance. Returns
445
+ * whether the instance is up.
446
+ */
447
+ private startClassicInstanceUnattended;
448
+ private backendNameOf;
449
+ /**
450
+ * One fleet-level notice per burst, not one per instance: a post-update herd
451
+ * fails many instances within the same second. Two notices per incident at
452
+ * most — "N failed, retrying in X" and, if it comes to that, "gave up on N".
453
+ */
454
+ private queueStartupRetryNotice;
314
455
  private runnableStartupCount;
315
456
  private configuredStartupInstanceNames;
316
457
  private restartProgressTarget;
@@ -362,6 +503,7 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
362
503
  retryCount: number;
363
504
  lastError?: string;
364
505
  }>;
506
+ private bindAdapterHealth;
365
507
  /**
366
508
  * Real, checkable fleet health for `/health` and the operator.
367
509
  *
@@ -390,6 +532,7 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
390
532
  total: number;
391
533
  connected: number;
392
534
  states: Record<string, string>;
535
+ details: Record<string, AdapterHealthSnapshot>;
393
536
  };
394
537
  startupComplete: boolean;
395
538
  /** See process-memory.ts: the fleet process and the whole service cgroup are
@@ -445,6 +588,10 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
445
588
  private handleDecisionCrud;
446
589
  /** Resolve display name for an instance, fallback to instance name. */
447
590
  resolveDisplayName(instanceName: string): string;
591
+ /** Persist identity to the instance's actual config store. Classic instances
592
+ * are registry rows in classicBot.yaml, not fleet.yaml instance entries. */
593
+ private setInstanceDisplayName;
594
+ private setInstanceDescription;
448
595
  private handleSetDisplayName;
449
596
  private handleSetDescription;
450
597
  handleScheduleCrudHttp(instance: string, op: string, args: Record<string, unknown>): Promise<unknown>;
@@ -465,9 +612,13 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
465
612
  private startTopicCleanupPoller;
466
613
  /**
467
614
  * Patch only values changed in the effective config into the original YAML
468
- * document. Unknown keys, explicit overrides and comments remain untouched.
615
+ * document. Unknown keys and comments remain untouched; redundant
616
+ * non-identity instance leaves are canonicalized to inheritance afterward.
469
617
  */
470
618
  saveFleetConfig(explicitPatches?: RawConfigPatch[]): void;
619
+ /** One-time upgrade migration; invalid YAML is never rewritten. */
620
+ private slimFleetConfigAtStartup;
621
+ private writeFleetConfigBackup;
471
622
  private patchFleetDocument;
472
623
  removeInstance(name: string): Promise<void>;
473
624
  startStatuslineWatcher(name: string): void;
@@ -548,13 +699,12 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
548
699
  */
549
700
  private postNonceButtonPrompt;
550
701
  /**
551
- * Claim a nonce-armed callback exactly once.
702
+ * Validate a nonce-armed callback and claim it exactly once.
552
703
  *
553
704
  * Returns:
554
705
  * - null — the callback is not for this prefix; try the next handler
555
706
  * - "consumed" — for this prefix but stale/denied/malformed; stop dispatch
556
- * - the entry+action — the click is authorized and now claimed (removed
557
- * from the map before any await, so double clicks cannot act twice)
707
+ * - the entry+action — the click is authorized and claimed before any await
558
708
  *
559
709
  * A stale click (expired nonce, or a pre-upgrade button whose payload no
560
710
  * longer parses) collapses the clicked message so the dead button stops
@@ -563,6 +713,23 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
563
713
  private consumeNonceCallback;
564
714
  /** Collapse a consumed prompt's buttons into a final status line. */
565
715
  private retireNonceButtons;
716
+ private tipsEnabled;
717
+ private readTipState;
718
+ private pickAvailableTip;
719
+ private tipText;
720
+ private formatTip;
721
+ /** Persistently enable advanced tips through the admin-only slash command. */
722
+ unlockAdvancedTips(userId: string): boolean;
723
+ /** Post one fresh nonce-armed tip in a known General channel. */
724
+ promptTip(generalName: string, adapter: ChannelAdapter, chatId: string, threadId?: string): Promise<"posted" | "empty" | "unavailable">;
725
+ private promptAdvancedTipUnlock;
726
+ private sendTipToGeneral;
727
+ private handleTipDismiss;
728
+ private handleTipUnlock;
729
+ /** Post the destructive `/clear` confirmation in the invoking channel. */
730
+ promptClearConfirmation(instanceName: string, channelId: string, adapter: ChannelAdapter, chatId: string, threadId?: string): Promise<string | null>;
731
+ /** Consume `/clear` Confirm/Cancel exactly once; only Confirm reaches IPC. */
732
+ private handleClearConfirmation;
566
733
  /**
567
734
  * Drop every pending prompt that refers to an instance being stopped or
568
735
  * restarted — a restart offer for an instance the operator just restarted
@@ -613,7 +780,7 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
613
780
  */
614
781
  private handleCancelClick;
615
782
  private hasCancelButton;
616
- sendCancelButton(instanceName: string, correlationId?: string): Promise<void>;
783
+ sendCancelButton(instanceName: string, correlationId?: string, preserveProgress?: boolean): Promise<void>;
617
784
  /**
618
785
  * The cancel button's text for a given elapsed time.
619
786
  *
@@ -623,6 +790,13 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
623
790
  * and once the agent had replied once there was no sign it was still going.
624
791
  */
625
792
  static progressText(elapsedMs: number, activity?: string | null, minElapsedMs?: number): string;
793
+ /** Render elapsed time consistently in live and retained progress bubbles. */
794
+ private static formatProgressElapsed;
795
+ /** Final read-only form of a bubble that contains semantic tool history.
796
+ * Neutral wording is intentional: the same retirement primitive is used for
797
+ * normal completion, a mid-turn bubble replacement, and user cancellation,
798
+ * so claiming every retained checkpoint is "completed" would be false. */
799
+ private composeRetiredBubbleText;
626
800
  /**
627
801
  * Make a tool summary safe to paste into a channel message.
628
802
  *
@@ -640,6 +814,26 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
640
814
  * never used to decide anything — purely what the user is shown.
641
815
  */
642
816
  private cacheInstanceActivity;
817
+ /**
818
+ * Cache the turn's tool-progress list and push it into the instance's live
819
+ * bubble, coalesced so a burst of tool events cannot flood the Bot API
820
+ * (Telegram's flood limit is per-chat across ALL forum topics, so edits are
821
+ * rate-limited per bubble AND ride behind the daemon-side 3s coalescer).
822
+ */
823
+ private cacheInstanceProgress;
824
+ /** At most one progress-driven edit per bubble per TOOL_PROGRESS_EDIT_MIN_MS. */
825
+ private scheduleProgressEdit;
826
+ /**
827
+ * The ONE composer for the bubble text. Both writers — the elapsed-time
828
+ * ticker and the tool-progress push — go through here; two independent
829
+ * renderers editing the same message is how the progress list used to get
830
+ * wiped by the next elapsed tick (#528 trap 2).
831
+ */
832
+ private composeBubbleText;
833
+ /** Pure composition of header + tool list, exposed for tests. */
834
+ static bubbleText(elapsedMs: number, activity: string | undefined, minElapsedMs: number, progress: string | undefined): string;
835
+ /** Recompose and edit the bubble in place; skips when nothing changed. */
836
+ private refreshBubble;
643
837
  /**
644
838
  * Refresh the button's text in place while the instance keeps working.
645
839
  *
@@ -652,10 +846,14 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
652
846
  private startProgressTicker;
653
847
  /**
654
848
  * After a reply: give the instance REPLY_RETIRE_GRACE_MS to resume working; if
655
- * it has not, retire its button. Re-arming replaces the previous timer, so a
656
- * burst of replies ends with exactly one pending check.
849
+ * it has not, retire its button. The daemon is asked for a fresh pane capture
850
+ * before deciding. Reading only the transition cache stranded the first
851
+ * post-restart bubble when its startup "working" report never got a matching
852
+ * idle edge. Re-arming replaces the previous timer, so a burst of replies ends
853
+ * with exactly one pending check.
657
854
  */
658
855
  private armReplyGrace;
856
+ private finishReplyGrace;
659
857
  /** Retire (delete) every cancel button belonging to an instance. */
660
858
  private retireInstanceButtons;
661
859
  /** Begin retiring one button (delete + bounded retry on failure). Idempotent:
@@ -680,7 +878,9 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
680
878
  private sweepOrphanedCancelButtons;
681
879
  /** Re-attempt a failed button delete up to CANCEL_BTN_MAX_RETRIES times. */
682
880
  private scheduleButtonRetry;
683
- /** Delete one button's message via its own adapter. Resolves on success,
881
+ /** Retire one cancel bubble via its own adapter. A bubble that accumulated
882
+ * tool progress is kept as a read-only history message; a plain bubble keeps
883
+ * the legacy delete/edit-to-checkmark behavior. Resolves on success and
684
884
  * rejects on failure so the caller can retry. */
685
885
  private deleteButtonMessage;
686
886
  /** Retire all cancel buttons for an instance — on reply or cancel. */
@@ -704,6 +904,123 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
704
904
  private reactDone;
705
905
  /** Interrupt an instance's current generation (cancel button / /cancel). */
706
906
  cancelInstance(instanceName: string): boolean;
907
+ getDeliveryEpoch(instanceName: string): number;
908
+ private isDeliveryEpochCurrent;
909
+ /** Invalidate work queued before a user cancel and wake idle-gate waiters. */
910
+ private cancelPendingDeliveries;
911
+ private activeLogin;
912
+ /** Post the backend chooser for a bare `/login`. Caller enforces admin. */
913
+ promptLoginBackends(chat: {
914
+ adapter: ChannelAdapter;
915
+ adapterId: string;
916
+ chatId: string;
917
+ threadId?: string;
918
+ }): Promise<void>;
919
+ /**
920
+ * Ask the General topic to approve a ClassicBot access request.
921
+ *
922
+ * Two affirmative buttons rather than one, when the trigger identified a
923
+ * user. Allowing a group to talk and granting that person ClassicBot admin
924
+ * (start/stop/model on classic channels — NOT fleet admin, which reads
925
+ * fleet.yaml `channel.access.allowed_users`) have different blast radii, and
926
+ * bundling them would force anyone who wants only the cheap one to accept the
927
+ * expensive one or go edit YAML by hand.
928
+ *
929
+ * Built on postNonceButtonPrompt, so it inherits the canonical-address
930
+ * binding (#682) that makes these buttons answer in a Telegram General topic.
931
+ */
932
+ private promptClassicApproval;
933
+ /**
934
+ * Apply an approved ClassicBot access request.
935
+ *
936
+ * Writes through ClassicChannelManager's typed mutators rather than handing
937
+ * General a YAML edit: the id is stored via String() so a Discord snowflake
938
+ * cannot land as a YAML integer and lose precision, the existing save() path
939
+ * is reused, in-memory state is correct immediately (no waiting on the 30s
940
+ * reload poll), and a click cannot silently do nothing because General
941
+ * happened to be paused. General is still told the outcome.
942
+ */
943
+ private applyClassicApproval;
944
+ /** Consume a ClassicBot approval button. */
945
+ private handleClassicApproval;
946
+ /**
947
+ * Backend chooser for a bare `/install-cli`, mirroring promptLoginBackends so
948
+ * both commands feel the same. Built on postNonceButtonPrompt rather than the
949
+ * `/model` selection coordinator: that is the mechanism `/login` already uses,
950
+ * and the one whose canonical-address binding (#682) makes the buttons answer
951
+ * in a Telegram General topic.
952
+ *
953
+ * Unlike the login chooser this does NOT filter to backends the fleet already
954
+ * runs. Installing is how you get a backend you do not have yet, so filtering
955
+ * by configured backends would hide the only entry the admin came for.
956
+ *
957
+ * gemini-cli is omitted: it is deprecated (see backend/factory.ts). Typing
958
+ * `/install-cli gemini-cli` still works — this only stops recommending it.
959
+ */
960
+ promptInstallBackends(chat: {
961
+ adapter: ChannelAdapter;
962
+ adapterId: string;
963
+ chatId: string;
964
+ threadId?: string;
965
+ }): Promise<void>;
966
+ /** Backend chooser button → start that backend's install session. */
967
+ private handleInstallBackendSelect;
968
+ /**
969
+ * Start a login session for one backend. Caller enforces admin.
970
+ * Returns a status line to post, or null when a confirmation prompt was
971
+ * posted instead (auth still valid — see the pre-check below).
972
+ */
973
+ startLoginSession(backendArg: string, chat: {
974
+ adapter: ChannelAdapter;
975
+ adapterId: string;
976
+ chatId: string;
977
+ threadId?: string;
978
+ }, opts?: {
979
+ skipAuthCheck?: boolean;
980
+ }): Promise<string | null>;
981
+ /** Create the login window and session (pre-check already settled). */
982
+ private launchLoginSession;
983
+ /** `/login code <text>` — paste admin-supplied text into the login window. */
984
+ loginSubmitInput(text: string): Promise<string>;
985
+ /** `/login cancel` — abort the active session and remove its window. */
986
+ cancelLoginSession(): Promise<string>;
987
+ /**
988
+ * The URL (+ code) is a live credential: whoever completes it binds THEIR
989
+ * account to this fleet's CLI. Telegram gets an HTML spoiler (same treatment
990
+ * as the dashboard token); other adapters get plain text with the warning.
991
+ */
992
+ private sendLoginSecret;
993
+ /**
994
+ * After a successful login: wake the paused instances of that backend AND
995
+ * restart the running ones — a live CLI holds the old token in memory and
996
+ * only re-reads credentials on process start (a paused instance's CLI is
997
+ * already dead, so waking it respawns with the new token for free).
998
+ */
999
+ private recoverBackendInstances;
1000
+ /** Backend chooser button → start that backend's login session. */
1001
+ private handleLoginBackendSelect;
1002
+ /** Re-login confirmation (auth pre-check said credentials still work). */
1003
+ private handleLoginConfirm;
1004
+ /** Kiro provider button → drive the CLI's arrow-key selector. */
1005
+ private handleLoginMenuSelect;
1006
+ private activeInstall;
1007
+ /** Start a CLI install session. Caller enforces admin. */
1008
+ startInstallSession(backendArg: string, chat: {
1009
+ adapter: ChannelAdapter;
1010
+ adapterId: string;
1011
+ chatId: string;
1012
+ threadId?: string;
1013
+ }): Promise<string>;
1014
+ /** `/install-cli cancel` — abort the active install and remove its window. */
1015
+ cancelInstallSession(): Promise<string>;
1016
+ /** `command -v` on a login shell, so PATH additions from rc files count. */
1017
+ private verifyBinaryOnLoginShell;
1018
+ /** "Sign in now?" button after a successful install. */
1019
+ private handleInstallLoginConfirm;
1020
+ /** Discord native `/login` slash — shared by every adapter dispatch block. */
1021
+ private handleLoginSlash;
1022
+ /** Discord native `/install-cli` slash — shared by every dispatch block. */
1023
+ private handleInstallCliSlash;
707
1024
  queueMirrorMessage(text: string): void;
708
1025
  /** Push an SSE event to all connected Web UI clients. */
709
1026
  emitSseEvent(event: string, data: unknown): void;
@@ -724,8 +1041,45 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
724
1041
  private static GENERAL_INSTRUCTIONS;
725
1042
  /** Ensure the general instance has its project instructions file + knowledge */
726
1043
  private ensureGeneralInstructions;
727
- /** Copy general-knowledge steering + skills to the general instance's workspace */
1044
+ /** Resolve the workspace path a backend actually uses before publishing knowledge. */
1045
+ private resolveKnowledgeWorkDir;
1046
+ /**
1047
+ * Where each backend natively loads on-demand skills from, relative to the
1048
+ * workspace. Backends without a native skill mechanism (opencode, grok,
1049
+ * antigravity, gemini-cli) are deliberately absent: dropping files a CLI
1050
+ * never reads is clutter, not capability. Unknown directories are ignored
1051
+ * by older CLI versions, so publishing is fail-open across upgrades.
1052
+ */
1053
+ private static SKILLS_DIR_SEGMENTS;
1054
+ /** Copy general-knowledge steering + all role-eligible skills to General. */
728
1055
  private syncGeneralKnowledge;
1056
+ /** Publish only the bundled skills eligible for an instance role. */
1057
+ private syncRoleSkills;
1058
+ /**
1059
+ * Steering (always-on rules like core-rules.md). Kiro loads a native
1060
+ * steering directory; every other backend gets the content embedded into
1061
+ * its instructions file (CLAUDE.md / AGENTS.md / …) inside a managed marker
1062
+ * block — the previous behavior dropped bare .md files in the workspace
1063
+ * root, which no CLI ever read. The block is replaced in place on every
1064
+ * sync, so rule updates reach EXISTING workspaces; everything the user
1065
+ * wrote outside the markers is preserved byte-for-byte.
1066
+ */
1067
+ private syncGeneralSteering;
1068
+ private static STEERING_BLOCK_BEGIN;
1069
+ private static STEERING_BLOCK_END;
1070
+ /**
1071
+ * Publish AgEnD's bundled skills into a CLI's native skills directory,
1072
+ * owning ONLY what we published. A manifest records which skill names AgEnD
1073
+ * wrote; a bundled rename/removal deletes the stale managed copy, while a
1074
+ * skill the user created by hand is never listed and therefore never
1075
+ * touched — even if a future bundle happens to reuse its name (the sync
1076
+ * then skips it and logs, rather than overwrite the user's work). The sole
1077
+ * migration exception is the pre-manifest Kiro General layout explicitly
1078
+ * selected by adoptLegacyUnmanaged.
1079
+ */
1080
+ private syncManagedSkills;
1081
+ /** Read AgEnD's roles extension from SKILL.md YAML frontmatter. */
1082
+ private readManagedSkillRoles;
729
1083
  /** Fetch forum topic icon stickers and pick emoji IDs for each state */
730
1084
  private resolveTopicIcons;
731
1085
  /** Set topic icon based on instance state */
@@ -768,6 +1122,34 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
768
1122
  private probeCliEnvs;
769
1123
  /** Best-effort model list for `/model`: cached CLI env first, else live probe. Never throws. */
770
1124
  private getModelOptions;
1125
+ /**
1126
+ * Model catalog behind the `list_models` tool.
1127
+ *
1128
+ * The two scopes are not cosmetic. "global" is the account/CLI catalog served
1129
+ * from the startup probe cache; "instance" is resolved through that instance's
1130
+ * OWN backend config, and for a Codex instance on a custom provider that is a
1131
+ * different catalog entirely — `listModels()` reads models_cache.json out of
1132
+ * the instance's private CODEX_HOME. Answering such an instance with the
1133
+ * account list would name models its CLI rejects, which is exactly the
1134
+ * mistake this tool exists to prevent.
1135
+ *
1136
+ * `scope` always describes where the returned LIST came from, not what was
1137
+ * asked for: an instance query that falls back to the account catalog reports
1138
+ * scope "global" and says so in `note`, rather than implying instance-level
1139
+ * accuracy it does not have.
1140
+ *
1141
+ * Never throws — a model listing is an aid, and failing it must not fail a turn.
1142
+ */
1143
+ listModelCatalog(opts?: {
1144
+ backend?: string;
1145
+ instanceName?: string;
1146
+ }): Promise<ModelCatalog>;
1147
+ /** The custom provider an instance overrides its backend with, if any. */
1148
+ private customProviderFor;
1149
+ /** Ask a backend for its catalog using ONE instance's real config. Never throws. */
1150
+ private instanceScopedModels;
1151
+ /** Account-wide catalog: probe cache first, live probe on miss. */
1152
+ private globalModelCatalog;
771
1153
  /** `/model` slash handler (admin only). No arg → DC menu; `/model <name>` → apply directly. */
772
1154
  /** Label an effort choice, marking the one currently configured. */
773
1155
  private effortChoiceLabel;
@@ -785,6 +1167,10 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
785
1167
  * Returns null on success (menu shown), or a fallback string to send.
786
1168
  */
787
1169
  promptModelMenu(instanceName: string, userId: string, channelId: string, adapter: ChannelAdapter, chatId: string, threadId?: string): Promise<string | null>;
1170
+ /** Cached-or-live account catalog behind "/model → more models" (claude). */
1171
+ private claudeApiModelOptions;
1172
+ /** Replace a consumed "/model" menu with the full account catalog tier. */
1173
+ private expandClaudeModelMenu;
788
1174
  /** Consume a `/model` selection callback. Returns true for all model-select ids (incl. stale). */
789
1175
  private handleModelSelection;
790
1176
  /** Apply a model to an instance: runtime paste (claude-code) or persist + restart (others). */
@@ -872,7 +1258,8 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
872
1258
  /**
873
1259
  * Hot-reload: re-read fleet.yaml and reconcile running instances.
874
1260
  * Starts new, stops removed, restarts modified instances.
875
- * Fleet-level config (access, cost_guard, etc.) requires /restart to take effect.
1261
+ * Whitelisted runtime fields are pushed into live daemons; all other instance
1262
+ * fields, plus cold fleet-level settings, retain restart semantics.
876
1263
  */
877
1264
  private reconcileInstances;
878
1265
  restartInstances(): Promise<void>;