claude-overnight 1.11.3 → 1.11.5
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.
- package/dist/render.js +3 -4
- package/dist/run.js +2 -2
- package/dist/swarm.d.ts +0 -1
- package/dist/swarm.js +58 -35
- package/package.json +1 -1
package/dist/render.js
CHANGED
|
@@ -80,10 +80,9 @@ function renderUsageBars(out, w, swarm) {
|
|
|
80
80
|
}
|
|
81
81
|
let label = `${Math.round(pct * 100)}% used`;
|
|
82
82
|
if (swarm.cappedOut) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
label = chalk.yellow(`Capped at ${capFrac != null ? Math.round(capFrac * 100) : 100}% \u2014 finishing active`);
|
|
83
|
+
label = swarm.isUsingOverage
|
|
84
|
+
? chalk.red(`Extra usage budget exhausted \u2014 finishing active`)
|
|
85
|
+
: chalk.yellow(`Capped at ${capFrac != null ? Math.round(capFrac * 100) : 100}% \u2014 finishing active`);
|
|
87
86
|
}
|
|
88
87
|
else if (swarm.rateLimitPaused > 0) {
|
|
89
88
|
label = chalk.yellow(`Cooling down \u2014 ${swarm.rateLimitPaused} worker(s) waiting`);
|
package/dist/run.js
CHANGED
|
@@ -390,7 +390,7 @@ export async function executeRun(cfg) {
|
|
|
390
390
|
else if (remaining <= 0)
|
|
391
391
|
console.log(chalk.bold.yellow(` CLAUDE OVERNIGHT — BUDGET EXHAUSTED`));
|
|
392
392
|
else if (lastCapped)
|
|
393
|
-
console.log(chalk.bold.yellow(` CLAUDE OVERNIGHT —
|
|
393
|
+
console.log(chalk.bold.yellow(` CLAUDE OVERNIGHT — EXTRA USAGE BUDGET HIT`));
|
|
394
394
|
else if (stopping || lastAborted)
|
|
395
395
|
console.log(chalk.bold.yellow(` CLAUDE OVERNIGHT — INTERRUPTED`));
|
|
396
396
|
else
|
|
@@ -406,7 +406,7 @@ export async function executeRun(cfg) {
|
|
|
406
406
|
for (const [k1, v1, k2, v2] of statRows)
|
|
407
407
|
console.log(` ${k1} ${v1.padEnd(20)} ${k2} ${v2}`);
|
|
408
408
|
if (lastCapped)
|
|
409
|
-
console.log(` ${chalk.yellow(`
|
|
409
|
+
console.log(` ${chalk.yellow(`Extra usage budget exhausted`)}`);
|
|
410
410
|
console.log("");
|
|
411
411
|
const statusFile = join(runDir, "status.md");
|
|
412
412
|
if (existsSync(statusFile)) {
|
package/dist/swarm.d.ts
CHANGED
package/dist/swarm.js
CHANGED
|
@@ -33,7 +33,6 @@ export class Swarm {
|
|
|
33
33
|
rateLimitWindows = new Map();
|
|
34
34
|
rateLimitPaused = 0;
|
|
35
35
|
isUsingOverage = false;
|
|
36
|
-
overageDisabledReason;
|
|
37
36
|
overageCostUsd = 0;
|
|
38
37
|
queue;
|
|
39
38
|
config;
|
|
@@ -180,43 +179,45 @@ export class Swarm {
|
|
|
180
179
|
async throttle() {
|
|
181
180
|
if (this.cappedOut)
|
|
182
181
|
return;
|
|
183
|
-
|
|
184
|
-
this.capForOverage(`Extra usage detected but not allowed — stopping dispatch`);
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
182
|
+
// Hard stop: overage budget exhausted (only legitimate cap)
|
|
187
183
|
if (this.isUsingOverage && this.extraUsageBudget != null && this.overageCostUsd >= this.extraUsageBudget) {
|
|
188
184
|
this.capForOverage(`Extra usage budget $${this.extraUsageBudget} reached ($${this.overageCostUsd.toFixed(2)} spent) — stopping dispatch`);
|
|
189
185
|
return;
|
|
190
186
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
187
|
+
// Wait loop: keep waiting until the blocking condition clears
|
|
188
|
+
let consecutiveWaits = 0;
|
|
189
|
+
for (;;) {
|
|
190
|
+
if (this.aborted || this.cappedOut)
|
|
191
|
+
return;
|
|
192
|
+
const cap = this.usageCap;
|
|
193
|
+
const overageBlocked = this.isUsingOverage && !this.allowExtraUsage;
|
|
194
|
+
const capExceeded = cap != null && cap < 1 && this.rateLimitUtilization >= cap;
|
|
195
|
+
const rejected = this.rateLimitResetsAt && this.rateLimitResetsAt > Date.now();
|
|
196
|
+
if (!overageBlocked && !capExceeded && !rejected)
|
|
197
|
+
break;
|
|
198
|
+
// Use SDK reset time if available, otherwise escalate: 1m → 3m → 5m (max)
|
|
199
|
+
const fallbackMs = Math.min(300_000, 60_000 * (1 + consecutiveWaits * 2));
|
|
200
|
+
const waitMs = this.rateLimitResetsAt && this.rateLimitResetsAt > Date.now()
|
|
194
201
|
? Math.max(5000, this.rateLimitResetsAt - Date.now())
|
|
195
|
-
:
|
|
196
|
-
|
|
202
|
+
: fallbackMs;
|
|
203
|
+
const reason = overageBlocked ? "Extra usage blocked (plan limit)"
|
|
204
|
+
: capExceeded ? `Usage at ${Math.round(this.rateLimitUtilization * 100)}% (cap ${Math.round(cap * 100)}%)`
|
|
205
|
+
: "Rate limited";
|
|
206
|
+
this.log(-1, `${reason} — waiting ${Math.ceil(waitMs / 1000)}s then retrying`);
|
|
197
207
|
this.rateLimitPaused++;
|
|
198
208
|
await sleep(waitMs);
|
|
199
209
|
this.rateLimitPaused--;
|
|
210
|
+
// Reset stale flags — fresh state will come from the next SDK event
|
|
211
|
+
this.isUsingOverage = false;
|
|
200
212
|
this.rateLimitUtilization = 0;
|
|
201
213
|
this.rateLimitResetsAt = undefined;
|
|
202
|
-
|
|
203
|
-
}
|
|
204
|
-
if (this.rateLimitResetsAt) {
|
|
205
|
-
const resetTarget = this.rateLimitResetsAt;
|
|
206
|
-
const waitMs = resetTarget - Date.now();
|
|
207
|
-
if (waitMs > 0) {
|
|
208
|
-
this.log(-1, `Rate limited, pausing ${Math.ceil(waitMs / 1000)}s`);
|
|
209
|
-
this.rateLimitPaused++;
|
|
210
|
-
await sleep(waitMs);
|
|
211
|
-
this.rateLimitPaused--;
|
|
212
|
-
}
|
|
213
|
-
if (this.rateLimitResetsAt === resetTarget)
|
|
214
|
-
this.rateLimitResetsAt = undefined;
|
|
214
|
+
consecutiveWaits++;
|
|
215
215
|
}
|
|
216
|
-
|
|
216
|
+
// Soft delay: high utilization, pace requests
|
|
217
|
+
if (this.rateLimitUtilization > 0.75) {
|
|
217
218
|
const delay = Math.floor((this.rateLimitUtilization - 0.75) * 60000);
|
|
218
|
-
|
|
219
|
-
|
|
219
|
+
if (delay > 0)
|
|
220
|
+
await sleep(delay);
|
|
220
221
|
}
|
|
221
222
|
}
|
|
222
223
|
// ── Agent execution ──
|
|
@@ -370,6 +371,21 @@ export class Swarm {
|
|
|
370
371
|
catch (err) {
|
|
371
372
|
if (agent.status !== "running")
|
|
372
373
|
break;
|
|
374
|
+
// Rate-limit errors: wait and retry WITHOUT burning the retry budget
|
|
375
|
+
if (!this.aborted && isRateLimitError(err)) {
|
|
376
|
+
const waitMs = this.rateLimitResetsAt && this.rateLimitResetsAt > Date.now()
|
|
377
|
+
? Math.max(5000, this.rateLimitResetsAt - Date.now())
|
|
378
|
+
: 120_000;
|
|
379
|
+
this.log(id, `Rate limited — waiting ${Math.ceil(waitMs / 1000)}s (attempt not counted)`);
|
|
380
|
+
this.rateLimitPaused++;
|
|
381
|
+
await sleep(waitMs);
|
|
382
|
+
this.rateLimitPaused--;
|
|
383
|
+
this.isUsingOverage = false;
|
|
384
|
+
this.rateLimitUtilization = 0;
|
|
385
|
+
this.rateLimitResetsAt = undefined;
|
|
386
|
+
attempt--; // don't count this against retries
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
373
389
|
const canRetry = attempt < maxRetries && !this.aborted && isTransientError(err);
|
|
374
390
|
if (canRetry) {
|
|
375
391
|
this.log(id, `Transient error: ${String(err.message || err).slice(0, 80)}`);
|
|
@@ -460,26 +476,21 @@ export class Swarm {
|
|
|
460
476
|
const rl = msg;
|
|
461
477
|
const info = rl.rate_limit_info;
|
|
462
478
|
this.rateLimitUtilization = info.utilization ?? 0;
|
|
463
|
-
if (info.
|
|
479
|
+
if (info.resetsAt)
|
|
464
480
|
this.rateLimitResetsAt = info.resetsAt;
|
|
465
481
|
else if (info.status !== "rejected")
|
|
466
482
|
this.rateLimitResetsAt = undefined;
|
|
483
|
+
if (info.isUsingOverage)
|
|
484
|
+
this.isUsingOverage = true;
|
|
467
485
|
const windowType = info.rateLimitType;
|
|
468
486
|
if (windowType) {
|
|
469
487
|
this.rateLimitWindows.set(windowType, {
|
|
470
488
|
type: windowType, utilization: info.utilization ?? 0, status: info.status, resetsAt: info.resetsAt,
|
|
471
489
|
});
|
|
472
490
|
}
|
|
473
|
-
if (info.isUsingOverage)
|
|
474
|
-
this.isUsingOverage = true;
|
|
475
|
-
if (info.overageDisabledReason)
|
|
476
|
-
this.overageDisabledReason = info.overageDisabledReason;
|
|
477
|
-
if (this.isUsingOverage && !this.allowExtraUsage)
|
|
478
|
-
this.capForOverage(`Extra usage detected but not allowed — stopping dispatch`);
|
|
479
491
|
const pct = info.utilization != null ? `${Math.round(info.utilization * 100)}%` : "";
|
|
480
492
|
const overageTag = this.isUsingOverage ? " [EXTRA]" : "";
|
|
481
|
-
|
|
482
|
-
this.log(agent.id, `Rate: ${statusLabel} ${pct}${overageTag}${windowType ? ` (${windowType})` : ""}`);
|
|
493
|
+
this.log(agent.id, `Rate: ${info.status} ${pct}${overageTag}${windowType ? ` (${windowType})` : ""}`);
|
|
483
494
|
break;
|
|
484
495
|
}
|
|
485
496
|
}
|
|
@@ -491,6 +502,18 @@ class AgentTimeoutError extends Error {
|
|
|
491
502
|
this.name = "AgentTimeoutError";
|
|
492
503
|
}
|
|
493
504
|
}
|
|
505
|
+
function isRateLimitError(err) {
|
|
506
|
+
const status = err?.status ?? err?.statusCode;
|
|
507
|
+
if (status === 429)
|
|
508
|
+
return true;
|
|
509
|
+
const msg = String(err?.message || err).toLowerCase();
|
|
510
|
+
if (msg.includes("rate limit") || msg.includes("rate_limit") || msg.includes("too many requests"))
|
|
511
|
+
return true;
|
|
512
|
+
const cause = err?.cause;
|
|
513
|
+
if (cause && cause !== err)
|
|
514
|
+
return isRateLimitError(cause);
|
|
515
|
+
return false;
|
|
516
|
+
}
|
|
494
517
|
function isTransientError(err) {
|
|
495
518
|
if (err instanceof AgentTimeoutError)
|
|
496
519
|
return false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-overnight",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.5",
|
|
4
4
|
"description": "Run 10, 100, or 1000 Claude agents overnight. Parallel autonomous AI coding with thinking waves, iterative quality steering, crash recovery, and rate limit handling. Built on the Claude Agent SDK.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|