claude-overnight 1.25.7 → 1.25.10
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/_version.d.ts +1 -1
- package/dist/_version.js +1 -1
- package/dist/index.js +26 -20
- package/dist/render.js +12 -3
- package/dist/swarm.js +69 -13
- package/dist/types.d.ts +5 -1
- package/package.json +1 -1
- package/plugins/claude-overnight/.claude-plugin/plugin.json +1 -1
package/dist/_version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "1.25.
|
|
1
|
+
export declare const VERSION = "1.25.10";
|
package/dist/_version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated by build — do not edit manually.
|
|
2
|
-
export const VERSION = "1.25.
|
|
2
|
+
export const VERSION = "1.25.10";
|
package/dist/index.js
CHANGED
|
@@ -318,6 +318,7 @@ async function main() {
|
|
|
318
318
|
}
|
|
319
319
|
// ── Resume / continue detection ──
|
|
320
320
|
let resuming = false;
|
|
321
|
+
let replanFromScratch = false;
|
|
321
322
|
let resumeState = null;
|
|
322
323
|
let resumeRunDir;
|
|
323
324
|
let continueObjective;
|
|
@@ -460,25 +461,29 @@ async function main() {
|
|
|
460
461
|
// thinking spend instead of throwing it away).
|
|
461
462
|
const designs = readMdDir(join(resumeRunDir, "designs"));
|
|
462
463
|
if (!designs || !resumeState.objective) {
|
|
463
|
-
|
|
464
|
-
|
|
464
|
+
// Planning died before producing anything — re-run planning from
|
|
465
|
+
// scratch while keeping all saved settings (model, budget, etc.).
|
|
466
|
+
console.log(chalk.yellow(`\n ⚠ Planning-phase run has no tasks or designs — will re-plan from scratch.\n`));
|
|
467
|
+
replanFromScratch = true;
|
|
465
468
|
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
469
|
+
else {
|
|
470
|
+
const remainingBudget = Math.max(resumeState.concurrency, resumeState.budget - resumeState.accCompleted);
|
|
471
|
+
const orchBudget = Math.min(50, Math.max(resumeState.concurrency, Math.ceil(remainingBudget * 0.5)));
|
|
472
|
+
const flexNote = `This is wave 1 of an adaptive multi-wave run (total budget: ${remainingBudget}). Plan the highest-impact foundational work first. Future waves will iterate based on what's learned.`;
|
|
473
|
+
console.log(chalk.cyan(`\n ◆ Re-orchestrating plan from existing designs...\n`));
|
|
474
|
+
process.stdout.write("\x1B[?25l");
|
|
475
|
+
try {
|
|
476
|
+
const orchTasks = await orchestrate(resumeState.objective, designs, cwd, resumeState.plannerModel, resumeState.workerModel, resumeState.permissionMode, orchBudget, resumeState.concurrency, makeProgressLog(), flexNote, join(resumeRunDir, "tasks.json"));
|
|
477
|
+
resumeState.currentTasks = orchTasks;
|
|
478
|
+
process.stdout.write(`\x1B[2K\r ${chalk.green(`✓ ${orchTasks.length} tasks`)}\n`);
|
|
479
|
+
}
|
|
480
|
+
catch (err) {
|
|
481
|
+
process.stdout.write("\x1B[?25h");
|
|
482
|
+
console.error(chalk.red(`\n Re-orchestration failed: ${err.message}\n Start Fresh instead.\n`));
|
|
483
|
+
process.exit(1);
|
|
484
|
+
}
|
|
477
485
|
process.stdout.write("\x1B[?25h");
|
|
478
|
-
console.error(chalk.red(`\n Re-orchestration failed: ${err.message}\n Start Fresh instead.\n`));
|
|
479
|
-
process.exit(1);
|
|
480
486
|
}
|
|
481
|
-
process.stdout.write("\x1B[?25h");
|
|
482
487
|
}
|
|
483
488
|
}
|
|
484
489
|
const unmerged = resumeState.branches.filter(b => b.status === "unmerged").length;
|
|
@@ -860,7 +865,7 @@ async function main() {
|
|
|
860
865
|
if (resuming && resumeRunDir)
|
|
861
866
|
updateLatestSymlink(rootDir, resumeRunDir);
|
|
862
867
|
const previousKnowledge = readPreviousRunKnowledge(rootDir);
|
|
863
|
-
const needsPlan = tasks.length === 0 && !resuming;
|
|
868
|
+
const needsPlan = tasks.length === 0 && (!resuming || replanFromScratch);
|
|
864
869
|
const designDir = join(runDir, "designs");
|
|
865
870
|
// Persist an early planning-phase state so the run is visible to the resume
|
|
866
871
|
// picker even if orchestrate dies before executeRun gets a chance to run.
|
|
@@ -970,15 +975,16 @@ async function main() {
|
|
|
970
975
|
console.log("");
|
|
971
976
|
}
|
|
972
977
|
else {
|
|
973
|
-
const
|
|
978
|
+
const researchModel = fastModel ? workerModel : plannerModel;
|
|
979
|
+
const thinkingTasks = buildThinkingTasks(objective, themes, designDir, researchModel, previousKnowledge || undefined);
|
|
974
980
|
console.log(chalk.cyan(`\n ◆ Thinking: ${thinkingTasks.length} agents exploring...\n`));
|
|
975
981
|
const thinkingSwarm = new Swarm({
|
|
976
|
-
tasks: thinkingTasks, concurrency, cwd, model:
|
|
982
|
+
tasks: thinkingTasks, concurrency, cwd, model: researchModel, permissionMode,
|
|
977
983
|
useWorktrees: false, mergeStrategy: "yolo", agentTimeoutMs, usageCap, allowExtraUsage, extraUsageBudget,
|
|
978
984
|
envForModel,
|
|
979
985
|
cursorProxy: [plannerProvider, workerProvider, fastProvider].some(p => p && isCursorProxyProvider(p)),
|
|
980
986
|
});
|
|
981
|
-
const thinkRunInfo = { accIn: 0, accOut: 0, accCost: 0, accCompleted: 0, accFailed: 0, sessionsBudget: budget ?? 10, waveNum: -1, remaining: budget ?? 10, model:
|
|
987
|
+
const thinkRunInfo = { accIn: 0, accOut: 0, accCost: 0, accCompleted: 0, accFailed: 0, sessionsBudget: budget ?? 10, waveNum: -1, remaining: budget ?? 10, model: researchModel, startedAt: Date.now() };
|
|
982
988
|
const thinkDisplay = new RunDisplay(thinkRunInfo, { remaining: 0, usageCap, concurrency, paused: false, dirty: false });
|
|
983
989
|
thinkDisplay.setWave(thinkingSwarm);
|
|
984
990
|
thinkDisplay.start();
|
package/dist/render.js
CHANGED
|
@@ -510,6 +510,7 @@ export function renderSummary(swarm) {
|
|
|
510
510
|
out.push(chalk.gray(" " + "\u2500".repeat(Math.min(w - 4, fixedW + taskW))));
|
|
511
511
|
const groups = [
|
|
512
512
|
swarm.agents.filter(a => a.status === "running"),
|
|
513
|
+
swarm.agents.filter(a => a.status === "paused"),
|
|
513
514
|
swarm.agents.filter(a => a.status === "done"),
|
|
514
515
|
swarm.agents.filter(a => a.status === "error"),
|
|
515
516
|
].filter(g => g.length > 0);
|
|
@@ -521,7 +522,10 @@ export function renderSummary(swarm) {
|
|
|
521
522
|
for (const a of groups[gi]) {
|
|
522
523
|
const id = String(a.id).padStart(3);
|
|
523
524
|
const ok = a.status === "done";
|
|
524
|
-
const status = ok ? chalk.green("\u2713 done")
|
|
525
|
+
const status = ok ? chalk.green("\u2713 done")
|
|
526
|
+
: a.status === "running" ? chalk.blue("~ run ")
|
|
527
|
+
: a.status === "paused" ? chalk.yellow("\u23F8 paused")
|
|
528
|
+
: chalk.red("\u2717 err ");
|
|
525
529
|
const task = truncate(a.task.prompt, taskW).padEnd(taskW);
|
|
526
530
|
const durMs = a.startedAt != null ? (a.finishedAt ?? Date.now()) - a.startedAt : 0;
|
|
527
531
|
const dur = fmtDur(durMs).padStart(8);
|
|
@@ -532,7 +536,7 @@ export function renderSummary(swarm) {
|
|
|
532
536
|
totalFiles += a.filesChanged ?? 0;
|
|
533
537
|
totalTools += a.toolCalls;
|
|
534
538
|
totalCost += a.costUsd ?? 0;
|
|
535
|
-
const color = ok ? chalk.white : a.status === "running" ? chalk.blue : chalk.red;
|
|
539
|
+
const color = ok ? chalk.white : a.status === "running" ? chalk.blue : a.status === "paused" ? chalk.yellow : chalk.red;
|
|
536
540
|
out.push(color(` ${id} ${status} ${task} ${dur} ${files} ${tools} ${cost}`));
|
|
537
541
|
}
|
|
538
542
|
}
|
|
@@ -549,7 +553,8 @@ function fmtRow(a, w, selected = false) {
|
|
|
549
553
|
const spin = SPINNER[Math.floor(Date.now() / 250) % SPINNER.length];
|
|
550
554
|
const icon = a.status === "running"
|
|
551
555
|
? (a.blockedAt ? chalk.yellow("\u25CF blk") : chalk.blue(`${spin} run`)) + elapsed
|
|
552
|
-
: a.status === "
|
|
556
|
+
: a.status === "paused" ? chalk.yellow("\u23F8 paused")
|
|
557
|
+
: a.status === "done" ? chalk.green("\u2713 done") : chalk.red("\u2717 err ");
|
|
553
558
|
const taskW = Math.max(20, Math.min(36, w - 50));
|
|
554
559
|
const task = truncate(a.task.prompt, taskW).padEnd(taskW);
|
|
555
560
|
let action;
|
|
@@ -562,6 +567,10 @@ function fmtRow(a, w, selected = false) {
|
|
|
562
567
|
else if (a.status === "running") {
|
|
563
568
|
action = chalk.dim(truncate(a.lastText || "...", 24));
|
|
564
569
|
}
|
|
570
|
+
else if (a.status === "paused") {
|
|
571
|
+
const dur = fmtDur((Date.now()) - (a.startedAt || Date.now()));
|
|
572
|
+
action = chalk.yellow(`paused ${dur}`);
|
|
573
|
+
}
|
|
565
574
|
else if (a.status === "done") {
|
|
566
575
|
const dur = fmtDur((a.finishedAt || Date.now()) - (a.startedAt || Date.now()));
|
|
567
576
|
const cost = a.costUsd != null ? ` $${a.costUsd.toFixed(3)}` : "";
|
package/dist/swarm.js
CHANGED
|
@@ -126,6 +126,13 @@ export class Swarm {
|
|
|
126
126
|
return;
|
|
127
127
|
this.paused = b;
|
|
128
128
|
this.log(-1, b ? "Dispatch paused" : "Dispatch resumed");
|
|
129
|
+
if (b) {
|
|
130
|
+
// Instant: interrupt every active SDK session so agents stop mid-turn.
|
|
131
|
+
// After the interrupt, the for await loop exits, runOnce returns, and
|
|
132
|
+
// runAgent detects this.paused and re-queues the task with resume info.
|
|
133
|
+
this.activeQueries.forEach(q => { q.interrupt().catch(() => { }); });
|
|
134
|
+
this.log(-1, "Pausing agents…");
|
|
135
|
+
}
|
|
129
136
|
}
|
|
130
137
|
/** Returns the rate-limit window currently holding the swarm back -- rejected first, then highest utilization. */
|
|
131
138
|
mostConstrainedWindow() {
|
|
@@ -232,7 +239,7 @@ export class Swarm {
|
|
|
232
239
|
abort() {
|
|
233
240
|
this.aborted = true;
|
|
234
241
|
this.queue.length = 0;
|
|
235
|
-
this.activeQueries.forEach(q => q.
|
|
242
|
+
this.activeQueries.forEach(q => { q.interrupt().catch(() => { }); });
|
|
236
243
|
this.activeQueries.clear();
|
|
237
244
|
}
|
|
238
245
|
/** Re-queue all errored agents' tasks for retry within this wave. */
|
|
@@ -431,11 +438,17 @@ export class Swarm {
|
|
|
431
438
|
}
|
|
432
439
|
// ── Agent execution ──
|
|
433
440
|
async runAgent(task) {
|
|
441
|
+
// Guard: if pause was triggered between dispatch and here, re-queue immediately.
|
|
442
|
+
// The worker already shifted this task, so unshift puts it back for resume.
|
|
443
|
+
if (this.paused) {
|
|
444
|
+
this.queue.unshift(task);
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
434
447
|
const id = this.nextId++;
|
|
435
448
|
const agent = { id, task, status: "running", startedAt: Date.now(), toolCalls: 0 };
|
|
436
449
|
this.agents.push(agent);
|
|
437
|
-
let agentCwd = task.cwd || this.config.cwd;
|
|
438
|
-
if (this.config.useWorktrees && this.worktreeBase && !task.noWorktree) {
|
|
450
|
+
let agentCwd = task.agentCwd || task.cwd || this.config.cwd;
|
|
451
|
+
if (this.config.useWorktrees && this.worktreeBase && !task.noWorktree && !task.agentCwd) {
|
|
439
452
|
const branch = `swarm/task-${id}`;
|
|
440
453
|
const dir = join(this.worktreeBase, `agent-${id}`);
|
|
441
454
|
let baseRef;
|
|
@@ -477,7 +490,8 @@ export class Swarm {
|
|
|
477
490
|
this.log(id, `Worktree failed after retry -- running without isolation`);
|
|
478
491
|
}
|
|
479
492
|
}
|
|
480
|
-
|
|
493
|
+
const isResumed = !!task.resumeSessionId;
|
|
494
|
+
this.log(id, isResumed ? `Resuming: ${task.prompt.slice(0, 60)}` : `Starting: ${task.prompt.slice(0, 60)}`);
|
|
481
495
|
const maxRetries = this.config.maxRetries ?? 2;
|
|
482
496
|
const inactivityMs = this.config.agentTimeoutMs ?? 15 * 60 * 1000;
|
|
483
497
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
@@ -491,7 +505,7 @@ export class Swarm {
|
|
|
491
505
|
}
|
|
492
506
|
try {
|
|
493
507
|
const perm = this.config.permissionMode ?? "auto";
|
|
494
|
-
let resumeSessionId;
|
|
508
|
+
let resumeSessionId = task.resumeSessionId;
|
|
495
509
|
let resumePrompt = "Continue. Complete the task.";
|
|
496
510
|
const runOnce = async (isResume) => {
|
|
497
511
|
const preamble = "Keep files under ~500 lines. If a file would exceed that, split it.\n\n";
|
|
@@ -529,6 +543,16 @@ export class Swarm {
|
|
|
529
543
|
timer = setTimeout(check, timeoutMs);
|
|
530
544
|
});
|
|
531
545
|
this.activeQueries.add(agentQuery);
|
|
546
|
+
// Guard: if pause was triggered between runAgent check and here, close the query
|
|
547
|
+
// immediately so requeueIfPaused can catch it without running a turn.
|
|
548
|
+
if (this.paused) {
|
|
549
|
+
this.activeQueries.delete(agentQuery);
|
|
550
|
+
try {
|
|
551
|
+
agentQuery.close();
|
|
552
|
+
}
|
|
553
|
+
catch { }
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
532
556
|
try {
|
|
533
557
|
await Promise.race([
|
|
534
558
|
(async () => {
|
|
@@ -553,17 +577,47 @@ export class Swarm {
|
|
|
553
577
|
catch { }
|
|
554
578
|
}
|
|
555
579
|
};
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
580
|
+
// Helper: re-queue this task with resume info when paused mid-turn.
|
|
581
|
+
const requeueIfPaused = () => {
|
|
582
|
+
if (!this.paused || agent.status !== "running")
|
|
583
|
+
return false;
|
|
584
|
+
agent.status = "paused";
|
|
585
|
+
this.log(id, "Paused mid-task");
|
|
586
|
+
if (resumeSessionId) {
|
|
587
|
+
this.queue.unshift({ ...task, resumeSessionId, agentCwd });
|
|
588
|
+
}
|
|
589
|
+
return true;
|
|
590
|
+
};
|
|
591
|
+
if (isResumed && resumeSessionId) {
|
|
592
|
+
// Resumed task: continue the existing SDK session
|
|
593
|
+
try {
|
|
562
594
|
await runOnce(true);
|
|
563
595
|
}
|
|
564
|
-
|
|
565
|
-
|
|
596
|
+
catch (nudgeErr) {
|
|
597
|
+
if (nudgeErr instanceof NudgeError && resumeSessionId) {
|
|
598
|
+
this.log(id, `Silent ${Math.round(inactivityMs / 60000)}m -- resuming with continue`);
|
|
599
|
+
await runOnce(true);
|
|
600
|
+
}
|
|
601
|
+
else
|
|
602
|
+
throw nudgeErr;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
// Fresh task: start with the task prompt
|
|
607
|
+
try {
|
|
608
|
+
await runOnce(false);
|
|
609
|
+
}
|
|
610
|
+
catch (nudgeErr) {
|
|
611
|
+
if (nudgeErr instanceof NudgeError && resumeSessionId) {
|
|
612
|
+
this.log(id, `Silent ${Math.round(inactivityMs / 60000)}m -- resuming with continue`);
|
|
613
|
+
await runOnce(true);
|
|
614
|
+
}
|
|
615
|
+
else
|
|
616
|
+
throw nudgeErr;
|
|
617
|
+
}
|
|
566
618
|
}
|
|
619
|
+
if (requeueIfPaused())
|
|
620
|
+
return;
|
|
567
621
|
if (resumeSessionId && agent.status === "running") {
|
|
568
622
|
try {
|
|
569
623
|
this.log(id, "Simplify pass");
|
|
@@ -574,6 +628,8 @@ export class Swarm {
|
|
|
574
628
|
this.log(id, "Simplify pass skipped");
|
|
575
629
|
}
|
|
576
630
|
}
|
|
631
|
+
if (requeueIfPaused())
|
|
632
|
+
return;
|
|
577
633
|
if (agent.status === "running") {
|
|
578
634
|
agent.finishedAt = Date.now();
|
|
579
635
|
const duration = agent.finishedAt - (agent.startedAt || agent.finishedAt);
|
package/dist/types.d.ts
CHANGED
|
@@ -10,6 +10,10 @@ export interface Task {
|
|
|
10
10
|
model?: string;
|
|
11
11
|
/** When true, skip worktree isolation -- run in the real project directory with env files, dependencies, and local config. */
|
|
12
12
|
noWorktree?: boolean;
|
|
13
|
+
/** SDK session ID to resume from (set when task was paused mid-turn). */
|
|
14
|
+
resumeSessionId?: string;
|
|
15
|
+
/** Working directory preserved from a previous run (worktree dir for paused-and-resumed tasks). */
|
|
16
|
+
agentCwd?: string;
|
|
13
17
|
}
|
|
14
18
|
/** Schema for a JSON task file that defines a batch of work for the swarm. */
|
|
15
19
|
export interface TaskFile {
|
|
@@ -45,7 +49,7 @@ export interface TaskFile {
|
|
|
45
49
|
})[];
|
|
46
50
|
}
|
|
47
51
|
/** Lifecycle status of a single agent. */
|
|
48
|
-
export type AgentStatus = "pending" | "running" | "done" | "error";
|
|
52
|
+
export type AgentStatus = "pending" | "running" | "paused" | "done" | "error";
|
|
49
53
|
/** Live mutable state for one agent, used by the UI and orchestrator. */
|
|
50
54
|
export interface AgentState {
|
|
51
55
|
/** Sequential agent index (0-based), used for display and log correlation. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-overnight",
|
|
3
|
-
"version": "1.25.
|
|
3
|
+
"version": "1.25.10",
|
|
4
4
|
"description": "Parallel Claude agents in git worktrees with a usage cap that reserves headroom for your interactive Claude Code. Crash-safe resume. Provider-agnostic model catalog (Anthropic, Cursor, OpenAI, Gemini, DeepSeek, Llama, Qwen) with capability-based task scoping.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-overnight",
|
|
3
|
-
"version": "1.25.
|
|
3
|
+
"version": "1.25.10",
|
|
4
4
|
"description": "Claude Code skill for understanding, installing, and inspecting claude-overnight runs -- parallel Claude agents in git worktrees with thinking waves, multi-wave steering, and crash-safe resume. Supports Cursor API Proxy, Qwen, OpenRouter.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Francesco Fornace"
|