taskplane 0.23.16 → 0.24.1

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.
@@ -109,8 +109,8 @@ export interface WorkerConfig {
109
109
  tools: string;
110
110
  /** Thinking mode setting passed to worker agent */
111
111
  thinking: string;
112
- /** Optional spawn mode override for task-runner */
113
- spawnMode?: "subprocess" | "tmux";
112
+ /** Optional spawn mode override for task-runner (Runtime V2 subprocess-only). */
113
+ spawnMode?: "subprocess";
114
114
  }
115
115
 
116
116
  /** Reviewer agent configuration */
@@ -139,7 +139,7 @@ export interface ContextConfig {
139
139
  maxReviewCycles: number;
140
140
  /** Max no-progress iterations before marking failure */
141
141
  noProgressLimit: number;
142
- /** Optional per-worker wall-clock cap (minutes, used in tmux/orchestrated flows) */
142
+ /** Optional per-worker wall-clock cap (minutes, used in orchestrated flows) */
143
143
  maxWorkerMinutes?: number;
144
144
  }
145
145
 
@@ -260,10 +260,10 @@ export interface OrchestratorCoreConfig {
260
260
  worktreePrefix: string;
261
261
  /** Batch ID format used in logs/branch naming */
262
262
  batchIdFormat: "timestamp" | "sequential";
263
- /** How lane sessions are spawned */
264
- spawnMode: "tmux" | "subprocess";
265
- /** Prefix for orchestrator tmux sessions (tmux mode) */
266
- tmuxPrefix: string;
263
+ /** How lane sessions are spawned (Runtime V2 subprocess-only). */
264
+ spawnMode: "subprocess";
265
+ /** Prefix for orchestrator session naming */
266
+ sessionPrefix: string;
267
267
  /** Operator identifier. Auto-detected from OS username if empty */
268
268
  operatorId: string;
269
269
  /** How completed batches are integrated. manual = user runs /orch-integrate. supervised = supervisor proposes plan, asks confirmation. auto = supervisor executes without asking. */
@@ -498,7 +498,7 @@ export interface TaskplaneConfig {
498
498
  * | Preference field | Config path | Type |
499
499
  * |--------------------|--------------------------------------|---------|
500
500
  * | operatorId | orchestrator.orchestrator.operatorId | string |
501
- * | tmuxPrefix | orchestrator.orchestrator.tmuxPrefix | string |
501
+ * | sessionPrefix | orchestrator.orchestrator.sessionPrefix | string |
502
502
  * | spawnMode | orchestrator.orchestrator.spawnMode | string |
503
503
  * | workerModel | taskRunner.worker.model | string |
504
504
  * | reviewerModel | taskRunner.reviewer.model | string |
@@ -509,10 +509,10 @@ export interface TaskplaneConfig {
509
509
  export interface UserPreferences {
510
510
  /** Operator identifier (overrides orchestrator.orchestrator.operatorId) */
511
511
  operatorId?: string;
512
- /** TMUX session prefix (overrides orchestrator.orchestrator.tmuxPrefix) */
513
- tmuxPrefix?: string;
514
- /** Spawn mode override (overrides orchestrator.orchestrator.spawnMode) */
515
- spawnMode?: "tmux" | "subprocess";
512
+ /** Orchestrator session prefix (overrides orchestrator.orchestrator.sessionPrefix) */
513
+ sessionPrefix?: string;
514
+ /** Spawn mode override (overrides orchestrator.orchestrator.spawnMode). */
515
+ spawnMode?: "subprocess";
516
516
  /** Worker model override (overrides taskRunner.worker.model) */
517
517
  workerModel?: string;
518
518
  /** Reviewer model override (overrides taskRunner.reviewer.model) */
@@ -582,7 +582,7 @@ export const DEFAULT_ORCHESTRATOR_SECTION: OrchestratorSection = {
582
582
  worktreePrefix: "taskplane-wt",
583
583
  batchIdFormat: "timestamp",
584
584
  spawnMode: "subprocess",
585
- tmuxPrefix: "orch",
585
+ sessionPrefix: "orch",
586
586
  operatorId: "",
587
587
  integration: "manual",
588
588
  },
@@ -411,7 +411,7 @@ export function assembleDiagnosticInput(
411
411
  const record: PersistedTaskRecord = {
412
412
  taskId,
413
413
  laneNumber: lane?.laneNumber ?? 0,
414
- sessionName: outcome?.sessionName || lane?.tmuxSessionName || "",
414
+ sessionName: outcome?.sessionName || lane?.laneSessionId || "",
415
415
  status: outcome?.status ?? "pending",
416
416
  taskFolder: "",
417
417
  startedAt: outcome?.startTime ?? null,
@@ -49,9 +49,9 @@ export interface SessionTokenCounts {
49
49
  * | `context_overflow` | Hit context window limit (compactions + high ctx %) |
50
50
  * | `wall_clock_timeout` | Killed by task-runner's max_worker_minutes timer |
51
51
  * | `process_crash` | Non-zero exit code with no API error indicators |
52
- * | `session_vanished` | Tmux session disappeared without exit summary |
52
+ * | `session_vanished` | Session disappeared without exit summary |
53
53
  * | `stall_timeout` | No STATUS.md progress for stall_timeout minutes |
54
- * | `user_killed` | User manually killed the session (e.g., tmux kill) |
54
+ * | `user_killed` | User manually killed the session (e.g., forced process kill) |
55
55
  * | `unknown` | Could not determine cause |
56
56
  */
57
57
  export type ExitClassification =
@@ -157,7 +157,7 @@ export interface ExitSummary {
157
157
  * - `timerKilled`: true if task-runner's max_worker_minutes timer killed the session
158
158
  * - `contextKilled`: true if the task-runner explicitly killed the session due to context limit
159
159
  * - `stallDetected`: true if monitoring detected no STATUS.md progress
160
- * - `userKilled`: true if user manually killed the session (e.g., /orch-abort, tmux kill)
160
+ * - `userKilled`: true if user manually killed the session (e.g., /orch-abort, forced process kill)
161
161
  * - `contextPct`: estimated context utilization % (0-100), null if unknown
162
162
  *
163
163
  * Design: single structured input object (not positional args) for
@@ -6,19 +6,19 @@ import { existsSync, readdirSync, readFileSync, unlinkSync } from "fs";
6
6
  import { join, resolve } from "path";
7
7
 
8
8
  import { formatDiscoveryResults, runDiscovery } from "./discovery.ts";
9
- import { computeTransitiveDependents, execLog, executeLane, executeLaneV2, executeWave, tmuxKillSession } from "./execution.ts";
9
+ import { computeTransitiveDependents, execLog, executeLaneV2, executeWave, killV2LaneAgents } from "./execution.ts";
10
10
  import type { RuntimeBackend } from "./execution.ts";
11
11
  import type { MonitorUpdateCallback } from "./execution.ts";
12
12
  // classifyExit no longer called directly — Tier 0 uses exitDiagnostic.classification
13
13
  // from the diagnostic-reports pipeline (populated by assembleDiagnosticInput).
14
14
  import { getCurrentBranch, runGit } from "./git.ts";
15
- import { mergeWaveByRepo, MergeHealthMonitor } from "./merge.ts";
15
+ import { killAllMergeAgentsV2, mergeWaveByRepo, MergeHealthMonitor } from "./merge.ts";
16
16
  import { applyMergeRetryLoop, computeCleanupGatePolicy, computeMergeFailurePolicy, extractFailedRepoId, formatRepoMergeSummary, ORCH_MESSAGES } from "./messages.ts";
17
17
  import type { CleanupGateRepoFailure } from "./messages.ts";
18
18
  import { assembleDiagnosticInput, emitDiagnosticReports } from "./diagnostic-reports.ts";
19
19
  import { resolveOperatorId } from "./naming.ts";
20
20
  import { applyPartialProgressToOutcomes, buildTier0EventBase, deleteBatchState, emitEngineEvent, emitTier0Event, loadBatchHistory, loadBatchState, persistRuntimeState, saveBatchHistory, seedPendingOutcomesForAllocatedLanes, syncTaskOutcomesFromMonitor, upsertTaskOutcome } from "./persistence.ts";
21
- import { listOrchSessions } from "./sessions.ts";
21
+ import { readRegistrySnapshot, isTerminalStatus, isProcessAlive as registryIsProcessAlive } from "./process-registry.ts";
22
22
  import { buildBatchProgressSnapshot, buildEngineEventBase, defaultResilienceState, FATAL_DISCOVERY_CODES, generateBatchId, TIER0_RETRYABLE_CLASSIFICATIONS, TIER0_RETRY_BUDGETS, tier0ScopeKey, tier0WaveScopeKey } from "./types.ts";
23
23
  import type { AllocatedLane, AllocatedTask, BatchHistorySummary, BatchTaskSummary, BatchWaveSummary, DiscoveryResult, EngineEventCallback, EscalationContext, LaneExecutionResult, LaneTaskOutcome, MergeWaveResult, OrchBatchPhase, OrchBatchRuntimeState, OrchestratorConfig, SupervisorAlert, SupervisorAlertCallback, TaskRunnerConfig, Tier0EscalationPattern, Tier0RecoveryPattern, TokenCounts, WaveExecutionResult, WorkspaceConfig } from "./types.ts";
24
24
  import { buildDependencyGraph, computeWaves, resolveBaseBranch, resolveRepoRoot, validateGraph } from "./waves.ts";
@@ -278,8 +278,7 @@ async function attemptWorkerCrashRetry(
278
278
  // may be paused due to stop-wave policy, but Tier 0 retry should
279
279
  // attempt recovery before the stop decision takes effect (R002-4).
280
280
  const retryPauseSignal = { paused: false };
281
- const retryExecutor = (runtimeBackend === "v2") ? executeLaneV2 : executeLane;
282
- const retryResult = await retryExecutor(
281
+ const retryResult = await executeLaneV2(
283
282
  retryLane,
284
283
  orchConfig,
285
284
  repoRoot,
@@ -546,8 +545,7 @@ async function attemptModelFallbackRetry(
546
545
  // the task-runner to use the session model instead of configured model.
547
546
  // TP-089: Also include ORCH_BATCH_ID so mailbox steering works for retries.
548
547
  const modelFallbackEnv = { TASKPLANE_MODEL_FALLBACK: "1", ORCH_BATCH_ID: batchState.batchId };
549
- const retryExecutor = (runtimeBackend === "v2") ? executeLaneV2 : executeLane;
550
- const retryResult = await retryExecutor(
548
+ const retryResult = await executeLaneV2(
551
549
  retryLane,
552
550
  orchConfig,
553
551
  repoRoot,
@@ -2475,16 +2473,39 @@ export async function executeOrchBatch(
2475
2473
  if (preserveWorktreesForResume) {
2476
2474
  execLog("batch", batchState.batchId, "skipping final cleanup to preserve worktrees/branches for resume");
2477
2475
  } else {
2478
- // Kill any lingering lane tmux sessions BEFORE removing worktrees.
2479
- // On Windows, tmux sessions with cwd inside the worktree lock the
2480
- // directory, causing git worktree remove to fail.
2481
- const orchPrefix = orchConfig.orchestrator.tmux_prefix;
2482
- const lingering = listOrchSessions(orchPrefix, batchState);
2483
- if (lingering.length > 0) {
2484
- execLog("batch", batchState.batchId, `killing ${lingering.length} lingering tmux session(s) before cleanup`);
2485
- for (const sess of lingering) {
2486
- tmuxKillSession(sess.sessionName);
2476
+ // Kill lingering Runtime V2 agents BEFORE removing worktrees.
2477
+ // On Windows, lingering processes with cwd inside the worktree can lock
2478
+ // the directory and cause `git worktree remove` to fail.
2479
+ const lingeringLaneSessions = new Set<string>();
2480
+ const registry = readRegistrySnapshot(stateRoot, batchState.batchId);
2481
+ if (registry) {
2482
+ for (const manifest of Object.values(registry.agents)) {
2483
+ if (manifest.role !== "worker" && manifest.role !== "reviewer") continue;
2484
+ if (isTerminalStatus(manifest.status) || !registryIsProcessAlive(manifest.pid)) continue;
2485
+ lingeringLaneSessions.add(manifest.agentId.replace(/-(worker|reviewer)$/, ""));
2487
2486
  }
2487
+ }
2488
+
2489
+ let performedAgentCleanup = false;
2490
+ if (lingeringLaneSessions.size > 0) {
2491
+ execLog("batch", batchState.batchId, `killing ${lingeringLaneSessions.size} lingering lane agent session(s) before cleanup`);
2492
+ for (const sessionName of lingeringLaneSessions) {
2493
+ killV2LaneAgents(sessionName, {
2494
+ stateRoot,
2495
+ batchId: batchState.batchId,
2496
+ logContext: "batch",
2497
+ });
2498
+ }
2499
+ performedAgentCleanup = true;
2500
+ }
2501
+
2502
+ const killedMergeAgents = killAllMergeAgentsV2();
2503
+ if (killedMergeAgents > 0) {
2504
+ execLog("batch", batchState.batchId, `killed ${killedMergeAgents} lingering merge agent(s) before cleanup`);
2505
+ performedAgentCleanup = true;
2506
+ }
2507
+
2508
+ if (performedAgentCleanup) {
2488
2509
  sleepSync(1000); // Give OS time to release file locks
2489
2510
  }
2490
2511