taskplane 0.29.2 → 0.30.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.
Files changed (41) hide show
  1. package/bin/gitignore-patterns.mjs +11 -8
  2. package/bin/rpc-wrapper.mjs +410 -357
  3. package/bin/taskplane.mjs +533 -250
  4. package/extensions/reviewer-extension.ts +17 -11
  5. package/extensions/taskplane/abort.ts +50 -18
  6. package/extensions/taskplane/agent-bridge-extension.ts +232 -105
  7. package/extensions/taskplane/agent-host.ts +224 -97
  8. package/extensions/taskplane/cleanup.ts +71 -42
  9. package/extensions/taskplane/config-loader.ts +142 -58
  10. package/extensions/taskplane/config-schema.ts +6 -13
  11. package/extensions/taskplane/config.ts +10 -2
  12. package/extensions/taskplane/diagnostic-reports.ts +59 -47
  13. package/extensions/taskplane/diagnostics.ts +13 -13
  14. package/extensions/taskplane/discovery.ts +35 -61
  15. package/extensions/taskplane/engine-worker.ts +53 -46
  16. package/extensions/taskplane/engine.ts +1760 -602
  17. package/extensions/taskplane/execution.ts +426 -206
  18. package/extensions/taskplane/extension.ts +1073 -598
  19. package/extensions/taskplane/formatting.ts +136 -124
  20. package/extensions/taskplane/git.ts +0 -2
  21. package/extensions/taskplane/lane-runner.ts +542 -311
  22. package/extensions/taskplane/mailbox.ts +57 -49
  23. package/extensions/taskplane/merge.ts +662 -383
  24. package/extensions/taskplane/messages.ts +109 -51
  25. package/extensions/taskplane/migrations.ts +1 -1
  26. package/extensions/taskplane/path-resolver.ts +8 -9
  27. package/extensions/taskplane/persistence.ts +425 -262
  28. package/extensions/taskplane/process-registry.ts +36 -7
  29. package/extensions/taskplane/quality-gate.ts +107 -55
  30. package/extensions/taskplane/resume.ts +774 -267
  31. package/extensions/taskplane/sessions.ts +1 -1
  32. package/extensions/taskplane/settings-tui.ts +505 -164
  33. package/extensions/taskplane/sidecar-telemetry.ts +25 -10
  34. package/extensions/taskplane/supervisor.ts +477 -270
  35. package/extensions/taskplane/task-executor-core.ts +178 -53
  36. package/extensions/taskplane/types.ts +186 -108
  37. package/extensions/taskplane/verification.ts +27 -22
  38. package/extensions/taskplane/waves.ts +59 -43
  39. package/extensions/taskplane/workspace.ts +14 -12
  40. package/extensions/taskplane/worktree.ts +218 -196
  41. package/package.json +14 -2
@@ -24,6 +24,17 @@ export interface OrchestratorConfig {
24
24
  operator_id: string;
25
25
  /** How completed batches are integrated. manual = user runs /orch-integrate. supervised = supervisor proposes plan, asks confirmation. auto = supervisor executes without asking. */
26
26
  integration: "manual" | "supervised" | "auto";
27
+ /**
28
+ * Optional pre-resolved batch ID injected by callers that already
29
+ * know the batch identity (e.g., resumed orchestrations). When
30
+ * absent, callers fall back to the `ORCH_BATCH_ID` env var or a
31
+ * timestamp. Read by `executeLaneV2` (execution.ts).
32
+ *
33
+ * @since TP-195 (#TBD) — documented field that was already being
34
+ * read at runtime via `config.orchestrator?.batchId` and asserted
35
+ * by the source-grep invariant in `runtime-model-fallback.test.ts`.
36
+ */
37
+ batchId?: string;
27
38
  };
28
39
  dependencies: {
29
40
  source: "prompt" | "agent";
@@ -158,7 +169,11 @@ export function parseSegmentIdRepo(segment: { repoId: string }): string {
158
169
  /** Build a dynamic segment expansion request ID (`exp-{timestamp}-{random5}`). */
159
170
  export function buildExpansionRequestId(timestamp = Date.now()): string {
160
171
  const ts = Number.isFinite(timestamp) ? Math.floor(timestamp) : Date.now();
161
- const base = Math.random().toString(36).slice(2).toLowerCase().replace(/[^a-z0-9]/g, "");
172
+ const base = Math.random()
173
+ .toString(36)
174
+ .slice(2)
175
+ .toLowerCase()
176
+ .replace(/[^a-z0-9]/g, "");
162
177
  const random5 = (base + "00000").slice(0, 5);
163
178
  return `exp-${ts}-${random5}`;
164
179
  }
@@ -365,7 +380,6 @@ export interface PreflightCheck {
365
380
  hint?: string;
366
381
  }
367
382
 
368
-
369
383
  // ── Defaults ─────────────────────────────────────────────────────────
370
384
 
371
385
  export const DEFAULT_ORCHESTRATOR_CONFIG: OrchestratorConfig = {
@@ -428,7 +442,6 @@ export const DEFAULT_TASK_RUNNER_CONFIG: TaskRunnerConfig = {
428
442
  model_fallback: "inherit",
429
443
  };
430
444
 
431
-
432
445
  // ── Helpers ──────────────────────────────────────────────────────────
433
446
 
434
447
  export function freshBatchState(): BatchState {
@@ -598,7 +611,12 @@ export interface RemoveAllWorktreesResult {
598
611
  /** All per-worktree outcomes in order */
599
612
  outcomes: RemoveWorktreeOutcome[];
600
613
  /** Branches preserved (had unmerged commits) */
601
- preserved: Array<{ branch: string; savedBranch: string; laneNumber: number; unmergedCount?: number }>;
614
+ preserved: Array<{
615
+ branch: string;
616
+ savedBranch: string;
617
+ laneNumber: number;
618
+ unmergedCount?: number;
619
+ }>;
602
620
  }
603
621
 
604
622
  // ── Discovery Types ──────────────────────────────────────────────────
@@ -656,7 +674,6 @@ export interface DiscoveryResult {
656
674
  errors: DiscoveryError[];
657
675
  }
658
676
 
659
-
660
677
  // ── Wave Computation Types ───────────────────────────────────────────
661
678
 
662
679
  /** Dependency graph: adjacency list (task → tasks it depends on) */
@@ -683,7 +700,6 @@ export interface WaveComputationResult {
683
700
  segmentPlans?: TaskSegmentPlanMap;
684
701
  }
685
702
 
686
-
687
703
  // ── Lane Allocation (Phase 3) ────────────────────────────────────────
688
704
 
689
705
  /**
@@ -760,7 +776,6 @@ export interface AllocatedLane {
760
776
  repoId?: string;
761
777
  }
762
778
 
763
-
764
779
  // ── Execution Types & Contracts ──────────────────────────────────────
765
780
 
766
781
  /**
@@ -921,7 +936,8 @@ export type ExecutionErrorCode =
921
936
  | "EXEC_TASK_STAGE_FAILED"
922
937
  | "EXEC_TASK_COMMIT_FAILED"
923
938
  | "EXEC_TMUX_NOT_AVAILABLE"
924
- | "EXEC_WORKTREE_MISSING";
939
+ | "EXEC_WORKTREE_MISSING"
940
+ | "EXEC_MISSING_TASK_FOLDER";
925
941
 
926
942
  /** Typed error for lane execution failures. */
927
943
  export class ExecutionError extends Error {
@@ -938,7 +954,6 @@ export class ExecutionError extends Error {
938
954
  }
939
955
  }
940
956
 
941
-
942
957
  // ── Monitoring Types & Contracts ─────────────────────────────────────
943
958
 
944
959
  /**
@@ -1050,7 +1065,6 @@ export interface MtimeTracker {
1050
1065
  stallTimerStart: number | null;
1051
1066
  }
1052
1067
 
1053
-
1054
1068
  // ── Wave Execution Types & Contracts ─────────────────────────────────
1055
1069
 
1056
1070
  /**
@@ -1122,7 +1136,6 @@ export interface WaveExecutionResult {
1122
1136
  } | null;
1123
1137
  }
1124
1138
 
1125
-
1126
1139
  // ── Orchestrator Runtime State ───────────────────────────────────────
1127
1140
 
1128
1141
  /**
@@ -1135,7 +1148,16 @@ export interface WaveExecutionResult {
1135
1148
  * → paused (via /orch-pause)
1136
1149
  * Any active state → idle (via cleanup after completion/failure)
1137
1150
  */
1138
- export type OrchBatchPhase = "idle" | "launching" | "planning" | "executing" | "merging" | "paused" | "stopped" | "completed" | "failed";
1151
+ export type OrchBatchPhase =
1152
+ | "idle"
1153
+ | "launching"
1154
+ | "planning"
1155
+ | "executing"
1156
+ | "merging"
1157
+ | "paused"
1158
+ | "stopped"
1159
+ | "completed"
1160
+ | "failed";
1139
1161
 
1140
1162
  /**
1141
1163
  * Runtime state for a batch execution.
@@ -1288,14 +1310,17 @@ export function freshOrchBatchState(): OrchBatchRuntimeState {
1288
1310
  };
1289
1311
  }
1290
1312
 
1291
-
1292
1313
  // ── Merge Types ──────────────────────────────────────────────────────
1293
1314
 
1294
1315
  /**
1295
1316
  * Valid merge result statuses.
1296
1317
  * Matches the contract in .pi/agents/task-merger.md.
1297
1318
  */
1298
- export type MergeResultStatus = "SUCCESS" | "CONFLICT_RESOLVED" | "CONFLICT_UNRESOLVED" | "BUILD_FAILURE";
1319
+ export type MergeResultStatus =
1320
+ | "SUCCESS"
1321
+ | "CONFLICT_RESOLVED"
1322
+ | "CONFLICT_UNRESOLVED"
1323
+ | "BUILD_FAILURE";
1299
1324
 
1300
1325
  /** All valid status strings for runtime validation. */
1301
1326
  export const VALID_MERGE_STATUSES: ReadonlySet<string> = new Set([
@@ -1686,7 +1711,6 @@ export interface MergeSessionHealthState {
1686
1711
  deadEmitted: boolean;
1687
1712
  }
1688
1713
 
1689
-
1690
1714
  // ── Merge Retry Policy Matrix (TP-033 Step 2) ───────────────────────
1691
1715
 
1692
1716
  /**
@@ -1743,7 +1767,9 @@ export interface MergeRetryPolicy {
1743
1767
  *
1744
1768
  * @since TP-033
1745
1769
  */
1746
- export const MERGE_RETRY_POLICY_MATRIX: Readonly<Record<MergeFailureClassification, MergeRetryPolicy>> = {
1770
+ export const MERGE_RETRY_POLICY_MATRIX: Readonly<
1771
+ Record<MergeFailureClassification, MergeRetryPolicy>
1772
+ > = {
1747
1773
  verification_new_failure: {
1748
1774
  retriable: true,
1749
1775
  maxAttempts: 1,
@@ -1788,7 +1814,6 @@ export const MERGE_FAILURE_CLASSIFICATIONS: readonly MergeFailureClassification[
1788
1814
  "git_lock_file",
1789
1815
  ] as const;
1790
1816
 
1791
-
1792
1817
  // ── Tier 0 Watchdog Recovery Types (TP-039) ──────────────────────────
1793
1818
 
1794
1819
  /**
@@ -1923,7 +1948,11 @@ export interface EscalationContext {
1923
1948
  *
1924
1949
  * @since TP-039
1925
1950
  */
1926
- export function tier0ScopeKey(pattern: Tier0RecoveryPattern, taskId: string, waveIndex: number): string {
1951
+ export function tier0ScopeKey(
1952
+ pattern: Tier0RecoveryPattern,
1953
+ taskId: string,
1954
+ waveIndex: number,
1955
+ ): string {
1927
1956
  return `t0:${pattern}:${taskId}:w${waveIndex}`;
1928
1957
  }
1929
1958
 
@@ -2066,7 +2095,6 @@ export interface EngineEvent {
2066
2095
  */
2067
2096
  export type EngineEventCallback = (event: EngineEvent) => void;
2068
2097
 
2069
-
2070
2098
  // ── Supervisor Alert Types (TP-076) ──────────────────────────────────
2071
2099
 
2072
2100
  /**
@@ -2278,7 +2306,10 @@ export function buildSupervisorSegmentFrontierSnapshot(
2278
2306
  preferredSegmentId?: string | null,
2279
2307
  ): SupervisorSegmentFrontierSnapshot | undefined {
2280
2308
  const orderedSegmentIds = Array.isArray(segmentIds)
2281
- ? segmentIds.filter((segmentId): segmentId is string => typeof segmentId === "string" && segmentId.trim().length > 0)
2309
+ ? segmentIds.filter(
2310
+ (segmentId): segmentId is string =>
2311
+ typeof segmentId === "string" && segmentId.trim().length > 0,
2312
+ )
2282
2313
  : [];
2283
2314
  if (orderedSegmentIds.length === 0) return undefined;
2284
2315
 
@@ -2289,16 +2320,17 @@ export function buildSupervisorSegmentFrontierSnapshot(
2289
2320
  }
2290
2321
  }
2291
2322
 
2292
- const resolvedActiveSegmentId = (activeSegmentId && orderedSegmentIds.includes(activeSegmentId))
2293
- ? activeSegmentId
2294
- : (preferredSegmentId && orderedSegmentIds.includes(preferredSegmentId)
2295
- ? preferredSegmentId
2296
- : null);
2323
+ const resolvedActiveSegmentId =
2324
+ activeSegmentId && orderedSegmentIds.includes(activeSegmentId)
2325
+ ? activeSegmentId
2326
+ : preferredSegmentId && orderedSegmentIds.includes(preferredSegmentId)
2327
+ ? preferredSegmentId
2328
+ : null;
2297
2329
 
2298
2330
  const segments = orderedSegmentIds.map((segmentId) => {
2299
2331
  const persisted = bySegmentId.get(segmentId);
2300
- const status: PersistedSegmentStatus = persisted?.status
2301
- ?? (resolvedActiveSegmentId === segmentId ? "running" : "pending");
2332
+ const status: PersistedSegmentStatus =
2333
+ persisted?.status ?? (resolvedActiveSegmentId === segmentId ? "running" : "pending");
2302
2334
  return {
2303
2335
  segmentId,
2304
2336
  repoId: persisted ? parseSegmentIdRepo(persisted) : "unknown",
@@ -2307,11 +2339,12 @@ export function buildSupervisorSegmentFrontierSnapshot(
2307
2339
  };
2308
2340
  });
2309
2341
 
2310
- const terminalSegments = segments.filter((segment) =>
2311
- segment.status === "succeeded"
2312
- || segment.status === "failed"
2313
- || segment.status === "stalled"
2314
- || segment.status === "skipped",
2342
+ const terminalSegments = segments.filter(
2343
+ (segment) =>
2344
+ segment.status === "succeeded" ||
2345
+ segment.status === "failed" ||
2346
+ segment.status === "stalled" ||
2347
+ segment.status === "skipped",
2315
2348
  ).length;
2316
2349
 
2317
2350
  return {
@@ -2346,7 +2379,6 @@ export function buildEngineEventBase(
2346
2379
  };
2347
2380
  }
2348
2381
 
2349
-
2350
2382
  /**
2351
2383
  * Decision output from the merge retry policy evaluator.
2352
2384
  *
@@ -2383,50 +2415,50 @@ export interface MergeRetryDecision {
2383
2415
  */
2384
2416
  export type MergeRetryLoopOutcome =
2385
2417
  | {
2386
- /** Retry succeeded — caller should continue normal post-merge flow */
2387
- kind: "retry_succeeded";
2388
- mergeResult: MergeWaveResult;
2389
- /** Classification of the failure that was retried */
2390
- classification: MergeFailureClassification | null;
2391
- /** Scope key used for retry counter tracking */
2392
- scopeKey: string;
2393
- /** Last retry decision (carries attempt/maxAttempts for event emission) */
2394
- lastDecision: MergeRetryDecision;
2395
- }
2418
+ /** Retry succeeded — caller should continue normal post-merge flow */
2419
+ kind: "retry_succeeded";
2420
+ mergeResult: MergeWaveResult;
2421
+ /** Classification of the failure that was retried */
2422
+ classification: MergeFailureClassification | null;
2423
+ /** Scope key used for retry counter tracking */
2424
+ scopeKey: string;
2425
+ /** Last retry decision (carries attempt/maxAttempts for event emission) */
2426
+ lastDecision: MergeRetryDecision;
2427
+ }
2396
2428
  | {
2397
- /** Safe-stop triggered during retry — caller should break the wave loop */
2398
- kind: "safe_stop";
2399
- mergeResult: MergeWaveResult;
2400
- /** Classification of the failure that was retried */
2401
- classification: MergeFailureClassification | null;
2402
- /** Scope key used for retry counter tracking */
2403
- scopeKey: string;
2404
- /** Last retry decision (carries attempt/maxAttempts for event emission) */
2405
- lastDecision: MergeRetryDecision;
2406
- errorMessage: string;
2407
- notifyMessage: string;
2408
- }
2429
+ /** Safe-stop triggered during retry — caller should break the wave loop */
2430
+ kind: "safe_stop";
2431
+ mergeResult: MergeWaveResult;
2432
+ /** Classification of the failure that was retried */
2433
+ classification: MergeFailureClassification | null;
2434
+ /** Scope key used for retry counter tracking */
2435
+ scopeKey: string;
2436
+ /** Last retry decision (carries attempt/maxAttempts for event emission) */
2437
+ lastDecision: MergeRetryDecision;
2438
+ errorMessage: string;
2439
+ notifyMessage: string;
2440
+ }
2409
2441
  | {
2410
- /**
2411
- * Retry exhausted or failure is non-retriable — caller should
2412
- * force `paused` regardless of on_merge_failure config.
2413
- */
2414
- kind: "exhausted";
2415
- mergeResult: MergeWaveResult;
2416
- classification: MergeFailureClassification | null;
2417
- scopeKey: string;
2418
- lastDecision: MergeRetryDecision;
2419
- errorMessage: string;
2420
- notifyMessage: string;
2421
- }
2442
+ /**
2443
+ * Retry exhausted or failure is non-retriable — caller should
2444
+ * force `paused` regardless of on_merge_failure config.
2445
+ */
2446
+ kind: "exhausted";
2447
+ mergeResult: MergeWaveResult;
2448
+ classification: MergeFailureClassification | null;
2449
+ scopeKey: string;
2450
+ lastDecision: MergeRetryDecision;
2451
+ errorMessage: string;
2452
+ notifyMessage: string;
2453
+ }
2422
2454
  | {
2423
- /** No retry attempted (unclassifiable or non-retriable with 0 attempts).
2424
- * Caller should fall through to standard on_merge_failure policy. */
2425
- kind: "no_retry";
2426
- mergeResult: MergeWaveResult;
2427
- classification: MergeFailureClassification | null;
2428
- scopeKey: string;
2429
- };
2455
+ /** No retry attempted (unclassifiable or non-retriable with 0 attempts).
2456
+ * Caller should fall through to standard on_merge_failure policy. */
2457
+ kind: "no_retry";
2458
+ mergeResult: MergeWaveResult;
2459
+ classification: MergeFailureClassification | null;
2460
+ scopeKey: string;
2461
+ };
2430
2462
 
2431
2463
  /**
2432
2464
  * Callbacks provided to `applyMergeRetryLoop()` for side effects
@@ -2510,7 +2542,6 @@ export interface OrchDashboardViewModel {
2510
2542
  failurePolicy: string | null; // e.g., "stop-wave" if stopped by policy
2511
2543
  }
2512
2544
 
2513
-
2514
2545
  // ── State Persistence Types (TS-009) ─────────────────────────────────
2515
2546
 
2516
2547
  // ── v3 Resilience & Diagnostics Sections (TP-030) ────────────────────
@@ -2832,7 +2863,13 @@ export interface PersistedTaskRecord {
2832
2863
  *
2833
2864
  * @since v4 (TP-081)
2834
2865
  */
2835
- export type PersistedSegmentStatus = "pending" | "running" | "succeeded" | "failed" | "stalled" | "skipped";
2866
+ export type PersistedSegmentStatus =
2867
+ | "pending"
2868
+ | "running"
2869
+ | "succeeded"
2870
+ | "failed"
2871
+ | "stalled"
2872
+ | "skipped";
2836
2873
 
2837
2874
  /**
2838
2875
  * Persisted record of a single segment's execution state.
@@ -3095,7 +3132,6 @@ export interface PersistedBatchState {
3095
3132
  _extraFields?: Record<string, unknown>;
3096
3133
  }
3097
3134
 
3098
-
3099
3135
  // ── Resume (TS-009 Step 4) ───────────────────────────────────────────
3100
3136
 
3101
3137
  /**
@@ -3313,10 +3349,7 @@ export const DURATION_BASE_MINUTES = 30;
3313
3349
  * Get estimated duration in minutes for a task size.
3314
3350
  * Uses explicit mapping, falling back to weight × base.
3315
3351
  */
3316
- export function getTaskDurationMinutes(
3317
- size: string,
3318
- sizeWeights: Record<string, number>,
3319
- ): number {
3352
+ export function getTaskDurationMinutes(size: string, sizeWeights: Record<string, number>): number {
3320
3353
  if (SIZE_DURATION_MINUTES[size] !== undefined) {
3321
3354
  return SIZE_DURATION_MINUTES[size];
3322
3355
  }
@@ -3324,7 +3357,6 @@ export function getTaskDurationMinutes(
3324
3357
  return weight * DURATION_BASE_MINUTES;
3325
3358
  }
3326
3359
 
3327
-
3328
3360
  // ── Batch History ────────────────────────────────────────────────────
3329
3361
 
3330
3362
  /** Token counts for a task, wave, or batch. */
@@ -3341,8 +3373,8 @@ export interface BatchTaskSummary {
3341
3373
  taskId: string;
3342
3374
  taskName: string;
3343
3375
  status: "succeeded" | "failed" | "skipped" | "blocked" | "stalled" | "pending";
3344
- wave: number; // 1-based
3345
- lane: number; // 1-based
3376
+ wave: number; // 1-based
3377
+ lane: number; // 1-based
3346
3378
  durationMs: number;
3347
3379
  tokens: TokenCounts;
3348
3380
  exitReason: string | null;
@@ -3350,8 +3382,8 @@ export interface BatchTaskSummary {
3350
3382
 
3351
3383
  /** Per-wave summary for history. */
3352
3384
  export interface BatchWaveSummary {
3353
- wave: number; // 1-based
3354
- tasks: string[]; // task IDs
3385
+ wave: number; // 1-based
3386
+ tasks: string[]; // task IDs
3355
3387
  mergeStatus: "succeeded" | "failed" | "partial" | "skipped";
3356
3388
  durationMs: number;
3357
3389
  tokens: TokenCounts;
@@ -3380,7 +3412,6 @@ export interface BatchHistorySummary {
3380
3412
  /** Max number of batch history entries to retain. */
3381
3413
  export const BATCH_HISTORY_MAX_ENTRIES = 100;
3382
3414
 
3383
-
3384
3415
  // ── Workspace Mode Types ─────────────────────────────────────────────
3385
3416
 
3386
3417
  /**
@@ -3518,7 +3549,6 @@ export interface ExecutionContext {
3518
3549
  pointer: PointerResolution | null;
3519
3550
  }
3520
3551
 
3521
-
3522
3552
  // ── Workspace Validation Error Types ─────────────────────────────────
3523
3553
 
3524
3554
  /**
@@ -3560,7 +3590,7 @@ export type WorkspaceConfigErrorCode =
3560
3590
  | "WORKSPACE_TASK_AREA_OUTSIDE_TASKS_ROOT"
3561
3591
  | "WORKSPACE_SETUP_REQUIRED"
3562
3592
  | "WORKSPACE_DUPLICATE_REPO_PATH"
3563
- | "WORKSPACE_SCHEMA_INVALID";/**
3593
+ | "WORKSPACE_SCHEMA_INVALID"; /**
3564
3594
  * Typed error class for workspace configuration failures.
3565
3595
  *
3566
3596
  * Thrown during workspace config loading/validation when the config file
@@ -3577,7 +3607,12 @@ export class WorkspaceConfigError extends Error {
3577
3607
  /** Optional filesystem path related to the error */
3578
3608
  relatedPath?: string;
3579
3609
 
3580
- constructor(code: WorkspaceConfigErrorCode, message: string, repoId?: string, relatedPath?: string) {
3610
+ constructor(
3611
+ code: WorkspaceConfigErrorCode,
3612
+ message: string,
3613
+ repoId?: string,
3614
+ relatedPath?: string,
3615
+ ) {
3581
3616
  super(message);
3582
3617
  this.name = "WorkspaceConfigError";
3583
3618
  this.code = code;
@@ -3586,7 +3621,6 @@ export class WorkspaceConfigError extends Error {
3586
3621
  }
3587
3622
  }
3588
3623
 
3589
-
3590
3624
  // ── Pointer Resolution Types ─────────────────────────────────────────
3591
3625
 
3592
3626
  /**
@@ -3653,7 +3687,6 @@ export interface PointerResolution {
3653
3687
  warning?: string;
3654
3688
  }
3655
3689
 
3656
-
3657
3690
  // ── Workspace Defaults ───────────────────────────────────────────────
3658
3691
 
3659
3692
  /**
@@ -3697,7 +3730,6 @@ export function createRepoModeContext(
3697
3730
  };
3698
3731
  }
3699
3732
 
3700
-
3701
3733
  // ── Agent Mailbox Types (TP-089) ─────────────────────────────────────
3702
3734
 
3703
3735
  /**
@@ -3735,7 +3767,12 @@ export type MailboxMessageType = "steer" | "query" | "abort" | "info" | "reply"
3735
3767
  * @since TP-089
3736
3768
  */
3737
3769
  export const MAILBOX_MESSAGE_TYPES: ReadonlySet<string> = new Set<MailboxMessageType>([
3738
- "steer", "query", "abort", "info", "reply", "escalate",
3770
+ "steer",
3771
+ "query",
3772
+ "abort",
3773
+ "info",
3774
+ "reply",
3775
+ "escalate",
3739
3776
  ]);
3740
3777
 
3741
3778
  /**
@@ -3838,7 +3875,10 @@ export type RuntimeAgentStatus =
3838
3875
 
3839
3876
  /** Set of terminal agent statuses (process is no longer alive). @since TP-102 */
3840
3877
  export const TERMINAL_AGENT_STATUSES: ReadonlySet<RuntimeAgentStatus> = new Set([
3841
- "exited", "crashed", "timed_out", "killed",
3878
+ "exited",
3879
+ "crashed",
3880
+ "timed_out",
3881
+ "killed",
3842
3882
  ]);
3843
3883
 
3844
3884
  /**
@@ -4173,7 +4213,11 @@ export function runtimeRoot(stateRoot: string, batchId: string): string {
4173
4213
  *
4174
4214
  * @since TP-102
4175
4215
  */
4176
- export function runtimeAgentDir(stateRoot: string, batchId: string, agentId: RuntimeAgentId): string {
4216
+ export function runtimeAgentDir(
4217
+ stateRoot: string,
4218
+ batchId: string,
4219
+ agentId: RuntimeAgentId,
4220
+ ): string {
4177
4221
  return `${stateRoot}/.pi/runtime/${batchId}/agents/${agentId}`;
4178
4222
  }
4179
4223
 
@@ -4182,7 +4226,11 @@ export function runtimeAgentDir(stateRoot: string, batchId: string, agentId: Run
4182
4226
  *
4183
4227
  * @since TP-102
4184
4228
  */
4185
- export function runtimeManifestPath(stateRoot: string, batchId: string, agentId: RuntimeAgentId): string {
4229
+ export function runtimeManifestPath(
4230
+ stateRoot: string,
4231
+ batchId: string,
4232
+ agentId: RuntimeAgentId,
4233
+ ): string {
4186
4234
  return `${runtimeAgentDir(stateRoot, batchId, agentId)}/manifest.json`;
4187
4235
  }
4188
4236
 
@@ -4191,7 +4239,11 @@ export function runtimeManifestPath(stateRoot: string, batchId: string, agentId:
4191
4239
  *
4192
4240
  * @since TP-102
4193
4241
  */
4194
- export function runtimeAgentEventsPath(stateRoot: string, batchId: string, agentId: RuntimeAgentId): string {
4242
+ export function runtimeAgentEventsPath(
4243
+ stateRoot: string,
4244
+ batchId: string,
4245
+ agentId: RuntimeAgentId,
4246
+ ): string {
4195
4247
  return `${runtimeAgentDir(stateRoot, batchId, agentId)}/events.jsonl`;
4196
4248
  }
4197
4249
 
@@ -4200,7 +4252,11 @@ export function runtimeAgentEventsPath(stateRoot: string, batchId: string, agent
4200
4252
  *
4201
4253
  * @since TP-102
4202
4254
  */
4203
- export function runtimeLaneSnapshotPath(stateRoot: string, batchId: string, laneNumber: number): string {
4255
+ export function runtimeLaneSnapshotPath(
4256
+ stateRoot: string,
4257
+ batchId: string,
4258
+ laneNumber: number,
4259
+ ): string {
4204
4260
  return `${stateRoot}/.pi/runtime/${batchId}/lanes/lane-${laneNumber}.json`;
4205
4261
  }
4206
4262
 
@@ -4244,7 +4300,11 @@ export interface RuntimeMergeSnapshot {
4244
4300
  *
4245
4301
  * @since TP-164
4246
4302
  */
4247
- export function runtimeMergeSnapshotPath(stateRoot: string, batchId: string, mergeNumber: number): string {
4303
+ export function runtimeMergeSnapshotPath(
4304
+ stateRoot: string,
4305
+ batchId: string,
4306
+ mergeNumber: number,
4307
+ ): string {
4248
4308
  return `${stateRoot}/.pi/runtime/${batchId}/lanes/merge-${mergeNumber}.json`;
4249
4309
  }
4250
4310
 
@@ -4309,15 +4369,28 @@ export function validateAgentManifest(manifest: unknown): string[] {
4309
4369
  if (typeof m.role !== "string") errors.push("role must be a string");
4310
4370
  else {
4311
4371
  const validRoles: ReadonlySet<string> = new Set(["worker", "reviewer", "merger", "lane-runner"]);
4312
- if (!validRoles.has(m.role as string)) errors.push(`role must be one of: ${[...validRoles].join(", ")}`);
4372
+ if (!validRoles.has(m.role as string))
4373
+ errors.push(`role must be one of: ${[...validRoles].join(", ")}`);
4313
4374
  }
4314
- if (typeof m.pid !== "number" || !Number.isFinite(m.pid) || m.pid <= 0) errors.push("pid must be a positive finite number");
4315
- if (typeof m.parentPid !== "number" || !Number.isFinite(m.parentPid) || m.parentPid <= 0) errors.push("parentPid must be a positive finite number");
4316
- if (typeof m.startedAt !== "number" || !Number.isFinite(m.startedAt)) errors.push("startedAt must be a finite number");
4375
+ if (typeof m.pid !== "number" || !Number.isFinite(m.pid) || m.pid <= 0)
4376
+ errors.push("pid must be a positive finite number");
4377
+ if (typeof m.parentPid !== "number" || !Number.isFinite(m.parentPid) || m.parentPid <= 0)
4378
+ errors.push("parentPid must be a positive finite number");
4379
+ if (typeof m.startedAt !== "number" || !Number.isFinite(m.startedAt))
4380
+ errors.push("startedAt must be a finite number");
4317
4381
  if (typeof m.status !== "string") errors.push("status must be a string");
4318
4382
  else {
4319
- const validStatuses: ReadonlySet<string> = new Set(["spawning", "running", "wrapping_up", "exited", "crashed", "timed_out", "killed"]);
4320
- if (!validStatuses.has(m.status as string)) errors.push(`status must be one of: ${[...validStatuses].join(", ")}`);
4383
+ const validStatuses: ReadonlySet<string> = new Set([
4384
+ "spawning",
4385
+ "running",
4386
+ "wrapping_up",
4387
+ "exited",
4388
+ "crashed",
4389
+ "timed_out",
4390
+ "killed",
4391
+ ]);
4392
+ if (!validStatuses.has(m.status as string))
4393
+ errors.push(`status must be one of: ${[...validStatuses].join(", ")}`);
4321
4394
  }
4322
4395
  if (typeof m.cwd !== "string" || !m.cwd) errors.push("cwd must be a non-empty string");
4323
4396
  if (typeof m.repoId !== "string") errors.push("repoId must be a string");
@@ -4339,7 +4412,13 @@ export function validatePacketPaths(packet: unknown): string[] {
4339
4412
  }
4340
4413
  const p = packet as Record<string, unknown>;
4341
4414
 
4342
- for (const field of ["promptPath", "statusPath", "donePath", "reviewsDir", "taskFolder"] as const) {
4415
+ for (const field of [
4416
+ "promptPath",
4417
+ "statusPath",
4418
+ "donePath",
4419
+ "reviewsDir",
4420
+ "taskFolder",
4421
+ ] as const) {
4343
4422
  if (typeof p[field] !== "string" || !(p[field] as string)) {
4344
4423
  errors.push(`${field} must be a non-empty string`);
4345
4424
  }
@@ -4347,4 +4426,3 @@ export function validatePacketPaths(packet: unknown): string[] {
4347
4426
 
4348
4427
  return errors;
4349
4428
  }
4350
-