crewly 1.20.234 → 1.20.236

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 (45) hide show
  1. package/dist/backend/backend/src/index.d.ts +17 -0
  2. package/dist/backend/backend/src/index.d.ts.map +1 -1
  3. package/dist/backend/backend/src/index.js +184 -125
  4. package/dist/backend/backend/src/index.js.map +1 -1
  5. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  6. package/dist/backend/backend/src/services/agent/agent-registration.service.js +30 -0
  7. package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
  8. package/dist/backend/backend/src/services/agent/idle-detection.service.d.ts.map +1 -1
  9. package/dist/backend/backend/src/services/agent/idle-detection.service.js +5 -9
  10. package/dist/backend/backend/src/services/agent/idle-detection.service.js.map +1 -1
  11. package/dist/backend/backend/src/services/agent/resource-mode.service.d.ts +127 -0
  12. package/dist/backend/backend/src/services/agent/resource-mode.service.d.ts.map +1 -0
  13. package/dist/backend/backend/src/services/agent/resource-mode.service.js +264 -0
  14. package/dist/backend/backend/src/services/agent/resource-mode.service.js.map +1 -0
  15. package/dist/backend/backend/src/services/agent/restore-queue.d.ts +103 -0
  16. package/dist/backend/backend/src/services/agent/restore-queue.d.ts.map +1 -0
  17. package/dist/backend/backend/src/services/agent/restore-queue.js +184 -0
  18. package/dist/backend/backend/src/services/agent/restore-queue.js.map +1 -0
  19. package/dist/backend/backend/src/types/settings.types.d.ts +4 -0
  20. package/dist/backend/backend/src/types/settings.types.d.ts.map +1 -1
  21. package/dist/backend/backend/src/types/settings.types.js +8 -0
  22. package/dist/backend/backend/src/types/settings.types.js.map +1 -1
  23. package/dist/backend/build-info.json +2 -2
  24. package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  25. package/dist/cli/backend/src/services/agent/agent-registration.service.js +30 -0
  26. package/dist/cli/backend/src/services/agent/agent-registration.service.js.map +1 -1
  27. package/dist/cli/backend/src/services/agent/resource-mode.service.d.ts +127 -0
  28. package/dist/cli/backend/src/services/agent/resource-mode.service.d.ts.map +1 -0
  29. package/dist/cli/backend/src/services/agent/resource-mode.service.js +264 -0
  30. package/dist/cli/backend/src/services/agent/resource-mode.service.js.map +1 -0
  31. package/dist/cli/backend/src/services/agent/restore-queue.d.ts +103 -0
  32. package/dist/cli/backend/src/services/agent/restore-queue.d.ts.map +1 -0
  33. package/dist/cli/backend/src/services/agent/restore-queue.js +184 -0
  34. package/dist/cli/backend/src/services/agent/restore-queue.js.map +1 -0
  35. package/dist/cli/backend/src/services/core/system-health.util.d.ts +89 -0
  36. package/dist/cli/backend/src/services/core/system-health.util.d.ts.map +1 -0
  37. package/dist/cli/backend/src/services/core/system-health.util.js +220 -0
  38. package/dist/cli/backend/src/services/core/system-health.util.js.map +1 -0
  39. package/dist/cli/backend/src/types/settings.types.d.ts +4 -0
  40. package/dist/cli/backend/src/types/settings.types.d.ts.map +1 -1
  41. package/dist/cli/backend/src/types/settings.types.js +8 -0
  42. package/dist/cli/backend/src/types/settings.types.js.map +1 -1
  43. package/frontend/dist/assets/{index-bc4e2a93.js → index-a719c15a.js} +54 -54
  44. package/frontend/dist/index.html +1 -1
  45. package/package.json +1 -1
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Adaptive agent limits. Normal mode: no cap, idle agents stop only when
3
+ * memory is tight (IdleDetectionService). Pressure mode (memory/swap/OS
4
+ * pressure or sustained high CPU load): cap running agents, stop idle ones
5
+ * sooner, and queue starts that would exceed the cap.
6
+ *
7
+ * @module services/agent/resource-mode.service
8
+ */
9
+ export declare const RESOURCE_MODE_CONSTANTS: {
10
+ readonly DEFAULT_MAX_RUNNING_AGENTS: 6;
11
+ readonly DEFAULT_IDLE_TIMEOUT_MINUTES: 10;
12
+ /** Consecutive tight samples before entering pressure mode */
13
+ readonly ENTER_AFTER: 2;
14
+ /** Consecutive calm samples before leaving pressure mode */
15
+ readonly LEAVE_AFTER: 3;
16
+ /** Load average (5 min) above this many times the core count counts as tight */
17
+ readonly LOAD_PER_CORE: 2;
18
+ readonly SAMPLE_INTERVAL_MS: 30000;
19
+ /** How long a queued start waits for a slot before giving up (messages stay queued) */
20
+ readonly START_WAIT_MS: number;
21
+ /** A started agent counts against the cap even before it shows as running */
22
+ readonly ADMIT_TTL_MS: number;
23
+ };
24
+ export type ResourceMode = 'normal' | 'pressure';
25
+ export interface MemoryReading {
26
+ usedPercent: number;
27
+ freeMB: number;
28
+ totalMB: number;
29
+ swapUsedPercent?: number;
30
+ pressureElevated?: boolean;
31
+ }
32
+ /**
33
+ * Whether memory alone is tight (used share, free floor, swap, OS pressure).
34
+ *
35
+ * @param stats - Memory reading
36
+ * @returns True when tight
37
+ */
38
+ export declare function memoryIsTightFor(stats: MemoryReading): boolean;
39
+ /** A running, non-exempt agent as seen by the cap. */
40
+ export interface RunningAgent {
41
+ sessionName: string;
42
+ role: string;
43
+ idleMs: number;
44
+ /** Mid-turn (working status in_progress) */
45
+ busy: boolean;
46
+ }
47
+ export interface ResourceModeDeps {
48
+ listRunning: () => Promise<RunningAgent[]>;
49
+ stopAgent: (sessionName: string, role: string) => Promise<void>;
50
+ hasOwnerMessage: (sessionName: string) => boolean;
51
+ limits: () => Promise<{
52
+ maxRunning: number;
53
+ idleTimeoutMinutes: number;
54
+ }>;
55
+ }
56
+ export declare class ResourceModeService {
57
+ private static instance;
58
+ private logger;
59
+ private mode;
60
+ private tightStreak;
61
+ private calmStreak;
62
+ private lastReason;
63
+ private timer;
64
+ private deps;
65
+ private waiters;
66
+ private seq;
67
+ private admitted;
68
+ private pumping;
69
+ private lastRunning;
70
+ private lastCap;
71
+ /** Readings, overridable in tests. */
72
+ memoryStats: () => MemoryReading;
73
+ loadAvg: () => number;
74
+ cpuCount: () => number;
75
+ private constructor();
76
+ static getInstance(): ResourceModeService;
77
+ static resetInstance(): void;
78
+ setDeps(deps: ResourceModeDeps | null): void;
79
+ /** Start periodic sampling. */
80
+ start(): void;
81
+ stop(): void;
82
+ getMode(): ResourceMode;
83
+ /** Why a sample is tight, or '' when calm. */
84
+ private tightReason;
85
+ /**
86
+ * Take one reading and apply hysteresis: enter pressure after
87
+ * ENTER_AFTER tight samples in a row, leave after LEAVE_AFTER calm ones.
88
+ *
89
+ * @returns The mode after this sample
90
+ */
91
+ sample(): Promise<ResourceMode>;
92
+ /**
93
+ * Idle timeout in minutes for the current mode.
94
+ *
95
+ * @param normalMinutes - `general.agentIdleTimeoutMinutes`
96
+ * @param pressureMinutes - `general.pressureIdleTimeoutMinutes`
97
+ * @returns Effective timeout (0 = disabled)
98
+ */
99
+ effectiveIdleTimeoutMinutes(normalMinutes: number, pressureMinutes: number): number;
100
+ /** Snapshot for /health. */
101
+ stats(): {
102
+ resourceMode: ResourceMode;
103
+ running: number;
104
+ cap: number | null;
105
+ waiting: number;
106
+ reason: string;
107
+ };
108
+ /**
109
+ * Ask for permission to start an agent. Resolves true at once in normal
110
+ * mode. In pressure mode with the cap reached, tries to free a slot by
111
+ * stopping the longest-idle agent; otherwise waits (owner-triggered starts
112
+ * first). Resolves false when no slot opened in time — the caller leaves
113
+ * the agent's messages queued.
114
+ *
115
+ * @param sessionName - Agent to start
116
+ * @param ownerTriggered - An owner message is waiting for it
117
+ * @returns True when the start may proceed
118
+ */
119
+ requestStart(sessionName: string, ownerTriggered: boolean): Promise<boolean>;
120
+ /** Admit waiting starts while a slot is free or can be freed. */
121
+ pump(): Promise<void>;
122
+ /** Longest-idle agent that is not busy and has no owner message pending. */
123
+ private pickVictim;
124
+ private release;
125
+ private schedulePump;
126
+ }
127
+ //# sourceMappingURL=resource-mode.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-mode.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/agent/resource-mode.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,264 @@
1
+ /**
2
+ * Adaptive agent limits. Normal mode: no cap, idle agents stop only when
3
+ * memory is tight (IdleDetectionService). Pressure mode (memory/swap/OS
4
+ * pressure or sustained high CPU load): cap running agents, stop idle ones
5
+ * sooner, and queue starts that would exceed the cap.
6
+ *
7
+ * @module services/agent/resource-mode.service
8
+ */
9
+ import os from 'os';
10
+ import { LoggerService } from '../core/logger.service.js';
11
+ import { getMemoryStats } from '../core/system-health.util.js';
12
+ import { AGENT_SUSPEND_CONSTANTS } from '../../constants.js';
13
+ export const RESOURCE_MODE_CONSTANTS = {
14
+ DEFAULT_MAX_RUNNING_AGENTS: 6,
15
+ DEFAULT_IDLE_TIMEOUT_MINUTES: 10,
16
+ /** Consecutive tight samples before entering pressure mode */
17
+ ENTER_AFTER: 2,
18
+ /** Consecutive calm samples before leaving pressure mode */
19
+ LEAVE_AFTER: 3,
20
+ /** Load average (5 min) above this many times the core count counts as tight */
21
+ LOAD_PER_CORE: 2,
22
+ SAMPLE_INTERVAL_MS: 30_000,
23
+ /** How long a queued start waits for a slot before giving up (messages stay queued) */
24
+ START_WAIT_MS: 2 * 60_000,
25
+ /** A started agent counts against the cap even before it shows as running */
26
+ ADMIT_TTL_MS: 3 * 60_000,
27
+ };
28
+ /**
29
+ * Whether memory alone is tight (used share, free floor, swap, OS pressure).
30
+ *
31
+ * @param stats - Memory reading
32
+ * @returns True when tight
33
+ */
34
+ export function memoryIsTightFor(stats) {
35
+ if (!stats.totalMB)
36
+ return false;
37
+ return (stats.usedPercent >= AGENT_SUSPEND_CONSTANTS.IDLE_STOP_MEMORY_USED_PERCENT ||
38
+ stats.freeMB < AGENT_SUSPEND_CONSTANTS.IDLE_STOP_MIN_FREE_MB ||
39
+ (stats.swapUsedPercent ?? 0) >= AGENT_SUSPEND_CONSTANTS.IDLE_STOP_SWAP_USED_PERCENT ||
40
+ stats.pressureElevated === true);
41
+ }
42
+ export class ResourceModeService {
43
+ static instance = null;
44
+ logger;
45
+ mode = 'normal';
46
+ tightStreak = 0;
47
+ calmStreak = 0;
48
+ lastReason = '';
49
+ timer = null;
50
+ deps = null;
51
+ waiters = [];
52
+ seq = 0;
53
+ admitted = new Map();
54
+ pumping = false;
55
+ lastRunning = 0;
56
+ lastCap = RESOURCE_MODE_CONSTANTS.DEFAULT_MAX_RUNNING_AGENTS;
57
+ /** Readings, overridable in tests. */
58
+ memoryStats = getMemoryStats;
59
+ loadAvg = () => os.loadavg()[1];
60
+ cpuCount = () => os.cpus().length || 1;
61
+ constructor() {
62
+ this.logger = LoggerService.getInstance().createComponentLogger('ResourceMode');
63
+ }
64
+ static getInstance() {
65
+ return (ResourceModeService.instance ??= new ResourceModeService());
66
+ }
67
+ static resetInstance() {
68
+ ResourceModeService.instance?.stop();
69
+ ResourceModeService.instance = null;
70
+ }
71
+ setDeps(deps) {
72
+ this.deps = deps;
73
+ }
74
+ /** Start periodic sampling. */
75
+ start() {
76
+ if (this.timer)
77
+ return;
78
+ this.timer = setInterval(() => { void this.sample(); }, RESOURCE_MODE_CONSTANTS.SAMPLE_INTERVAL_MS);
79
+ this.timer.unref?.();
80
+ }
81
+ stop() {
82
+ if (this.timer)
83
+ clearInterval(this.timer);
84
+ this.timer = null;
85
+ for (const w of this.waiters) {
86
+ clearTimeout(w.timer);
87
+ w.resolve(false);
88
+ }
89
+ this.waiters = [];
90
+ }
91
+ getMode() {
92
+ return this.mode;
93
+ }
94
+ /** Why a sample is tight, or '' when calm. */
95
+ tightReason() {
96
+ const mem = this.memoryStats();
97
+ if (memoryIsTightFor(mem)) {
98
+ return `memory used=${mem.usedPercent}% free=${mem.freeMB}MB swap=${mem.swapUsedPercent ?? 0}% osPressure=${mem.pressureElevated === true}`;
99
+ }
100
+ const load = this.loadAvg();
101
+ const cores = this.cpuCount();
102
+ if (load > RESOURCE_MODE_CONSTANTS.LOAD_PER_CORE * cores)
103
+ return `cpu load ${load.toFixed(1)} > ${RESOURCE_MODE_CONSTANTS.LOAD_PER_CORE}x${cores} cores`;
104
+ return '';
105
+ }
106
+ /**
107
+ * Take one reading and apply hysteresis: enter pressure after
108
+ * ENTER_AFTER tight samples in a row, leave after LEAVE_AFTER calm ones.
109
+ *
110
+ * @returns The mode after this sample
111
+ */
112
+ async sample() {
113
+ const reason = this.tightReason();
114
+ if (reason) {
115
+ this.tightStreak++;
116
+ this.calmStreak = 0;
117
+ }
118
+ else {
119
+ this.calmStreak++;
120
+ this.tightStreak = 0;
121
+ }
122
+ if (this.mode === 'normal' && this.tightStreak >= RESOURCE_MODE_CONSTANTS.ENTER_AFTER) {
123
+ this.mode = 'pressure';
124
+ this.lastReason = reason;
125
+ this.logger.warn('Entering pressure mode: agent cap and shorter idle timeout apply', { reason });
126
+ }
127
+ else if (this.mode === 'pressure' && this.calmStreak >= RESOURCE_MODE_CONSTANTS.LEAVE_AFTER) {
128
+ this.mode = 'normal';
129
+ this.lastReason = '';
130
+ this.logger.info('Leaving pressure mode: back to normal limits');
131
+ }
132
+ await this.pump();
133
+ return this.mode;
134
+ }
135
+ /**
136
+ * Idle timeout in minutes for the current mode.
137
+ *
138
+ * @param normalMinutes - `general.agentIdleTimeoutMinutes`
139
+ * @param pressureMinutes - `general.pressureIdleTimeoutMinutes`
140
+ * @returns Effective timeout (0 = disabled)
141
+ */
142
+ effectiveIdleTimeoutMinutes(normalMinutes, pressureMinutes) {
143
+ if (this.mode !== 'pressure')
144
+ return normalMinutes;
145
+ return pressureMinutes > 0 ? pressureMinutes : normalMinutes;
146
+ }
147
+ /** Snapshot for /health. */
148
+ stats() {
149
+ return {
150
+ resourceMode: this.mode,
151
+ running: this.lastRunning,
152
+ cap: this.mode === 'pressure' ? this.lastCap : null,
153
+ waiting: this.waiters.length,
154
+ reason: this.lastReason,
155
+ };
156
+ }
157
+ /**
158
+ * Ask for permission to start an agent. Resolves true at once in normal
159
+ * mode. In pressure mode with the cap reached, tries to free a slot by
160
+ * stopping the longest-idle agent; otherwise waits (owner-triggered starts
161
+ * first). Resolves false when no slot opened in time — the caller leaves
162
+ * the agent's messages queued.
163
+ *
164
+ * @param sessionName - Agent to start
165
+ * @param ownerTriggered - An owner message is waiting for it
166
+ * @returns True when the start may proceed
167
+ */
168
+ async requestStart(sessionName, ownerTriggered) {
169
+ if (this.mode !== 'pressure' || !this.deps)
170
+ return true;
171
+ return new Promise((resolve) => {
172
+ const timer = setTimeout(() => {
173
+ this.waiters = this.waiters.filter((w) => w !== waiter);
174
+ this.logger.warn('Agent start still waiting for a slot; giving up for now (messages stay queued)', { sessionName });
175
+ resolve(false);
176
+ }, RESOURCE_MODE_CONSTANTS.START_WAIT_MS);
177
+ const waiter = { name: sessionName, owner: ownerTriggered, seq: this.seq++, resolve, timer };
178
+ this.waiters.push(waiter);
179
+ this.waiters.sort((a, b) => Number(b.owner) - Number(a.owner) || a.seq - b.seq);
180
+ this.logger.info('Start queued: running-agent cap reached', { sessionName, ownerTriggered, waiting: this.waiters.length });
181
+ void this.pump();
182
+ });
183
+ }
184
+ /** Admit waiting starts while a slot is free or can be freed. */
185
+ async pump() {
186
+ if (this.pumping || !this.deps)
187
+ return;
188
+ this.pumping = true;
189
+ try {
190
+ const now = Date.now();
191
+ for (const [n, at] of this.admitted)
192
+ if (now - at > RESOURCE_MODE_CONSTANTS.ADMIT_TTL_MS)
193
+ this.admitted.delete(n);
194
+ const { maxRunning } = await this.deps.limits();
195
+ this.lastCap = maxRunning;
196
+ const running = await this.deps.listRunning();
197
+ const names = new Set(running.map((r) => r.sessionName));
198
+ for (const n of names)
199
+ this.admitted.delete(n);
200
+ let count = running.length + this.admitted.size;
201
+ this.lastRunning = count;
202
+ let pool = running;
203
+ // Already running: nothing to admit, nothing to count twice.
204
+ for (const w of this.waiters.filter((x) => names.has(x.name))) {
205
+ this.waiters = this.waiters.filter((x) => x !== w);
206
+ this.release(w);
207
+ }
208
+ while (this.waiters.length > 0) {
209
+ if (this.mode !== 'pressure') {
210
+ this.release(this.waiters.shift());
211
+ continue;
212
+ }
213
+ if (count >= maxRunning) {
214
+ const victim = this.pickVictim(pool);
215
+ if (!victim)
216
+ break;
217
+ try {
218
+ this.logger.info('Stopping longest-idle agent to free a slot', { sessionName: victim.sessionName, idleMinutes: Math.round(victim.idleMs / 60000), waiter: this.waiters[0].name });
219
+ await this.deps.stopAgent(victim.sessionName, victim.role);
220
+ }
221
+ catch (err) {
222
+ this.logger.error('Failed to stop agent for slot', { sessionName: victim.sessionName, error: err instanceof Error ? err.message : String(err) });
223
+ pool = pool.filter((r) => r !== victim);
224
+ continue;
225
+ }
226
+ pool = pool.filter((r) => r !== victim);
227
+ count--;
228
+ }
229
+ const w = this.waiters.shift();
230
+ this.admitted.set(w.name, Date.now());
231
+ count++;
232
+ this.release(w);
233
+ }
234
+ this.lastRunning = count;
235
+ if (this.waiters.length > 0)
236
+ this.schedulePump();
237
+ }
238
+ catch (err) {
239
+ this.logger.error('Slot pump failed', { error: err instanceof Error ? err.message : String(err) });
240
+ }
241
+ finally {
242
+ this.pumping = false;
243
+ }
244
+ }
245
+ /** Longest-idle agent that is not busy and has no owner message pending. */
246
+ pickVictim(running) {
247
+ const waiting = new Set(this.waiters.map((w) => w.name));
248
+ return running
249
+ .filter((r) => !r.busy
250
+ && !waiting.has(r.sessionName)
251
+ && !AGENT_SUSPEND_CONSTANTS.ALWAYS_ON_ROLES.includes(r.role)
252
+ && !this.deps.hasOwnerMessage(r.sessionName))
253
+ .sort((a, b) => b.idleMs - a.idleMs)[0];
254
+ }
255
+ release(w) {
256
+ clearTimeout(w.timer);
257
+ w.resolve(true);
258
+ }
259
+ schedulePump() {
260
+ const t = setTimeout(() => { void this.pump(); }, 10_000);
261
+ t.unref?.();
262
+ }
263
+ }
264
+ //# sourceMappingURL=resource-mode.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-mode.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/agent/resource-mode.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Staggered boot restore: agents with work in hand come back one at a time
3
+ * (each Claude Code relaunch on a loaded machine takes minutes), the owner's
4
+ * agents first. An on-demand wake for an agent still waiting in the queue
5
+ * moves it up instead of starting a second launch in parallel.
6
+ *
7
+ * @module services/agent/restore-queue
8
+ */
9
+ /** Defaults for the restore queue. */
10
+ export declare const RESTORE_QUEUE_CONSTANTS: {
11
+ readonly DEFAULT_CONCURRENCY: 1;
12
+ readonly MAX_CONCURRENCY: 3;
13
+ /** Per-agent budget before the queue moves on and retries it later */
14
+ readonly DEFAULT_TIMEOUT_MS: number;
15
+ /** Times an agent that timed out is tried again, at the back of the queue */
16
+ readonly MAX_RETRIES: 1;
17
+ };
18
+ /** Restore priority tiers, lowest first. */
19
+ export declare const RESTORE_TIER: {
20
+ readonly OWNER: 1;
21
+ readonly ORCHESTRATOR: 2;
22
+ readonly LEAD: 3;
23
+ readonly OTHER: 4;
24
+ };
25
+ /** One agent to restore. */
26
+ export interface RestoreEntry {
27
+ name: string;
28
+ tier: number;
29
+ /** Why it has this position (logged in the plan) */
30
+ reason: string;
31
+ /** Age of the oldest owner message waiting for it (ms epoch); orders the owner tier */
32
+ ownerSince?: number;
33
+ /** Start the agent; resolves when it is registered. `attempt` is 0 on the first try. */
34
+ run: (attempt: number) => Promise<{
35
+ success: boolean;
36
+ }>;
37
+ /** Called right after the agent registered */
38
+ onReady?: () => Promise<void>;
39
+ }
40
+ /** Progress counters exposed in /health. */
41
+ export interface RestoreQueueStats {
42
+ running: boolean;
43
+ pending: number;
44
+ started: number;
45
+ ready: number;
46
+ failed: number;
47
+ timedOut: number;
48
+ order: string[];
49
+ }
50
+ /**
51
+ * Concurrency from `CREWLY_RESTORE_CONCURRENCY`, clamped to 1..3.
52
+ *
53
+ * @param raw - Env value
54
+ * @returns Concurrency
55
+ */
56
+ export declare function restoreConcurrencyFromEnv(raw?: string | undefined): number;
57
+ /**
58
+ * Order entries: tier first; inside the owner tier, oldest owner message first.
59
+ *
60
+ * @param entries - Entries to order
61
+ * @returns A new, ordered array
62
+ */
63
+ export declare function orderRestoreEntries(entries: readonly RestoreEntry[]): RestoreEntry[];
64
+ /** The queue itself. */
65
+ export declare class RestoreQueue {
66
+ private readonly concurrency;
67
+ private readonly timeoutMs;
68
+ private pending;
69
+ private active;
70
+ private counters;
71
+ private running;
72
+ private done;
73
+ private readonly logger;
74
+ constructor(concurrency?: number, timeoutMs?: number);
75
+ /**
76
+ * Plan and run the restore. Resolves when every entry is ready, failed or
77
+ * out of retries. Returns the same promise while already running.
78
+ *
79
+ * @param entries - Agents to restore
80
+ * @returns Promise settled when the queue is drained
81
+ */
82
+ start(entries: readonly RestoreEntry[]): Promise<void>;
83
+ /** Whether the agent is still waiting (not yet started) in the queue. */
84
+ isPending(name: string): boolean;
85
+ /**
86
+ * An on-demand wake arrived for an agent. Owner-triggered wakes move it to
87
+ * the front (behind earlier promoted ones); other wakes keep queue order.
88
+ *
89
+ * @param name - Session name
90
+ * @param ownerTriggered - The wake carries an owner message
91
+ * @returns True when the agent is waiting in the queue (the caller must not start it)
92
+ */
93
+ wake(name: string, ownerTriggered: boolean): boolean;
94
+ /** Progress for /health. */
95
+ stats(): RestoreQueueStats;
96
+ private worker;
97
+ private runSlot;
98
+ }
99
+ /** The process-wide restore queue. */
100
+ export declare function getRestoreQueue(): RestoreQueue;
101
+ /** Test hook: drop the singleton. */
102
+ export declare function resetRestoreQueue(): void;
103
+ //# sourceMappingURL=restore-queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"restore-queue.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/agent/restore-queue.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,sCAAsC;AACtC,eAAO,MAAM,uBAAuB;;;IAGnC,sEAAsE;;IAEtE,6EAA6E;;CAEpE,CAAC;AAEX,4CAA4C;AAC5C,eAAO,MAAM,YAAY;;;;;CAA4D,CAAC;AAEtF,4BAA4B;AAC5B,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,MAAM,EAAE,MAAM,CAAC;IACf,uFAAuF;IACvF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wFAAwF;IACxF,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACxD,8CAA8C;IAC9C,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,4CAA4C;AAC5C,MAAM,WAAW,iBAAiB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,GAAE,MAAM,GAAG,SAAkD,GAAG,MAAM,CAIlH;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,YAAY,EAAE,CAQpF;AAID,wBAAwB;AACxB,qBAAa,YAAY;IASvB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAT3B,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,QAAQ,CAAoD;IACpE,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,IAAI,CAAoC;IAChD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;gBAGvB,WAAW,GAAE,MAAoC,EACjD,SAAS,GAAE,MAAoG;IAKjI;;;;;;OAMG;IACH,KAAK,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBtD,yEAAyE;IACzE,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIhC;;;;;;;OAOG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,GAAG,OAAO;IAepD,4BAA4B;IAC5B,KAAK,IAAI,iBAAiB;YAIZ,MAAM;YAQN,OAAO;CA2CrB;AAID,sCAAsC;AACtC,wBAAgB,eAAe,IAAI,YAAY,CAE9C;AAED,qCAAqC;AACrC,wBAAgB,iBAAiB,IAAI,IAAI,CAExC"}