@songsid/agend 2.1.8-beta.9 → 2.1.9

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 (56) hide show
  1. package/dist/backend/claude-code.d.ts +35 -5
  2. package/dist/backend/claude-code.js +288 -17
  3. package/dist/backend/claude-code.js.map +1 -1
  4. package/dist/channel/mcp-tools.js +1 -1
  5. package/dist/channel/mcp-tools.js.map +1 -1
  6. package/dist/cli.js +16 -6
  7. package/dist/cli.js.map +1 -1
  8. package/dist/config-validator.js +11 -0
  9. package/dist/config-validator.js.map +1 -1
  10. package/dist/daemon.d.ts +47 -1
  11. package/dist/daemon.js +136 -17
  12. package/dist/daemon.js.map +1 -1
  13. package/dist/delivery-outbox.d.ts +9 -1
  14. package/dist/delivery-outbox.js +25 -4
  15. package/dist/delivery-outbox.js.map +1 -1
  16. package/dist/fleet-manager.d.ts +89 -4
  17. package/dist/fleet-manager.js +361 -53
  18. package/dist/fleet-manager.js.map +1 -1
  19. package/dist/general-knowledge/skills/persona-emoji/SKILL.md +5 -2
  20. package/dist/instance-lifecycle.d.ts +78 -5
  21. package/dist/instance-lifecycle.js +187 -14
  22. package/dist/instance-lifecycle.js.map +1 -1
  23. package/dist/login-flows.d.ts +15 -0
  24. package/dist/login-flows.js +4 -1
  25. package/dist/login-flows.js.map +1 -1
  26. package/dist/outbound-handlers.d.ts +3 -0
  27. package/dist/outbound-handlers.js +3 -3
  28. package/dist/outbound-handlers.js.map +1 -1
  29. package/dist/outbound-schemas.d.ts +2 -0
  30. package/dist/outbound-schemas.js +1 -1
  31. package/dist/outbound-schemas.js.map +1 -1
  32. package/dist/pause-marker.d.ts +10 -1
  33. package/dist/pause-marker.js +26 -2
  34. package/dist/pause-marker.js.map +1 -1
  35. package/dist/settings-api.d.ts +2 -0
  36. package/dist/settings-api.js +3 -1
  37. package/dist/settings-api.js.map +1 -1
  38. package/dist/status-emojis.d.ts +9 -1
  39. package/dist/status-emojis.js +11 -1
  40. package/dist/status-emojis.js.map +1 -1
  41. package/dist/target-queue-worker.d.ts +67 -0
  42. package/dist/target-queue-worker.js +76 -0
  43. package/dist/target-queue-worker.js.map +1 -0
  44. package/dist/tool-permissions.js +7 -2
  45. package/dist/tool-permissions.js.map +1 -1
  46. package/dist/types.d.ts +26 -1
  47. package/dist/types.js +9 -1
  48. package/dist/types.js.map +1 -1
  49. package/dist/ui/settings.html +2 -2
  50. package/dist/wake-coordinator.d.ts +143 -0
  51. package/dist/wake-coordinator.js +321 -0
  52. package/dist/wake-coordinator.js.map +1 -0
  53. package/dist/web-api.d.ts +3 -1
  54. package/dist/web-api.js +1 -1
  55. package/dist/web-api.js.map +1 -1
  56. package/package.json +1 -1
@@ -263,7 +263,7 @@
263
263
  description: "Description", systemPrompt: "System prompt", tags: "Tags", generalTopicField: "General topic (fleet dispatcher)",
264
264
  runtimeResources: "Runtime & Resources", autoPause: "Auto-pause after", minutes: "minutes", inheritDefault: "Inherit from defaults",
265
265
  statusEmojis: "Status emojis", statusEmojisInstance: "This agent's status emojis (override the connection's)", statusEmojisChannel: "Status emojis for this connection's bots",
266
- se_received: "Received", se_queued: "Queued", se_processing: "Processing", se_delivered: "Delivered", se_failed: "Failed", se_progress_prefix: "Progress prefix",
266
+ se_received: "Received", se_queued: "Queued", se_processing: "Processing", se_delivered: "Delivered", se_failed: "Failed", se_progress_prefix: "Progress prefix", se_photo: "Photo saved", se_attachment: "File saved",
267
267
  se_src_instance: "agent", se_src_platform: "connection", se_src_builtin: "default", se_pick: "Pick…", se_useDefault: "Use the default", se_fallback: "the default is used instead",
268
268
  se_unicode: "Emoji", se_telegramOnly: "Telegram allows only these reactions", se_serverEmojis: "Server emojis", se_noServerEmojis: "No custom emojis on this server.", se_primaryServer: "primary", se_externalEmojiHint: "Another server's emoji needs the bot's Use External Emojis permission in the channel it reacts in.", se_unavailable: "unavailable",
269
269
  se_hint: "Empty keeps the default. The preview is resolved by AgEnD exactly as the bot will react.",
@@ -315,7 +315,7 @@
315
315
  },
316
316
  "zh-TW": {
317
317
  statusEmojis: "狀態 emoji", statusEmojisInstance: "此 Agent 的狀態 emoji(覆蓋連線設定)", statusEmojisChannel: "此連線 bot 的狀態 emoji",
318
- se_received: "已收到", se_queued: "排隊中", se_processing: "處理中", se_delivered: "已送達", se_failed: "失敗", se_progress_prefix: "進度前綴",
318
+ se_received: "已收到", se_queued: "排隊中", se_processing: "處理中", se_delivered: "已送達", se_failed: "失敗", se_progress_prefix: "進度前綴", se_photo: "已存圖片", se_attachment: "已存檔案",
319
319
  se_src_instance: "Agent", se_src_platform: "連線", se_src_builtin: "預設", se_pick: "選擇…", se_useDefault: "使用預設", se_fallback: "改用預設值",
320
320
  se_unicode: "Emoji", se_telegramOnly: "Telegram 只允許這些反應", se_serverEmojis: "伺服器 emoji", se_noServerEmojis: "此伺服器沒有自訂 emoji。", se_primaryServer: "主伺服器", se_externalEmojiHint: "使用其他伺服器的 emoji,bot 在要加反應的頻道需有「使用外部表情符號」權限。", se_unavailable: "無法使用",
321
321
  se_hint: "留空即使用預設值。預覽由 AgEnD 依 bot 實際 react 的方式解析。",
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Phase 2b (docs/design/phase2-submit-contract.md §1.2–§1.6, §3.1): the one
3
+ * owner that wakes a paused target because durable work is waiting for it.
4
+ *
5
+ * Before this, the only code that woke a target for a queued cross-instance
6
+ * message ran *after* the outbox pump had claimed the row, and the pump never
7
+ * claims a target without a live daemon — so a marker-only paused instance
8
+ * (paused across a fleet restart) was never woken: Bug1, the attempt_0
9
+ * deadlock. The coordinator wakes first; the pump stays the only claimer and
10
+ * only claims targets that are awake. Nothing here writes a pane or claims a
11
+ * row.
12
+ *
13
+ * Active only for targets whose `delivery_worker` is `wake_only` or `on`;
14
+ * `off` (the default) leaves every path as it was.
15
+ */
16
+ import type { DeliveryWorkerMode } from "./types.js";
17
+ import type { OutboxDelivery } from "./delivery-outbox.js";
18
+ import type { PauseReason } from "./pause-marker.js";
19
+ /** Row kinds that never warrant waking a target. Empty: every current kind asks for action (§1.2). */
20
+ export declare const PASSIVE_KINDS: ReadonlySet<string>;
21
+ export declare const isWakeEligibleKind: (kind: string) => boolean;
22
+ export declare const WAKE_BACKOFF_BASE_MS = 1000;
23
+ export declare const WAKE_BACKOFF_MAX_MS = 60000;
24
+ /** Consecutive wake failures before both topics are told, once per failure episode. */
25
+ export declare const WAKE_FAILURE_NOTICE_AFTER = 3;
26
+ /** Pre-claim lease length (mirrors instance-lifecycle PRECLAIM_LEASE_MS). */
27
+ export declare const PRECLAIM_WINDOW_MS = 120000;
28
+ export declare const PARK_BASE_MS = 60000;
29
+ export declare const PARK_MAX_MS: number;
30
+ /** Queued work on an awake target that has not been claimed for this long is reported once (§1.4). */
31
+ export declare const CLAIM_BLOCK_NOTICE_MS: number;
32
+ /** How far `warm_cap` may be exceeded for leased work (§1.6). */
33
+ export declare const DEFAULT_WARM_OVERFLOW = 2;
34
+ export declare const WATCHDOG_INTERVAL_MS = 30000;
35
+ export interface WakeCoordinatorDeps {
36
+ mode(target: string): DeliveryWorkerMode;
37
+ /**
38
+ * Whether the outbox can be read now. False once its database is closed
39
+ * (fleet shutdown, a test tearing down): the coordinator then does nothing,
40
+ * and stops itself, instead of querying a closed connection.
41
+ */
42
+ available(): boolean;
43
+ /** Rows still to deliver: queued, retry_wait, delivering, submission_started (created_seq order). */
44
+ listPending(): OutboxDelivery[];
45
+ isPaused(target: string): boolean;
46
+ pauseReason(target: string): PauseReason | null;
47
+ isRestarting(target: string): boolean;
48
+ /** The single-flight lifecycle wake, marked as the coordinator's own. */
49
+ wake(target: string): Promise<void>;
50
+ /** Instances counted against warm_cap right now (running, not paused, not general). */
51
+ residentNames(): string[];
52
+ /** warm_cap; 0 or less means unlimited. */
53
+ warmCap(): number;
54
+ warmOverflow(): number;
55
+ /** When the target was last woken or started for work (the pre-claim clock). */
56
+ wokeAt(target: string): number | undefined;
57
+ /** Why an awake target is not accepting input (dialog, auth hold…), or null. */
58
+ notAcceptingReason(target: string): string | null;
59
+ notifyTarget(target: string, text: string): void;
60
+ /**
61
+ * Run the outbox pump. A wake changes no outbox row, so nothing else would
62
+ * tell the pump that the target it skipped while paused can be claimed now.
63
+ */
64
+ kickPump(): void;
65
+ notifySender(source: string, text: string): void;
66
+ now(): number;
67
+ logger: {
68
+ info(obj: object, msg: string): void;
69
+ warn(obj: object, msg: string): void;
70
+ debug?(obj: object, msg: string): void;
71
+ };
72
+ }
73
+ export declare class WakeCoordinator {
74
+ private readonly deps;
75
+ private readonly states;
76
+ /** Targets whose wake is in flight (a reservation each, counted against the hard cap). */
77
+ private readonly reserved;
78
+ private scanTimer;
79
+ private watchdog;
80
+ private stopped;
81
+ constructor(deps: WakeCoordinatorDeps);
82
+ start(): void;
83
+ stop(): void;
84
+ /** Schedule a scan soon (coalesced). Safe to call from any event. */
85
+ kick(delayMs?: number): void;
86
+ private state;
87
+ private active;
88
+ /**
89
+ * The pump must not claim this target (§3.1, §1.4): it is paused, being
90
+ * woken, or awake but not accepting input by the daemon's own account
91
+ * (a dialog, a login failure, stuck). Called from the pump's claim
92
+ * callback, in the same synchronous step that reads the daemon's bootId, so
93
+ * the verdict and the claim are about the same daemon generation. A busy
94
+ * (working) target is accepting: the manager's idle gate still waits it out.
95
+ */
96
+ blocksClaim(target: string): boolean;
97
+ isWaking(target: string): boolean;
98
+ /** The last wake error, while the target is failing to wake (for the expiry reason). */
99
+ wakeFailure(target: string): string | null;
100
+ isParked(target: string): boolean;
101
+ /**
102
+ * A wake that did not come from here (an operator's /wake or wake_instance,
103
+ * a user's channel message) *succeeded*. Park and backoff block only
104
+ * automatic wakes, so such a wake always gets its one attempt (§1.6); only
105
+ * its success lifts them. A failed attempt, or a refusal for capacity,
106
+ * leaves the park and backoff exactly as they were. If the hold persists
107
+ * after a successful wake, the park comes back on its next window with the
108
+ * level kept (it is reset only by a delivery).
109
+ */
110
+ noteExternalWake(target: string): void;
111
+ /** A row for `target` was delivered: the hold is over, and so are the backoffs. */
112
+ noteDelivered(target: string): void;
113
+ /** Free warm slots under the hard cap, counting every target exactly once. */
114
+ private freeSlots;
115
+ /** Synchronously take a slot for `target` (before any await). */
116
+ tryReserve(target: string): symbol | null;
117
+ /** Return exactly the slot `token` holds (idempotent). */
118
+ release(target: string, token: symbol): void;
119
+ get reservedCount(): number;
120
+ /**
121
+ * One pass over the pending queue: wake what needs waking, park what will
122
+ * not take work. Runs from timers, so it must never throw: an exception
123
+ * there is uncaught and takes the whole fleet process down (e.g. a scan
124
+ * landing after the outbox's database was closed). The next kick or the
125
+ * watchdog retries.
126
+ */
127
+ scan(): void;
128
+ private scanOnce;
129
+ /**
130
+ * An awake target with queued work: past its pre-claim window without the
131
+ * work moving, it is parked (no automatic re-wake until the hold clears or
132
+ * the park expires), and a long pre-claim block is reported once.
133
+ */
134
+ private checkAwakeTarget;
135
+ /**
136
+ * Whether a wake completion must do nothing but return its slot: the
137
+ * coordinator was stopped, or the outbox went away while the wake was in
138
+ * flight (the coordinator stops itself then). Checked before any side
139
+ * effect, so a late completion can never kick a pump at a closed database.
140
+ */
141
+ private fencedCompletion;
142
+ private startWake;
143
+ }
@@ -0,0 +1,321 @@
1
+ /** Row kinds that never warrant waking a target. Empty: every current kind asks for action (§1.2). */
2
+ export const PASSIVE_KINDS = new Set();
3
+ export const isWakeEligibleKind = (kind) => !PASSIVE_KINDS.has(kind);
4
+ export const WAKE_BACKOFF_BASE_MS = 1_000;
5
+ export const WAKE_BACKOFF_MAX_MS = 60_000;
6
+ /** Consecutive wake failures before both topics are told, once per failure episode. */
7
+ export const WAKE_FAILURE_NOTICE_AFTER = 3;
8
+ /** Pre-claim lease length (mirrors instance-lifecycle PRECLAIM_LEASE_MS). */
9
+ export const PRECLAIM_WINDOW_MS = 120_000;
10
+ export const PARK_BASE_MS = 60_000;
11
+ export const PARK_MAX_MS = 30 * 60_000;
12
+ /** Queued work on an awake target that has not been claimed for this long is reported once (§1.4). */
13
+ export const CLAIM_BLOCK_NOTICE_MS = 5 * 60_000;
14
+ /** How far `warm_cap` may be exceeded for leased work (§1.6). */
15
+ export const DEFAULT_WARM_OVERFLOW = 2;
16
+ export const WATCHDOG_INTERVAL_MS = 30_000;
17
+ export class WakeCoordinator {
18
+ deps;
19
+ states = new Map();
20
+ /** Targets whose wake is in flight (a reservation each, counted against the hard cap). */
21
+ reserved = new Map();
22
+ scanTimer = null;
23
+ watchdog = null;
24
+ stopped = false;
25
+ constructor(deps) {
26
+ this.deps = deps;
27
+ }
28
+ start() {
29
+ if (this.watchdog || this.stopped)
30
+ return;
31
+ this.watchdog = setInterval(() => this.scan(), WATCHDOG_INTERVAL_MS);
32
+ this.watchdog.unref?.();
33
+ this.kick();
34
+ }
35
+ stop() {
36
+ this.stopped = true;
37
+ if (this.watchdog)
38
+ clearInterval(this.watchdog);
39
+ if (this.scanTimer)
40
+ clearTimeout(this.scanTimer);
41
+ this.watchdog = null;
42
+ this.scanTimer = null;
43
+ }
44
+ /** Schedule a scan soon (coalesced). Safe to call from any event. */
45
+ kick(delayMs = 0) {
46
+ if (this.stopped)
47
+ return;
48
+ if (!this.deps.available()) {
49
+ this.stop();
50
+ return;
51
+ }
52
+ if (this.scanTimer) {
53
+ if (delayMs > 0)
54
+ return;
55
+ clearTimeout(this.scanTimer);
56
+ }
57
+ this.scanTimer = setTimeout(() => { this.scanTimer = null; this.scan(); }, Math.max(0, delayMs));
58
+ this.scanTimer.unref?.();
59
+ }
60
+ state(target) {
61
+ let s = this.states.get(target);
62
+ if (!s) {
63
+ s = {
64
+ failures: 0, backoffUntil: 0, lastError: null, failureNoticeSent: false,
65
+ parkedUntil: 0, parkLevel: 0, parkReason: null, authNoticeSent: false, claimBlockNoticeSent: false,
66
+ };
67
+ this.states.set(target, s);
68
+ }
69
+ return s;
70
+ }
71
+ active(target) {
72
+ return this.deps.mode(target) !== "off";
73
+ }
74
+ /**
75
+ * The pump must not claim this target (§3.1, §1.4): it is paused, being
76
+ * woken, or awake but not accepting input by the daemon's own account
77
+ * (a dialog, a login failure, stuck). Called from the pump's claim
78
+ * callback, in the same synchronous step that reads the daemon's bootId, so
79
+ * the verdict and the claim are about the same daemon generation. A busy
80
+ * (working) target is accepting: the manager's idle gate still waits it out.
81
+ */
82
+ blocksClaim(target) {
83
+ if (!this.active(target))
84
+ return false;
85
+ return this.reserved.has(target) || this.deps.isPaused(target) || this.deps.notAcceptingReason(target) !== null;
86
+ }
87
+ isWaking(target) {
88
+ return this.reserved.has(target);
89
+ }
90
+ /** The last wake error, while the target is failing to wake (for the expiry reason). */
91
+ wakeFailure(target) {
92
+ const s = this.states.get(target);
93
+ return s && s.failures > 0 ? s.lastError : null;
94
+ }
95
+ isParked(target) {
96
+ return (this.states.get(target)?.parkedUntil ?? 0) > this.deps.now();
97
+ }
98
+ /**
99
+ * A wake that did not come from here (an operator's /wake or wake_instance,
100
+ * a user's channel message) *succeeded*. Park and backoff block only
101
+ * automatic wakes, so such a wake always gets its one attempt (§1.6); only
102
+ * its success lifts them. A failed attempt, or a refusal for capacity,
103
+ * leaves the park and backoff exactly as they were. If the hold persists
104
+ * after a successful wake, the park comes back on its next window with the
105
+ * level kept (it is reset only by a delivery).
106
+ */
107
+ noteExternalWake(target) {
108
+ const s = this.states.get(target);
109
+ if (!s)
110
+ return;
111
+ s.parkedUntil = 0;
112
+ s.backoffUntil = 0;
113
+ s.authNoticeSent = false;
114
+ }
115
+ /** A row for `target` was delivered: the hold is over, and so are the backoffs. */
116
+ noteDelivered(target) {
117
+ const s = this.states.get(target);
118
+ if (!s)
119
+ return;
120
+ s.failures = 0;
121
+ s.lastError = null;
122
+ s.failureNoticeSent = false;
123
+ s.parkLevel = 0;
124
+ s.parkedUntil = 0;
125
+ s.parkReason = null;
126
+ s.claimBlockNoticeSent = false;
127
+ }
128
+ /** Free warm slots under the hard cap, counting every target exactly once. */
129
+ freeSlots() {
130
+ const cap = this.deps.warmCap();
131
+ if (!Number.isInteger(cap) || cap <= 0)
132
+ return Number.POSITIVE_INFINITY;
133
+ const hard = cap + Math.max(0, this.deps.warmOverflow());
134
+ // A reserved target is counted as reserved, never also as resident: its
135
+ // promotion (reservation released once it is running) cannot double-count.
136
+ const resident = this.deps.residentNames().filter(name => !this.reserved.has(name)).length;
137
+ return hard - resident - this.reserved.size;
138
+ }
139
+ /** Synchronously take a slot for `target` (before any await). */
140
+ tryReserve(target) {
141
+ if (this.reserved.has(target))
142
+ return null;
143
+ if (this.freeSlots() <= 0)
144
+ return null;
145
+ const token = Symbol(target);
146
+ this.reserved.set(target, token);
147
+ return token;
148
+ }
149
+ /** Return exactly the slot `token` holds (idempotent). */
150
+ release(target, token) {
151
+ if (this.reserved.get(target) === token)
152
+ this.reserved.delete(target);
153
+ }
154
+ get reservedCount() {
155
+ return this.reserved.size;
156
+ }
157
+ /**
158
+ * One pass over the pending queue: wake what needs waking, park what will
159
+ * not take work. Runs from timers, so it must never throw: an exception
160
+ * there is uncaught and takes the whole fleet process down (e.g. a scan
161
+ * landing after the outbox's database was closed). The next kick or the
162
+ * watchdog retries.
163
+ */
164
+ scan() {
165
+ if (this.stopped)
166
+ return;
167
+ if (!this.deps.available()) {
168
+ this.stop();
169
+ return;
170
+ }
171
+ try {
172
+ this.scanOnce();
173
+ }
174
+ catch (err) {
175
+ this.deps.logger.warn({ err: err instanceof Error ? err.message : String(err) }, "Wake coordinator scan failed; will retry");
176
+ }
177
+ }
178
+ scanOnce() {
179
+ const now = this.deps.now();
180
+ const byTarget = new Map();
181
+ for (const row of this.deps.listPending()) {
182
+ if (!this.active(row.targetInstance))
183
+ continue;
184
+ const list = byTarget.get(row.targetInstance) ?? [];
185
+ list.push(row);
186
+ byTarget.set(row.targetInstance, list);
187
+ }
188
+ // Capacity is handed out oldest-work-first.
189
+ const ordered = [...byTarget.entries()].sort((a, b) => a[1][0].createdSeq - b[1][0].createdSeq);
190
+ let nextDue = Number.POSITIVE_INFINITY;
191
+ let wakeableAwake = false;
192
+ for (const [target, rows] of ordered) {
193
+ const waiting = rows.filter(r => r.state === "queued" || r.state === "retry_wait");
194
+ // §1.2: the whole queue decides, not only its head.
195
+ if (!waiting.some(r => isWakeEligibleKind(r.kind)))
196
+ continue;
197
+ const s = this.state(target);
198
+ if (this.deps.isPaused(target)) {
199
+ s.claimBlockNoticeSent = false;
200
+ if (this.reserved.has(target) || this.deps.isRestarting(target))
201
+ continue;
202
+ if (this.deps.pauseReason(target) === "auth") {
203
+ if (!s.authNoticeSent) {
204
+ s.authNoticeSent = true;
205
+ this.deps.notifyTarget(target, `⏸️ ${target} is paused because its login failed; ${waiting.length} queued message(s) are waiting. Fix the login and /wake it.`);
206
+ }
207
+ continue;
208
+ }
209
+ if (s.parkedUntil > now) {
210
+ nextDue = Math.min(nextDue, s.parkedUntil);
211
+ continue;
212
+ }
213
+ if (s.backoffUntil > now) {
214
+ nextDue = Math.min(nextDue, s.backoffUntil);
215
+ continue;
216
+ }
217
+ this.startWake(target, waiting);
218
+ continue;
219
+ }
220
+ s.authNoticeSent = false;
221
+ wakeableAwake = true;
222
+ this.checkAwakeTarget(target, s, rows, waiting, now);
223
+ }
224
+ if (Number.isFinite(nextDue))
225
+ this.kick(Math.max(1, nextDue - now));
226
+ // Awake targets with work still waiting: make sure the pump looks again.
227
+ if (wakeableAwake && !this.stopped)
228
+ this.deps.kickPump();
229
+ }
230
+ /**
231
+ * An awake target with queued work: past its pre-claim window without the
232
+ * work moving, it is parked (no automatic re-wake until the hold clears or
233
+ * the park expires), and a long pre-claim block is reported once.
234
+ */
235
+ checkAwakeTarget(target, s, rows, waiting, now) {
236
+ const inFlight = rows.some(r => r.state === "delivering" || r.state === "submission_started" || r.state === "reconciliation_pending");
237
+ if (inFlight)
238
+ return;
239
+ const head = waiting[0];
240
+ const headCreated = head?.createdAt ? Date.parse(head.createdAt) : now;
241
+ // Measured from whichever is later — the wake or the head row's admission —
242
+ // so fresh work for a long-awake instance is not mistaken for a hold.
243
+ const woke = this.deps.wokeAt(target);
244
+ if (woke !== undefined && now - Math.max(woke, headCreated) >= PRECLAIM_WINDOW_MS && s.parkedUntil <= now) {
245
+ const reason = this.deps.notAcceptingReason(target) ?? "queued work was not claimed within the pre-claim window";
246
+ const span = Math.min(PARK_MAX_MS, PARK_BASE_MS * 2 ** s.parkLevel);
247
+ s.parkLevel += 1;
248
+ s.parkedUntil = now + span;
249
+ s.parkReason = reason;
250
+ this.deps.logger.warn({ target, reason, parkMs: span }, "Wake coordinator parked a target that is not taking its queued work");
251
+ }
252
+ const headAge = now - headCreated;
253
+ if (!s.claimBlockNoticeSent && headAge >= CLAIM_BLOCK_NOTICE_MS) {
254
+ const reason = this.deps.notAcceptingReason(target);
255
+ if (reason) {
256
+ s.claimBlockNoticeSent = true;
257
+ this.deps.notifyTarget(target, `⚠️ ${target} has ${waiting.length} queued message(s) waiting ${Math.round(headAge / 60_000)} min: ${reason}.`);
258
+ }
259
+ }
260
+ }
261
+ /**
262
+ * Whether a wake completion must do nothing but return its slot: the
263
+ * coordinator was stopped, or the outbox went away while the wake was in
264
+ * flight (the coordinator stops itself then). Checked before any side
265
+ * effect, so a late completion can never kick a pump at a closed database.
266
+ */
267
+ fencedCompletion() {
268
+ if (this.stopped)
269
+ return true;
270
+ if (!this.deps.available()) {
271
+ this.stop();
272
+ return true;
273
+ }
274
+ return false;
275
+ }
276
+ startWake(target, waiting) {
277
+ const token = this.tryReserve(target);
278
+ if (!token) {
279
+ this.deps.logger.debug?.({ target, reserved: this.reserved.size }, "Wake deferred: no warm slot under the hard cap");
280
+ return;
281
+ }
282
+ const s = this.state(target);
283
+ this.deps.logger.info({ target, queued: waiting.length }, "Waking a paused target for queued durable work");
284
+ // Completions are fenced on stop(): once stopped, a late settle only
285
+ // returns its slot (exactly once) — no state change, notice or kick.
286
+ this.deps.wake(target).then(() => {
287
+ if (this.fencedCompletion())
288
+ return;
289
+ this.deps.kickPump();
290
+ s.failures = 0;
291
+ s.backoffUntil = 0;
292
+ s.lastError = null;
293
+ s.failureNoticeSent = false;
294
+ }, (err) => {
295
+ if (this.fencedCompletion())
296
+ return;
297
+ const message = err instanceof Error ? err.message : String(err);
298
+ s.failures += 1;
299
+ s.lastError = message;
300
+ const delay = Math.min(WAKE_BACKOFF_MAX_MS, WAKE_BACKOFF_BASE_MS * 2 ** (s.failures - 1));
301
+ s.backoffUntil = this.deps.now() + delay;
302
+ this.deps.logger.warn({ target, failures: s.failures, retryInMs: delay, err: message }, "Wake for queued durable work failed; rows stay queued");
303
+ if (s.failures >= WAKE_FAILURE_NOTICE_AFTER && !s.failureNoticeSent) {
304
+ s.failureNoticeSent = true;
305
+ const text = `⚠️ ${target} could not be woken (${s.failures} attempts): ${message}. ${waiting.length} message(s) are still queued and will be retried.`;
306
+ this.deps.notifyTarget(target, text);
307
+ for (const source of new Set(waiting.map(r => r.sourceInstance))) {
308
+ if (source && source !== target && source !== "agend-system")
309
+ this.deps.notifySender(source, text);
310
+ }
311
+ }
312
+ }).catch((err) => {
313
+ this.deps.logger.warn({ target, err: err instanceof Error ? err.message : String(err) }, "Wake coordinator completion handler failed");
314
+ }).finally(() => {
315
+ this.release(target, token);
316
+ if (!this.stopped)
317
+ this.kick();
318
+ });
319
+ }
320
+ }
321
+ //# sourceMappingURL=wake-coordinator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wake-coordinator.js","sourceRoot":"","sources":["../src/wake-coordinator.ts"],"names":[],"mappings":"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"}
package/dist/web-api.d.ts CHANGED
@@ -72,7 +72,9 @@ export interface WebApiContext {
72
72
  emitSseEvent(event: string, data: unknown): void;
73
73
  startInstance(name: string, config: unknown, topicMode: boolean): Promise<void>;
74
74
  stopInstance(name: string): Promise<void>;
75
- restartSingleInstance(name: string): Promise<void>;
75
+ restartSingleInstance(name: string, opts?: {
76
+ explicit?: boolean;
77
+ }): Promise<void>;
76
78
  removeInstance(name: string, authorization: ExplicitInstanceRemoval): Promise<void>;
77
79
  lastInboundUser: Map<string, string>;
78
80
  saveFleetConfig(): void;
package/dist/web-api.js CHANGED
@@ -349,7 +349,7 @@ export function handleWebRequest(req, res, url, ctx) {
349
349
  const name = decodeURIComponent(restartMatch[1]);
350
350
  (async () => {
351
351
  try {
352
- await ctx.restartSingleInstance(name);
352
+ await ctx.restartSingleInstance(name, { explicit: true });
353
353
  ctx.emitSseEvent("status", ctx.getUiStatus());
354
354
  json(res, 200, { restarted: name });
355
355
  }