@songsid/agend 2.1.5-beta.9 → 2.1.6-beta.1

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 (86) hide show
  1. package/dist/backend/claude-code.d.ts +12 -0
  2. package/dist/backend/claude-code.js +12 -0
  3. package/dist/backend/claude-code.js.map +1 -1
  4. package/dist/backend/codex.d.ts +41 -1
  5. package/dist/backend/codex.js +126 -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 +61 -0
  10. package/dist/backend/types.js.map +1 -1
  11. package/dist/channel/adapters/discord.d.ts +17 -2
  12. package/dist/channel/adapters/discord.js +64 -5
  13. package/dist/channel/adapters/discord.js.map +1 -1
  14. package/dist/channel/adapters/telegram.d.ts +41 -2
  15. package/dist/channel/adapters/telegram.js +116 -20
  16. package/dist/channel/adapters/telegram.js.map +1 -1
  17. package/dist/channel/factory.js +6 -1
  18. package/dist/channel/factory.js.map +1 -1
  19. package/dist/channel/types.d.ts +33 -1
  20. package/dist/classic-channel-manager.d.ts +23 -3
  21. package/dist/classic-channel-manager.js +35 -0
  22. package/dist/classic-channel-manager.js.map +1 -1
  23. package/dist/config-validator.js +23 -0
  24. package/dist/config-validator.js.map +1 -1
  25. package/dist/config.d.ts +4 -0
  26. package/dist/config.js +5 -0
  27. package/dist/config.js.map +1 -1
  28. package/dist/daemon.d.ts +158 -21
  29. package/dist/daemon.js +1099 -200
  30. package/dist/daemon.js.map +1 -1
  31. package/dist/deadline.d.ts +23 -0
  32. package/dist/deadline.js +29 -0
  33. package/dist/deadline.js.map +1 -0
  34. package/dist/fleet-context.d.ts +15 -1
  35. package/dist/fleet-context.js.map +1 -1
  36. package/dist/fleet-manager.d.ts +186 -3
  37. package/dist/fleet-manager.js +833 -119
  38. package/dist/fleet-manager.js.map +1 -1
  39. package/dist/general-knowledge/skills/fleet-config/SKILL.md +26 -2
  40. package/dist/instance-lifecycle.d.ts +8 -2
  41. package/dist/instance-lifecycle.js +47 -3
  42. package/dist/instance-lifecycle.js.map +1 -1
  43. package/dist/instance-removal.d.ts +13 -0
  44. package/dist/instance-removal.js +14 -0
  45. package/dist/instance-removal.js.map +1 -0
  46. package/dist/instructions.js +2 -2
  47. package/dist/instructions.js.map +1 -1
  48. package/dist/locale.js +28 -2
  49. package/dist/locale.js.map +1 -1
  50. package/dist/login-controller.d.ts +31 -4
  51. package/dist/login-controller.js +52 -4
  52. package/dist/login-controller.js.map +1 -1
  53. package/dist/login-flows.d.ts +29 -0
  54. package/dist/login-flows.js +48 -1
  55. package/dist/login-flows.js.map +1 -1
  56. package/dist/outbound-handlers.d.ts +1 -0
  57. package/dist/outbound-handlers.js +118 -29
  58. package/dist/outbound-handlers.js.map +1 -1
  59. package/dist/outbound-schemas.d.ts +7 -0
  60. package/dist/outbound-schemas.js +3 -0
  61. package/dist/outbound-schemas.js.map +1 -1
  62. package/dist/pane-input-residue.d.ts +25 -2
  63. package/dist/pane-input-residue.js +31 -2
  64. package/dist/pane-input-residue.js.map +1 -1
  65. package/dist/restart-progress.js +3 -21
  66. package/dist/restart-progress.js.map +1 -1
  67. package/dist/settings-api.d.ts +1 -1
  68. package/dist/settings-api.js +38 -6
  69. package/dist/settings-api.js.map +1 -1
  70. package/dist/topic-commands.d.ts +27 -2
  71. package/dist/topic-commands.js +37 -6
  72. package/dist/topic-commands.js.map +1 -1
  73. package/dist/turn-reply-guard.d.ts +41 -0
  74. package/dist/turn-reply-guard.js +77 -0
  75. package/dist/turn-reply-guard.js.map +1 -0
  76. package/dist/types.d.ts +7 -0
  77. package/dist/ui/dashboard.html +38 -10
  78. package/dist/ui/settings.html +93 -15
  79. package/dist/ui/view.html +35 -20
  80. package/dist/view-api.d.ts +1 -0
  81. package/dist/view-api.js +12 -2
  82. package/dist/view-api.js.map +1 -1
  83. package/dist/web-api.d.ts +22 -1
  84. package/dist/web-api.js +25 -1
  85. package/dist/web-api.js.map +1 -1
  86. 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;
@@ -153,6 +160,13 @@ export interface FleetContext {
153
160
  cancelInstallSession?(): Promise<string>;
154
161
  /** Human-readable effective model for an instance (resolves inherited defaults). */
155
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
+ };
156
170
  /**
157
171
  * Show a model-selection inline keyboard for the given instance in a TG topic.
158
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,7 @@ 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:";
69
71
  /**
70
72
  * Upper bound on a live probe driven by `/model`.
71
73
  *
@@ -88,6 +90,15 @@ export interface DeliveryOptions {
88
90
  * The wait is announced before it starts, so it reads as progress, not a stall.
89
91
  */
90
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;
91
102
  export declare class FleetManager implements FleetContext, LifecycleContext, ArchiverContext, StatuslineWatcherContext, OutboundContext, AgentEndpointContext {
92
103
  dataDir: string;
93
104
  private static signalTarget;
@@ -124,6 +135,20 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
124
135
  private reloadPending;
125
136
  /** A running reconciliation; only one may mutate lifecycle/config state at a time. */
126
137
  private reconcileInFlight;
138
+ /** Topology checks are serialized separately from config reconciliation. */
139
+ private topicCleanupInFlight;
140
+ private topicCleanupGeneration;
141
+ private topicProbeWarnings;
142
+ /**
143
+ * Consecutive unknown probe results per route (or per adapter for outage
144
+ * class reasons). A single transient never reaches the operator; only a
145
+ * streak of TOPIC_PROBE_UNKNOWN_ESCALATION does.
146
+ */
147
+ private topicProbeUnknownStreak;
148
+ /** Unknown results in a row before the operator is told. 3 × 5 min poller = 15 min. */
149
+ static readonly TOPIC_PROBE_UNKNOWN_ESCALATION = 3;
150
+ /** Reasons that describe the adapter, not one topic — counted once per adapter. */
151
+ private static readonly TOPIC_PROBE_ADAPTER_SCOPED_REASONS;
127
152
  logger: Logger;
128
153
  private topicCommands;
129
154
  sessionRegistry: Map<string, string>;
@@ -156,6 +181,10 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
156
181
  private instanceIdleWaiters;
157
182
  private lastInboundUser;
158
183
  private cancelButtons;
184
+ /** Latest publication generation per instance. Late results from older
185
+ * generations are retired without touching the current button. */
186
+ private cancelButtonPublications;
187
+ private nextCancelButtonPublicationGeneration;
159
188
  /** Pending idle-edge retirement, one timer per instance. */
160
189
  private cancelButtonIdleRetireTimers;
161
190
  /** Duplicate-reply suppression across both the MCP and HTTP reply paths. */
@@ -321,6 +350,26 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
321
350
  getWorldForInstance(name: string): AdapterWorld | undefined;
322
351
  /** Get channel config for a specific adapter (by id), falling back to primary */
323
352
  getChannelConfig(adapterId?: string): import("./types.js").ChannelConfig | undefined;
353
+ /**
354
+ * The configured world that owns `chatId`, when that is provably NOT the
355
+ * world `target` lives in. Returns undefined when they agree, when there is
356
+ * nothing to check, or when no configured channel claims the id.
357
+ *
358
+ * Deliberately one-sided: only a POSITIVE match against another channel's
359
+ * group id counts as foreign. A chat id that matches nothing may still be
360
+ * legitimate for this world (a classic channel, a DM), and treating
361
+ * "unrecognised" as "wrong" would stop seeding for cases that work today.
362
+ *
363
+ * Read from config rather than the live worlds map on purpose: a channel
364
+ * whose adapter failed to start still owns its group id, and the coordinates
365
+ * are just as unusable by the target's adapter either way.
366
+ *
367
+ * This is the other half of what scheduleSourceAdapter fixed. That one stops
368
+ * the trigger NOTICE being sent through the wrong bot; this one stops the
369
+ * same coordinates being planted as the target instance's reply context,
370
+ * which is what made its own replies fail until someone spoke to it (#752).
371
+ */
372
+ private scheduleChatWorldMismatch;
324
373
  /** Get the group_id for an instance's bound adapter */
325
374
  getGroupIdForInstance(name: string): string;
326
375
  /** Configured primary adapter id. Never infer this from Map insertion order. */
@@ -402,8 +451,14 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
402
451
  /** Fleet admin is an explicit config allowlist entry, not merely an open/paired user. */
403
452
  isFleetAdmin(userId: string, adapterId?: string): boolean;
404
453
  changeInstancePauseState(name: string, action: "pause" | "wake"): Promise<"paused" | "awake" | "not_idle">;
454
+ /** Deliver an already-resolved hot snapshot without depending on IPC timing. */
455
+ private applyHotConfigUpdate;
456
+ private classicBehaviorUpdate;
405
457
  /** Apply a Settings edit to a ClassicBot channel without waiting for the poller. */
406
- restartClassicInstanceFromSettings(instanceName: string): Promise<void>;
458
+ restartClassicInstanceFromSettings(instanceName: string, changedFields?: string[]): Promise<void>;
459
+ /** Reload classicBot.yaml once. Kept callable so the periodic production
460
+ * path is covered without relying on fake timers around startAll(). */
461
+ private reloadClassicConfigFromDisk;
407
462
  startInstance(name: string, config: InstanceConfig, topicMode: boolean, kind?: "fleet-topic" | "classic",
408
463
  /**
409
464
  * Explicit starts (CLI/API) may resume a paused or failed daemon. Startup
@@ -674,6 +729,35 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
674
729
  /** Side effects of a routed reply: cancel-button lifecycle, logs, SSE, chat log. */
675
730
  private afterReplyRouted;
676
731
  private handleScheduleTrigger;
732
+ /**
733
+ * The adapter that can actually post into a chat, found by its group.
734
+ *
735
+ * A schedule records where it was created (reply_chat_id) separately from
736
+ * what it triggers (target). Those need not share a platform: a Telegram
737
+ * group can schedule a Discord-topic instance. Picking the adapter from the
738
+ * target then sends a Telegram chat id through the Discord bot, which fails
739
+ * with Unknown Channel — the source topic never hears that its schedule ran.
740
+ *
741
+ * When several bots share one guild, the primary wins: a persona should not
742
+ * be the voice announcing fleet scheduling.
743
+ */
744
+ private adapterForChat;
745
+ /**
746
+ * The adapter that can answer a schedule in the chat it was created from.
747
+ *
748
+ * A schedule records its creator (source) and its trigger (target) separately,
749
+ * and they need not share a platform — the live fleet has a Telegram group
750
+ * scheduling a Discord-topic instance. Routing by target sends a Telegram chat
751
+ * id through the Discord bot, which is one half of the Unknown Channel errors.
752
+ *
753
+ * The creator's own adapter comes first, and only when its world actually owns
754
+ * that chat. Classic keeps its own identity, so a schedule made from a
755
+ * persona-bound Classic channel is answered by that persona: the primary bot
756
+ * may not even have access there, and would be the wrong voice if it did.
757
+ * Falling back to the target's adapter is deliberately NOT an option — that is
758
+ * the misroute itself; callers say why they stayed silent instead.
759
+ */
760
+ private scheduleSourceAdapter;
677
761
  private notifySourceTopic;
678
762
  private notifyScheduleFailure;
679
763
  private handleScheduleCrud;
@@ -700,8 +784,56 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
700
784
  private sessionPruneTimer;
701
785
  private classicReloadTimer;
702
786
  private botUserId;
703
- /** Periodically check if bound topics still exist */
787
+ /** Periodically check if bound topics still exist. Never deletes user data. */
704
788
  private startTopicCleanupPoller;
789
+ /** Coalesce timer ticks; an outage must not create overlapping destructive-looking scans. */
790
+ private scheduleTopicCleanup;
791
+ private confirmedProbeFence;
792
+ private sameProbeFence;
793
+ private topicProbeStreakKey;
794
+ /** A definite answer (present or missing) ends the unknown streak for that route and its adapter. */
795
+ private clearTopicProbeUnknownStreak;
796
+ /**
797
+ * Record one unknown probe result. Nothing here can touch quarantine or
798
+ * removal: unknown is always retained data. The only question is whether
799
+ * the operator hears about it, and a single transient (one flaky HTTP call
800
+ * out of dozens per pass) must not — only a streak does.
801
+ *
802
+ * Used directly for single-route events (channelDelete, the pre-action
803
+ * fence). The periodic scan goes through a TopicProbePass instead, so one
804
+ * pass over N routes of a dead adapter counts as ONE check, not N.
805
+ */
806
+ private warnTopicProbeUnknown;
807
+ /** One scan's worth of probe outcomes, applied to the streaks after the loop. */
808
+ private newTopicProbePass;
809
+ private passTopicProbeUnknown;
810
+ private passTopicProbeDefinite;
811
+ /**
812
+ * Apply a pass: a definite answer resets its keys, and an adapter that
813
+ * answered for any route this pass is evidently alive, so an unknown for the
814
+ * same adapter key in the same pass does not count — regardless of the order
815
+ * the routes happened to be probed in.
816
+ */
817
+ private applyTopicProbePass;
818
+ private noteTopicProbeUnknown;
819
+ /** One fixed-snapshot topology pass. Automatic evidence can only quarantine. */
820
+ private runTopicCleanup;
821
+ /**
822
+ * A gateway event is only a hint. Confirm it through the passive REST probe;
823
+ * reconnecting gateways have emitted false channelDelete events in practice.
824
+ */
825
+ private handleProviderTopicClosed;
826
+ private bindTopicClosedHandler;
827
+ /**
828
+ * Remove only the volatile route. The instance config, daemon, schedules,
829
+ * teams, metadata directory, and working tree remain untouched until an
830
+ * authenticated explicit deletion is requested.
831
+ */
832
+ quarantineMissingTopic(threadId: string, target: RouteTarget, evidence: {
833
+ source: "provider-event" | "provider-probe";
834
+ adapterId?: string;
835
+ generation?: number;
836
+ }): void;
705
837
  /**
706
838
  * Patch only values changed in the effective config into the original YAML
707
839
  * document. Unknown keys and comments remain untouched; redundant
@@ -712,7 +844,7 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
712
844
  private slimFleetConfigAtStartup;
713
845
  private writeFleetConfigBackup;
714
846
  private patchFleetDocument;
715
- removeInstance(name: string): Promise<void>;
847
+ removeInstance(name: string, authorization: ExplicitInstanceRemoval): Promise<void>;
716
848
  startStatuslineWatcher(name: string): void;
717
849
  stopStatuslineWatcher(name: string): void;
718
850
  reactMessageStatus(instanceName: string, chatId: string, messageId: string, emoji: string): void;
@@ -789,6 +921,31 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
789
921
  * poll cannot flood the topic and a down General does not swallow it.
790
922
  */
791
923
  private reportClassicUnrecoverableIds;
924
+ /**
925
+ * Where a fleet-wide notice can actually be posted, or null if nowhere.
926
+ *
927
+ * On Telegram a group id is itself a chat, so posting straight to it is
928
+ * right. On Discord it is a *guild* id, and sending there makes the adapter
929
+ * fetch a channel that does not exist — DiscordAPIError 10003 Unknown
930
+ * Channel. That is why the daily summary never arrived on a Discord fleet:
931
+ * it had been posting to the guild every night and only the catch handler
932
+ * ever saw it.
933
+ *
934
+ * Discord therefore needs a real channel: the General topic (resolved from
935
+ * config rather than findGeneralInstance, so a fleet-level fault can still be
936
+ * reported while the General daemon is down), else the adapter's configured
937
+ * general_channel_id. With neither, there is no safe target and the caller
938
+ * should say so rather than send into a guaranteed failure.
939
+ */
940
+ private fleetNoticeTarget;
941
+ /**
942
+ * Post the daily summary where fleet-wide notices go.
943
+ *
944
+ * A named method rather than an inline closure so a test can drive the real
945
+ * thing: asserting on fleetNoticeTarget alone leaves the call site free to go
946
+ * back to posting at the bare group id, which is the defect this replaced.
947
+ */
948
+ private postDailySummary;
792
949
  notifyFleetError(text: string): void;
793
950
  private static readonly FLEET_ERROR_THROTTLE_MS;
794
951
  private fleetErrorNotices;
@@ -865,6 +1022,17 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
865
1022
  * contain usernames or secret-adjacent text and stays in daemon.log.
866
1023
  */
867
1024
  notifyInteractivePrompt(instanceName: string, kind: string): Promise<void>;
1025
+ /**
1026
+ * Offer a one-tap re-login next to an auth alert.
1027
+ *
1028
+ * The alert already names the remedy in words (`/login <backend>`), which
1029
+ * still leaves the user to retype it somewhere. The button routes into the
1030
+ * same chooser `/login` uses, so pressing it starts the flow in place.
1031
+ *
1032
+ * Backends with no remote login flow (opencode logs in from a terminal) get
1033
+ * no button — the alert's own wording already tells them what to run.
1034
+ */
1035
+ offerBackendLogin(targetInstance: string, backend: string): Promise<void>;
868
1036
  /** Consume a General assist button exactly once. */
869
1037
  private handleInteractivePromptAssist;
870
1038
  /** Send a "🛑 Cancel" button to the instance's topic/channel after delivery. */
@@ -892,6 +1060,10 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
892
1060
  */
893
1061
  private handleCancelClick;
894
1062
  private hasCancelButton;
1063
+ private beginCancelButtonPublication;
1064
+ /** Remember a clear/cancel/idle decision that arrived before notifyAlert
1065
+ * returned a message id. `expected` fences an idle timer to its generation. */
1066
+ private markCancelButtonPublicationForRetirement;
895
1067
  sendCancelButton(instanceName: string, correlationId?: string, preserveProgress?: boolean): Promise<void>;
896
1068
  /**
897
1069
  * The cancel button's text for a given elapsed time.
@@ -1125,6 +1297,17 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
1125
1297
  * only re-reads credentials on process start (a paused instance's CLI is
1126
1298
  * already dead, so waking it respawns with the new token for free).
1127
1299
  */
1300
+ /**
1301
+ * Wake/restart every instance of a backend after a successful re-login.
1302
+ *
1303
+ * Bounded by a wall-clock deadline. This used to be an unbounded sequential
1304
+ * loop, and the caller only built its "login completed" message AFTER it
1305
+ * returned — so with several instances (or one slow restart) the user was
1306
+ * told nothing at all for minutes, concluded the login had hung, and
1307
+ * restarted things by hand. Instances that do not finish in time are NOT
1308
+ * cancelled: they are still coming back, and are reported as pending so the
1309
+ * message can say so instead of implying failure.
1310
+ */
1128
1311
  private recoverBackendInstances;
1129
1312
  /** Backend chooser button → start that backend's login session. */
1130
1313
  private handleLoginBackendSelect;