agent-lyceum 0.24.1 → 0.25.0

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/cli.js CHANGED
@@ -545,7 +545,7 @@ program
545
545
  }
546
546
  const lease = takeLock(pr, full, { checkout: false });
547
547
  try {
548
- const plan = planRunCleanup(pr, full, { keepWorktrees: opts.keepWorktrees });
548
+ const plan = planRunCleanup(pr, full, { keepWorktrees: opts.keepWorktrees, lockHeld: true });
549
549
  if (plan.refusals.length)
550
550
  refuse(plan);
551
551
  showPlan(plan);
@@ -35,9 +35,12 @@ export interface CleanupReport {
35
35
  /**
36
36
  * What clearing a run would do. Deletes nothing. Refuses (see `refusals`) while the run is running, or while
37
37
  * a worktree holds work that is not in the repo, unless `keepWorktrees` keeps the worktrees and their branches.
38
+ * `lockHeld`: the caller holds this run's lock (taking it would have thrown while a dispatcher was alive), so the
39
+ * run cannot be running and the lock, which is the caller's own, is not consulted.
38
40
  */
39
41
  export declare function planRunCleanup(project: ResolvedProject, runId: string, opts?: {
40
42
  keepWorktrees?: boolean;
43
+ lockHeld?: boolean;
41
44
  }): CleanupPlan;
42
45
  export interface ExecuteHooks {
43
46
  /** Test seam: called after each item is done and recorded. */
@@ -2,6 +2,7 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { atomicWrite } from "./fs-util.js";
4
4
  import { assertName } from "./paths.js";
5
+ import { inspectRunLock } from "./project-lock.js";
5
6
  import { loadRunState } from "./run-store.js";
6
7
  import { runIsAlive } from "./status.js";
7
8
  import { deleteSnapshotRefs, existingAgentWorkspace, inspectWorkspace, listSnapshotRefs, listWorkspaceAgents, removeAgentWorkspace } from "./worktree.js";
@@ -20,6 +21,8 @@ const lexists = (p) => {
20
21
  /**
21
22
  * What clearing a run would do. Deletes nothing. Refuses (see `refusals`) while the run is running, or while
22
23
  * a worktree holds work that is not in the repo, unless `keepWorktrees` keeps the worktrees and their branches.
24
+ * `lockHeld`: the caller holds this run's lock (taking it would have thrown while a dispatcher was alive), so the
25
+ * run cannot be running and the lock, which is the caller's own, is not consulted.
23
26
  */
24
27
  export function planRunCleanup(project, runId, opts = {}) {
25
28
  assertName("run", runId);
@@ -33,8 +36,10 @@ export function planRunCleanup(project, runId, opts = {}) {
33
36
  if (hasRun) {
34
37
  const state = loadRunState(runDir);
35
38
  plan.summary = `${runId}: ${state.task_summary}`;
36
- if (!state.end_reason && runIsAlive(state, project))
37
- plan.refusals.push(`Run ${runId} is still running (pid ${state.pid}); stop it first.`);
39
+ if (!state.end_reason && !opts.lockHeld && runIsAlive(state, project)) {
40
+ const holder = state.schema_version >= 2 ? inspectRunLock(project.paths.root, runId) : undefined;
41
+ plan.refusals.push(`Run ${runId} is still running (pid ${holder?.pid ?? state.pid}); stop it first.`);
42
+ }
38
43
  }
39
44
  // A concurrent run has a worktree for the lead: members' work lands there, and the lead's branch is what you merge.
40
45
  const leadWs = existingAgentWorkspace(project, runId, project.lead);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-lyceum",
3
- "version": "0.24.1",
3
+ "version": "0.25.0",
4
4
  "description": "Config + dispatcher for teams of Claude Code / Codex agents with scoped context and file-based mailboxes",
5
5
  "keywords": [
6
6
  "ai",