claude-overnight 1.25.9 → 1.25.12

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.
@@ -1 +1 @@
1
- export declare const VERSION = "1.25.9";
1
+ export declare const VERSION = "1.25.12";
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.9";
2
+ export const VERSION = "1.25.12";
package/dist/index.js CHANGED
@@ -975,15 +975,16 @@ async function main() {
975
975
  console.log("");
976
976
  }
977
977
  else {
978
- const thinkingTasks = buildThinkingTasks(objective, themes, designDir, plannerModel, previousKnowledge || undefined);
978
+ const researchModel = fastModel ? workerModel : plannerModel;
979
+ const thinkingTasks = buildThinkingTasks(objective, themes, designDir, researchModel, previousKnowledge || undefined);
979
980
  console.log(chalk.cyan(`\n ◆ Thinking: ${thinkingTasks.length} agents exploring...\n`));
980
981
  const thinkingSwarm = new Swarm({
981
- tasks: thinkingTasks, concurrency, cwd, model: plannerModel, permissionMode,
982
+ tasks: thinkingTasks, concurrency, cwd, model: researchModel, permissionMode,
982
983
  useWorktrees: false, mergeStrategy: "yolo", agentTimeoutMs, usageCap, allowExtraUsage, extraUsageBudget,
983
984
  envForModel,
984
985
  cursorProxy: [plannerProvider, workerProvider, fastProvider].some(p => p && isCursorProxyProvider(p)),
985
986
  });
986
- const thinkRunInfo = { accIn: 0, accOut: 0, accCost: 0, accCompleted: 0, accFailed: 0, sessionsBudget: budget ?? 10, waveNum: -1, remaining: budget ?? 10, model: plannerModel, startedAt: Date.now() };
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() };
987
988
  const thinkDisplay = new RunDisplay(thinkRunInfo, { remaining: 0, usageCap, concurrency, paused: false, dirty: false });
988
989
  thinkDisplay.setWave(thinkingSwarm);
989
990
  thinkDisplay.start();
package/dist/models.d.ts CHANGED
@@ -7,7 +7,7 @@ export interface ModelCapability {
7
7
  }
8
8
  export declare const MODEL_CAPABILITIES: Record<string, ModelCapability>;
9
9
  export declare const DEFAULT_MODEL = "claude-sonnet-4-6";
10
- export declare const FALLBACK_MODEL = "claude-opus-4-6";
10
+ export declare const FALLBACK_MODEL = "claude-opus-4-7";
11
11
  /**
12
12
  * Find capability info for a model string. Tries: exact match → lowercase
13
13
  * exact → substring match. Falls back to "unknown" entry.
package/dist/models.js CHANGED
@@ -20,6 +20,7 @@
20
20
  export const MODEL_CAPABILITIES = {
21
21
  // ── Anthropic Claude (Apr 2026) ──
22
22
  // Opus: only model that earns "relaxed". 100% on 38-task routing, 95%+ IFEval.
23
+ "claude-opus-4-7": { contextWindow: 1_000_000, safeContext: 400_000, contextConstraint: "relaxed", displayName: "Opus 4.7" },
23
24
  "claude-opus-4-6": { contextWindow: 1_000_000, safeContext: 400_000, contextConstraint: "relaxed", displayName: "Opus 4.6" },
24
25
  // Sonnet: good but loses thread more than Opus on autonomous multi-file work.
25
26
  "claude-sonnet-4-6": { contextWindow: 1_000_000, safeContext: 300_000, contextConstraint: "moderate", displayName: "Sonnet 4.6" },
@@ -59,7 +60,7 @@ export const MODEL_CAPABILITIES = {
59
60
  };
60
61
  // ── Default / fallback models ──
61
62
  export const DEFAULT_MODEL = "claude-sonnet-4-6";
62
- export const FALLBACK_MODEL = "claude-opus-4-6"; // used for planner + worker recovery
63
+ export const FALLBACK_MODEL = "claude-opus-4-7"; // used for planner + worker recovery
63
64
  // ── Lookup ──
64
65
  /**
65
66
  * Find capability info for a model string. Tries: exact match → lowercase
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") : a.status === "running" ? chalk.blue("~ run ") : chalk.red("\u2717 err ");
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 === "done" ? chalk.green("\u2713 done") : chalk.red("\u2717 err ");
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.close());
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
- this.log(id, `Starting: ${task.prompt.slice(0, 60)}`);
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
- try {
557
- await runOnce(false);
558
- }
559
- catch (nudgeErr) {
560
- if (nudgeErr instanceof NudgeError && resumeSessionId) {
561
- this.log(id, `Silent ${Math.round(inactivityMs / 60000)}m -- resuming with continue`);
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
- else
565
- throw nudgeErr;
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.9",
3
+ "version": "1.25.12",
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.9",
3
+ "version": "1.25.12",
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"