@songsid/agend 2.1.5-beta.2 → 2.1.5-beta.21

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 (96) hide show
  1. package/dist/backend/claude-code.d.ts +12 -0
  2. package/dist/backend/claude-code.js +14 -2
  3. package/dist/backend/claude-code.js.map +1 -1
  4. package/dist/backend/codex.d.ts +25 -1
  5. package/dist/backend/codex.js +76 -0
  6. package/dist/backend/codex.js.map +1 -1
  7. package/dist/backend/kiro.js +30 -2
  8. package/dist/backend/kiro.js.map +1 -1
  9. package/dist/backend/types.d.ts +70 -0
  10. package/dist/backend/types.js +23 -1
  11. package/dist/backend/types.js.map +1 -1
  12. package/dist/channel/adapters/discord.d.ts +17 -2
  13. package/dist/channel/adapters/discord.js +82 -8
  14. package/dist/channel/adapters/discord.js.map +1 -1
  15. package/dist/channel/adapters/telegram.d.ts +2 -2
  16. package/dist/channel/adapters/telegram.js +8 -8
  17. package/dist/channel/adapters/telegram.js.map +1 -1
  18. package/dist/channel/types.d.ts +22 -1
  19. package/dist/cli.js +37 -37
  20. package/dist/cli.js.map +1 -1
  21. package/dist/config.d.ts +4 -0
  22. package/dist/config.js +4 -0
  23. package/dist/config.js.map +1 -1
  24. package/dist/daemon.d.ts +181 -21
  25. package/dist/daemon.js +1054 -225
  26. package/dist/daemon.js.map +1 -1
  27. package/dist/deadline.d.ts +23 -0
  28. package/dist/deadline.js +29 -0
  29. package/dist/deadline.js.map +1 -0
  30. package/dist/fleet-context.d.ts +17 -1
  31. package/dist/fleet-context.js.map +1 -1
  32. package/dist/fleet-manager.d.ts +189 -2
  33. package/dist/fleet-manager.js +843 -124
  34. package/dist/fleet-manager.js.map +1 -1
  35. package/dist/full-restart.d.ts +20 -0
  36. package/dist/full-restart.js +39 -0
  37. package/dist/full-restart.js.map +1 -0
  38. package/dist/instance-lifecycle.d.ts +8 -2
  39. package/dist/instance-lifecycle.js +47 -3
  40. package/dist/instance-lifecycle.js.map +1 -1
  41. package/dist/instance-removal.d.ts +13 -0
  42. package/dist/instance-removal.js +14 -0
  43. package/dist/instance-removal.js.map +1 -0
  44. package/dist/instructions.js +2 -2
  45. package/dist/instructions.js.map +1 -1
  46. package/dist/locale.js +54 -4
  47. package/dist/locale.js.map +1 -1
  48. package/dist/login-controller.d.ts +31 -4
  49. package/dist/login-controller.js +52 -4
  50. package/dist/login-controller.js.map +1 -1
  51. package/dist/login-flows.d.ts +29 -0
  52. package/dist/login-flows.js +48 -1
  53. package/dist/login-flows.js.map +1 -1
  54. package/dist/outbound-handlers.d.ts +1 -0
  55. package/dist/outbound-handlers.js +118 -29
  56. package/dist/outbound-handlers.js.map +1 -1
  57. package/dist/outbound-schemas.d.ts +7 -0
  58. package/dist/outbound-schemas.js +3 -0
  59. package/dist/outbound-schemas.js.map +1 -1
  60. package/dist/pane-input-residue.d.ts +25 -2
  61. package/dist/pane-input-residue.js +31 -2
  62. package/dist/pane-input-residue.js.map +1 -1
  63. package/dist/restart-progress.d.ts +35 -5
  64. package/dist/restart-progress.js +177 -34
  65. package/dist/restart-progress.js.map +1 -1
  66. package/dist/service-restart-selection.d.ts +27 -0
  67. package/dist/service-restart-selection.js +25 -0
  68. package/dist/service-restart-selection.js.map +1 -0
  69. package/dist/topic-commands.d.ts +27 -2
  70. package/dist/topic-commands.js +59 -13
  71. package/dist/topic-commands.js.map +1 -1
  72. package/dist/turn-reply-guard.d.ts +41 -0
  73. package/dist/turn-reply-guard.js +77 -0
  74. package/dist/turn-reply-guard.js.map +1 -0
  75. package/dist/ui/dashboard.html +34 -8
  76. package/dist/ui/view.html +32 -18
  77. package/dist/ui/web-terminal/terminal.html +1 -0
  78. package/dist/update-marker.d.ts +11 -1
  79. package/dist/update-marker.js +19 -4
  80. package/dist/update-marker.js.map +1 -1
  81. package/dist/update-progress.d.ts +1 -1
  82. package/dist/update-progress.js +12 -0
  83. package/dist/update-progress.js.map +1 -1
  84. package/dist/usage/providers.d.ts +39 -0
  85. package/dist/usage/providers.js +69 -3
  86. package/dist/usage/providers.js.map +1 -1
  87. package/dist/view-api.d.ts +1 -0
  88. package/dist/view-api.js +12 -2
  89. package/dist/view-api.js.map +1 -1
  90. package/dist/web-api.d.ts +22 -1
  91. package/dist/web-api.js +25 -1
  92. package/dist/web-api.js.map +1 -1
  93. package/dist/web-terminal-http.d.ts +8 -0
  94. package/dist/web-terminal-http.js +29 -3
  95. package/dist/web-terminal-http.js.map +1 -1
  96. package/package.json +1 -1
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Run work under a wall-clock deadline without cancelling it.
3
+ *
4
+ * Nothing here aborts anything — a tmux spawn or an HTTP call keeps going after
5
+ * its deadline passes; the caller simply stops waiting and can say so. That is
6
+ * the point: a step with no deadline cannot report anything while it hangs, so
7
+ * the user sees silence and assumes the whole operation is stuck (#722, and the
8
+ * post-login recovery that silently swallowed its own success message).
9
+ *
10
+ * Rejections are reported rather than thrown, so one failed step never takes
11
+ * the surrounding loop down with it, and are always observed — an abandoned
12
+ * promise that rejects later must not surface as an unhandled rejection.
13
+ */
14
+ export type DeadlineResult<T> = {
15
+ status: "fulfilled";
16
+ value: T;
17
+ } | {
18
+ status: "rejected";
19
+ reason: unknown;
20
+ } | {
21
+ status: "timeout";
22
+ };
23
+ export declare function runBeforeDeadline<T>(start: () => Promise<T>, deadline: number): Promise<DeadlineResult<T>>;
@@ -0,0 +1,29 @@
1
+ /** Largest delay setTimeout honours; anything above is clamped to 1ms by Node. */
2
+ const MAX_TIMER_MS = 2_147_483_647;
3
+ export async function runBeforeDeadline(start, deadline) {
4
+ const remaining = deadline - Date.now();
5
+ if (remaining <= 0)
6
+ return { status: "timeout" };
7
+ let work;
8
+ try {
9
+ work = start();
10
+ }
11
+ catch (reason) {
12
+ // A synchronous throw from `start` is a rejection like any other.
13
+ return { status: "rejected", reason };
14
+ }
15
+ let timer;
16
+ const timeout = new Promise(resolve => {
17
+ // Node clamps a delay above the 32-bit maximum to 1ms — so a very distant
18
+ // deadline would fire IMMEDIATELY and report every step as timed out, the
19
+ // exact opposite of what the caller asked for. Cap instead.
20
+ timer = setTimeout(() => resolve({ status: "timeout" }), Math.min(remaining, MAX_TIMER_MS));
21
+ timer.unref?.();
22
+ });
23
+ const settled = work.then(value => ({ status: "fulfilled", value }), reason => ({ status: "rejected", reason }));
24
+ const result = await Promise.race([settled, timeout]);
25
+ if (timer)
26
+ clearTimeout(timer);
27
+ return result;
28
+ }
29
+ //# sourceMappingURL=deadline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deadline.js","sourceRoot":"","sources":["../src/deadline.ts"],"names":[],"mappings":"AAkBA,kFAAkF;AAClF,MAAM,YAAY,GAAG,aAAa,CAAC;AAEnC,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAI,KAAuB,EAAE,QAAgB;IAClF,MAAM,SAAS,GAAG,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACxC,IAAI,SAAS,IAAI,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IACjD,IAAI,IAAgB,CAAC;IACrB,IAAI,CAAC;QACH,IAAI,GAAG,KAAK,EAAE,CAAC;IACjB,CAAC;IAAC,OAAO,MAAM,EAAE,CAAC;QAChB,kEAAkE;QAClE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;IACxC,CAAC;IACD,IAAI,KAAgD,CAAC;IACrD,MAAM,OAAO,GAAG,IAAI,OAAO,CAAoB,OAAO,CAAC,EAAE;QACvD,0EAA0E;QAC1E,0EAA0E;QAC1E,4DAA4D;QAC5D,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC;QAC5F,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CACvB,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,EACzC,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,CAC3C,CAAC;IACF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,IAAI,KAAK;QAAE,YAAY,CAAC,KAAK,CAAC,CAAC;IAC/B,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -5,6 +5,7 @@ import type { Scheduler } from "./scheduler/index.js";
5
5
  import type { Logger } from "./logger.js";
6
6
  import type { CostGuard } from "./cost-guard.js";
7
7
  import type { ClassicChannelManager } from "./classic-channel-manager.js";
8
+ import type { ExplicitInstanceRemoval } from "./instance-removal.js";
8
9
  export type RouteTarget = {
9
10
  kind: "instance";
10
11
  name: string;
@@ -88,7 +89,13 @@ export interface FleetContext {
88
89
  saveFleetConfig(): void;
89
90
  getInstanceDir(name: string): string;
90
91
  createForumTopic(topicName: string, adapterId?: string): Promise<number | string>;
91
- removeInstance(name: string): Promise<void>;
92
+ removeInstance(name: string, authorization: ExplicitInstanceRemoval): Promise<void>;
93
+ /** Quarantine a provider-confirmed missing topic without deleting user data. */
94
+ quarantineMissingTopic(threadId: string, target: RouteTarget, evidence: {
95
+ source: "provider-event" | "provider-probe";
96
+ adapterId?: string;
97
+ generation?: number;
98
+ }): void;
92
99
  getAdapterStates?(): Map<string, {
93
100
  status: string;
94
101
  retryCount: number;
@@ -103,6 +110,8 @@ export interface FleetContext {
103
110
  /** Persist and edit one `/update` message across the fleet process restart. */
104
111
  beginUpdateProgress?(adapter: import("./channel/types.js").ChannelAdapter, chatId: string, threadId: string | undefined, messageId: string): void;
105
112
  failUpdateProgress?(message: string): void;
113
+ /** Persist `/restart full`, wait for idle, then launch the environment-aware service restart. */
114
+ requestFullRestart?(adapter: import("./channel/types.js").ChannelAdapter, chatId: string, threadId: string | undefined, messageId: string): Promise<boolean>;
106
115
  /** Post one random, not-yet-dismissed tip with a dismiss button. */
107
116
  promptTip?(generalName: string, adapter: import("./channel/types.js").ChannelAdapter, chatId: string, threadId?: string): Promise<"posted" | "empty" | "unavailable">;
108
117
  /** Persistently enable advanced tips without waiting for the dismissal threshold. */
@@ -151,6 +160,13 @@ export interface FleetContext {
151
160
  cancelInstallSession?(): Promise<string>;
152
161
  /** Human-readable effective model for an instance (resolves inherited defaults). */
153
162
  modelDisplayForInstance?(name: string): string;
163
+ /** How this instance's backend takes a reasoning-effort setting, if at all. */
164
+ effortStrategyFor?(name: string): "runtime" | "restart" | "unsupported";
165
+ /** Configured effort for an instance: per-instance, else fleet default, else none. */
166
+ resolveInstanceEffort?(name: string): {
167
+ effort: string | null;
168
+ source: "instance" | "fleet-default" | "unset";
169
+ };
154
170
  /**
155
171
  * Show a model-selection inline keyboard for the given instance in a TG topic.
156
172
  * Returns a fallback text message if no model list is available (caller should send it).
@@ -1 +1 @@
1
- {"version":3,"file":"fleet-context.js","sourceRoot":"","sources":["../src/fleet-context.ts"],"names":[],"mappings":"AAgCA,MAAM,UAAU,sBAAsB,CAAC,MAAmB;IACxD,OAAO,MAAM,CAAC,IAAI,KAAK,UAAU,CAAC;AACpC,CAAC"}
1
+ {"version":3,"file":"fleet-context.js","sourceRoot":"","sources":["../src/fleet-context.ts"],"names":[],"mappings":"AAiCA,MAAM,UAAU,sBAAsB,CAAC,MAAmB;IACxD,OAAO,MAAM,CAAC,IAAI,KAAK,UAAU,CAAC;AACpC,CAAC"}
@@ -19,6 +19,7 @@ import { type OutboundContext } from "./outbound-handlers.js";
19
19
  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
+ import { type ExplicitInstanceRemoval } from "./instance-removal.js";
22
23
  import type { InstanceState } from "./backend/types.js";
23
24
  import { StormWindow } from "./storm-window.js";
24
25
  import { SpawnGate } from "./spawn-gate.js";
@@ -66,6 +67,38 @@ export interface DeliveryOptions {
66
67
  /** Test/operational override; normal deliveries use the 60 second backstop. */
67
68
  idleTimeoutMs?: number;
68
69
  }
70
+ export declare const LOGIN_CALLBACK_PREFIX = "login:";
71
+ /**
72
+ * Upper bound on a live probe driven by `/model`.
73
+ *
74
+ * A probe chains bounded leaves but is not itself bounded: claude-code runs
75
+ * `--version` (5s) then listModels then listApiModels (an 8s AbortController
76
+ * against api.anthropic.com), and several backends' probes carry no explicit
77
+ * timeout at all. Awaiting that inline would make `/model` the next thing to
78
+ * hang, so it races this deadline and falls back to the cached list.
79
+ *
80
+ * Must stay above CLI_PROBE_LONGEST_CHAIN_MS — the probe's bounded steps run
81
+ * back to back, so clearing only the longest single leaf would be false
82
+ * confidence: a deadline above 8s but below the 13s chain still truncates a
83
+ * probe that would have succeeded. Bounding the chain rather than the leaf is
84
+ * the lesson from the usage hang, and the tests assert against the derived
85
+ * chain constant so raising either step cannot silently break it.
86
+ *
87
+ * The deadline exists to stop a probe hanging forever, not to cut short one that
88
+ * would have finished: truncating a legitimate probe serves the previous list
89
+ * and hides exactly the newly released model the user opened `/model` to find.
90
+ * The wait is announced before it starts, so it reads as progress, not a stall.
91
+ */
92
+ export declare const CLI_ENV_PROBE_DEADLINE_MS = 16000;
93
+ /**
94
+ * How many CLIs may cold-start at once, from BOTH memory and cores.
95
+ *
96
+ * Memory alone said 10 on any host with roughly 3GB free, so a three-core box
97
+ * started ten CLIs together, saturated the CPU, and healthy starts then missed
98
+ * their startup budget — which used to cost the user their conversation. Cores
99
+ * bound how many can actually make progress; memory bounds how many fit.
100
+ */
101
+ export declare function deriveSpawnConcurrency(freeMemMB: number, cores: number): number;
69
102
  export declare class FleetManager implements FleetContext, LifecycleContext, ArchiverContext, StatuslineWatcherContext, OutboundContext, AgentEndpointContext {
70
103
  dataDir: string;
71
104
  private static signalTarget;
@@ -102,6 +135,10 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
102
135
  private reloadPending;
103
136
  /** A running reconciliation; only one may mutate lifecycle/config state at a time. */
104
137
  private reconcileInFlight;
138
+ /** Topology checks are serialized separately from config reconciliation. */
139
+ private topicCleanupInFlight;
140
+ private topicCleanupGeneration;
141
+ private topicProbeWarnings;
105
142
  logger: Logger;
106
143
  private topicCommands;
107
144
  sessionRegistry: Map<string, string>;
@@ -199,6 +236,8 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
199
236
  private updateProgressEditRunning;
200
237
  private lastUpdateProgressText;
201
238
  private updateCompletionTipText;
239
+ /** Injectable only to keep the chat→CLI reload hand-off deterministic in tests. */
240
+ private fullRestartLauncher;
202
241
  private eventLogPruneTimer;
203
242
  private logRotateTimer;
204
243
  /** Days of event/activity history to keep. */
@@ -287,6 +326,7 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
287
326
  private isModelAdmin;
288
327
  private handlePauseWakeSlash;
289
328
  private handleUpdateSlash;
329
+ private handleRestartSlash;
290
330
  private handleTipsSlash;
291
331
  /** Admin-only full conversation reset for fleet-topic and Classic instances. */
292
332
  private handleClearSlash;
@@ -296,6 +336,26 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
296
336
  getWorldForInstance(name: string): AdapterWorld | undefined;
297
337
  /** Get channel config for a specific adapter (by id), falling back to primary */
298
338
  getChannelConfig(adapterId?: string): import("./types.js").ChannelConfig | undefined;
339
+ /**
340
+ * The configured world that owns `chatId`, when that is provably NOT the
341
+ * world `target` lives in. Returns undefined when they agree, when there is
342
+ * nothing to check, or when no configured channel claims the id.
343
+ *
344
+ * Deliberately one-sided: only a POSITIVE match against another channel's
345
+ * group id counts as foreign. A chat id that matches nothing may still be
346
+ * legitimate for this world (a classic channel, a DM), and treating
347
+ * "unrecognised" as "wrong" would stop seeding for cases that work today.
348
+ *
349
+ * Read from config rather than the live worlds map on purpose: a channel
350
+ * whose adapter failed to start still owns its group id, and the coordinates
351
+ * are just as unusable by the target's adapter either way.
352
+ *
353
+ * This is the other half of what scheduleSourceAdapter fixed. That one stops
354
+ * the trigger NOTICE being sent through the wrong bot; this one stops the
355
+ * same coordinates being planted as the target instance's reply context,
356
+ * which is what made its own replies fail until someone spoke to it (#752).
357
+ */
358
+ private scheduleChatWorldMismatch;
299
359
  /** Get the group_id for an instance's bound adapter */
300
360
  getGroupIdForInstance(name: string): string;
301
361
  /** Configured primary adapter id. Never infer this from Map insertion order. */
@@ -448,6 +508,13 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
448
508
  */
449
509
  private startClassicInstanceUnattended;
450
510
  private backendNameOf;
511
+ /**
512
+ * Every configured instance whose backend may share credentials. ClassicBot
513
+ * rows live only in classicBot.yaml, so backend-wide operations must not use
514
+ * fleetConfig.instances as their roster. Set keeps a malformed name collision
515
+ * from restarting the same process twice; backendNameOf defines ownership.
516
+ */
517
+ private configuredBackendInstanceNames;
451
518
  /**
452
519
  * One fleet-level notice per burst, not one per instance: a post-update herd
453
520
  * fails many instances within the same second. Two notices per incident at
@@ -457,6 +524,13 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
457
524
  private runnableStartupCount;
458
525
  private configuredStartupInstanceNames;
459
526
  private restartProgressTarget;
527
+ /** Resolve only an adapter generation that can accept progress delivery.
528
+ * Discord exposes direct gateway readiness; adapters without a health
529
+ * snapshot use the fleet startup/retry state. */
530
+ private readyProgressAdapter;
531
+ /** Last-resort completion after RestartProgress could not deliver to its
532
+ * adopted target. It stays bounded and never wakes a not-ready gateway. */
533
+ private sendFleetStartCompletionFallback;
460
534
  stopInstance(name: string): Promise<void>;
461
535
  /** Restart a single instance, reloading fleet.yaml first to pick up config changes. */
462
536
  restartSingleInstance(name: string, opts?: {
@@ -479,6 +553,12 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
479
553
  token: string | null;
480
554
  };
481
555
  beginUpdateProgress(adapter: ChannelAdapter, chatId: string, threadId: string | undefined, messageId: string): void;
556
+ /** Persist the public response, wait for idle, then start the canonical service restart. */
557
+ requestFullRestart(adapter: ChannelAdapter, chatId: string, threadId: string | undefined, messageId: string): Promise<boolean>;
558
+ private isOwnedFullRestartMarker;
559
+ /** Give current work the same bounded idle grace used by graceful reload. */
560
+ private waitForFullRestartIdleGrace;
561
+ private reportFullRestartFailure;
482
562
  failUpdateProgress(message: string): void;
483
563
  /** The old fleet edits CLI install stages; the new fleet adopts the same message below. */
484
564
  private startUpdateProgressMonitor;
@@ -629,6 +709,35 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
629
709
  /** Side effects of a routed reply: cancel-button lifecycle, logs, SSE, chat log. */
630
710
  private afterReplyRouted;
631
711
  private handleScheduleTrigger;
712
+ /**
713
+ * The adapter that can actually post into a chat, found by its group.
714
+ *
715
+ * A schedule records where it was created (reply_chat_id) separately from
716
+ * what it triggers (target). Those need not share a platform: a Telegram
717
+ * group can schedule a Discord-topic instance. Picking the adapter from the
718
+ * target then sends a Telegram chat id through the Discord bot, which fails
719
+ * with Unknown Channel — the source topic never hears that its schedule ran.
720
+ *
721
+ * When several bots share one guild, the primary wins: a persona should not
722
+ * be the voice announcing fleet scheduling.
723
+ */
724
+ private adapterForChat;
725
+ /**
726
+ * The adapter that can answer a schedule in the chat it was created from.
727
+ *
728
+ * A schedule records its creator (source) and its trigger (target) separately,
729
+ * and they need not share a platform — the live fleet has a Telegram group
730
+ * scheduling a Discord-topic instance. Routing by target sends a Telegram chat
731
+ * id through the Discord bot, which is one half of the Unknown Channel errors.
732
+ *
733
+ * The creator's own adapter comes first, and only when its world actually owns
734
+ * that chat. Classic keeps its own identity, so a schedule made from a
735
+ * persona-bound Classic channel is answered by that persona: the primary bot
736
+ * may not even have access there, and would be the wrong voice if it did.
737
+ * Falling back to the target's adapter is deliberately NOT an option — that is
738
+ * the misroute itself; callers say why they stayed silent instead.
739
+ */
740
+ private scheduleSourceAdapter;
632
741
  private notifySourceTopic;
633
742
  private notifyScheduleFailure;
634
743
  private handleScheduleCrud;
@@ -655,8 +764,31 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
655
764
  private sessionPruneTimer;
656
765
  private classicReloadTimer;
657
766
  private botUserId;
658
- /** Periodically check if bound topics still exist */
767
+ /** Periodically check if bound topics still exist. Never deletes user data. */
659
768
  private startTopicCleanupPoller;
769
+ /** Coalesce timer ticks; an outage must not create overlapping destructive-looking scans. */
770
+ private scheduleTopicCleanup;
771
+ private confirmedProbeFence;
772
+ private sameProbeFence;
773
+ private warnTopicProbeUnknown;
774
+ /** One fixed-snapshot topology pass. Automatic evidence can only quarantine. */
775
+ private runTopicCleanup;
776
+ /**
777
+ * A gateway event is only a hint. Confirm it through the passive REST probe;
778
+ * reconnecting gateways have emitted false channelDelete events in practice.
779
+ */
780
+ private handleProviderTopicClosed;
781
+ private bindTopicClosedHandler;
782
+ /**
783
+ * Remove only the volatile route. The instance config, daemon, schedules,
784
+ * teams, metadata directory, and working tree remain untouched until an
785
+ * authenticated explicit deletion is requested.
786
+ */
787
+ quarantineMissingTopic(threadId: string, target: RouteTarget, evidence: {
788
+ source: "provider-event" | "provider-probe";
789
+ adapterId?: string;
790
+ generation?: number;
791
+ }): void;
660
792
  /**
661
793
  * Patch only values changed in the effective config into the original YAML
662
794
  * document. Unknown keys and comments remain untouched; redundant
@@ -667,7 +799,7 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
667
799
  private slimFleetConfigAtStartup;
668
800
  private writeFleetConfigBackup;
669
801
  private patchFleetDocument;
670
- removeInstance(name: string): Promise<void>;
802
+ removeInstance(name: string, authorization: ExplicitInstanceRemoval): Promise<void>;
671
803
  startStatuslineWatcher(name: string): void;
672
804
  stopStatuslineWatcher(name: string): void;
673
805
  reactMessageStatus(instanceName: string, chatId: string, messageId: string, emoji: string): void;
@@ -744,6 +876,31 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
744
876
  * poll cannot flood the topic and a down General does not swallow it.
745
877
  */
746
878
  private reportClassicUnrecoverableIds;
879
+ /**
880
+ * Where a fleet-wide notice can actually be posted, or null if nowhere.
881
+ *
882
+ * On Telegram a group id is itself a chat, so posting straight to it is
883
+ * right. On Discord it is a *guild* id, and sending there makes the adapter
884
+ * fetch a channel that does not exist — DiscordAPIError 10003 Unknown
885
+ * Channel. That is why the daily summary never arrived on a Discord fleet:
886
+ * it had been posting to the guild every night and only the catch handler
887
+ * ever saw it.
888
+ *
889
+ * Discord therefore needs a real channel: the General topic (resolved from
890
+ * config rather than findGeneralInstance, so a fleet-level fault can still be
891
+ * reported while the General daemon is down), else the adapter's configured
892
+ * general_channel_id. With neither, there is no safe target and the caller
893
+ * should say so rather than send into a guaranteed failure.
894
+ */
895
+ private fleetNoticeTarget;
896
+ /**
897
+ * Post the daily summary where fleet-wide notices go.
898
+ *
899
+ * A named method rather than an inline closure so a test can drive the real
900
+ * thing: asserting on fleetNoticeTarget alone leaves the call site free to go
901
+ * back to posting at the bare group id, which is the defect this replaced.
902
+ */
903
+ private postDailySummary;
747
904
  notifyFleetError(text: string): void;
748
905
  private static readonly FLEET_ERROR_THROTTLE_MS;
749
906
  private fleetErrorNotices;
@@ -820,6 +977,17 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
820
977
  * contain usernames or secret-adjacent text and stays in daemon.log.
821
978
  */
822
979
  notifyInteractivePrompt(instanceName: string, kind: string): Promise<void>;
980
+ /**
981
+ * Offer a one-tap re-login next to an auth alert.
982
+ *
983
+ * The alert already names the remedy in words (`/login <backend>`), which
984
+ * still leaves the user to retype it somewhere. The button routes into the
985
+ * same chooser `/login` uses, so pressing it starts the flow in place.
986
+ *
987
+ * Backends with no remote login flow (opencode logs in from a terminal) get
988
+ * no button — the alert's own wording already tells them what to run.
989
+ */
990
+ offerBackendLogin(targetInstance: string, backend: string): Promise<void>;
823
991
  /** Consume a General assist button exactly once. */
824
992
  private handleInteractivePromptAssist;
825
993
  /** Send a "🛑 Cancel" button to the instance's topic/channel after delivery. */
@@ -1080,6 +1248,17 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
1080
1248
  * only re-reads credentials on process start (a paused instance's CLI is
1081
1249
  * already dead, so waking it respawns with the new token for free).
1082
1250
  */
1251
+ /**
1252
+ * Wake/restart every instance of a backend after a successful re-login.
1253
+ *
1254
+ * Bounded by a wall-clock deadline. This used to be an unbounded sequential
1255
+ * loop, and the caller only built its "login completed" message AFTER it
1256
+ * returned — so with several instances (or one slow restart) the user was
1257
+ * told nothing at all for minutes, concluded the login had hung, and
1258
+ * restarted things by hand. Instances that do not finish in time are NOT
1259
+ * cancelled: they are still coming back, and are reported as pending so the
1260
+ * message can say so instead of implying failure.
1261
+ */
1083
1262
  private recoverBackendInstances;
1084
1263
  /** Backend chooser button → start that backend's login session. */
1085
1264
  private handleLoginBackendSelect;
@@ -1187,6 +1366,14 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
1187
1366
  private cliEnvPath;
1188
1367
  /** Read the cached CLI env for a backend, or null if missing/stale/unparseable. */
1189
1368
  private readCliEnv;
1369
+ /** True when a cached CLI env is old enough that `/model` should re-probe. */
1370
+ private cliEnvNeedsRefresh;
1371
+ /**
1372
+ * Run a live probe under a deadline, falling back to whatever the cache holds.
1373
+ * A model list is an aid: a vendor that stops answering must degrade to the
1374
+ * previous list, never stall the command that asked for it.
1375
+ */
1376
+ private probeBackendBounded;
1190
1377
  /**
1191
1378
  * Resolve the effective model for a fleet or ClassicBot instance, plus where it
1192
1379
  * came from. Single source of truth for `/model` and `/ctx` — precedence: