taskplane 0.29.2 → 0.30.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.
- package/bin/gitignore-patterns.mjs +11 -8
- package/bin/rpc-wrapper.mjs +410 -357
- package/bin/taskplane.mjs +533 -250
- package/dashboard/public/app.js +124 -15
- package/dashboard/public/style.css +83 -2
- package/extensions/reviewer-extension.ts +17 -11
- package/extensions/taskplane/abort.ts +50 -18
- package/extensions/taskplane/agent-bridge-extension.ts +232 -105
- package/extensions/taskplane/agent-host.ts +224 -97
- package/extensions/taskplane/cleanup.ts +71 -42
- package/extensions/taskplane/config-loader.ts +142 -58
- package/extensions/taskplane/config-schema.ts +6 -13
- package/extensions/taskplane/config.ts +10 -2
- package/extensions/taskplane/diagnostic-reports.ts +59 -47
- package/extensions/taskplane/diagnostics.ts +13 -13
- package/extensions/taskplane/discovery.ts +78 -63
- package/extensions/taskplane/engine-worker.ts +53 -46
- package/extensions/taskplane/engine.ts +1760 -602
- package/extensions/taskplane/execution.ts +469 -207
- package/extensions/taskplane/extension.ts +1073 -598
- package/extensions/taskplane/formatting.ts +136 -124
- package/extensions/taskplane/git.ts +0 -2
- package/extensions/taskplane/lane-runner.ts +652 -319
- package/extensions/taskplane/mailbox.ts +57 -49
- package/extensions/taskplane/merge.ts +662 -383
- package/extensions/taskplane/messages.ts +109 -51
- package/extensions/taskplane/migrations.ts +1 -1
- package/extensions/taskplane/path-resolver.ts +8 -9
- package/extensions/taskplane/persistence.ts +425 -262
- package/extensions/taskplane/process-registry.ts +36 -7
- package/extensions/taskplane/quality-gate.ts +107 -55
- package/extensions/taskplane/resume.ts +832 -280
- package/extensions/taskplane/sessions.ts +1 -1
- package/extensions/taskplane/settings-tui.ts +505 -164
- package/extensions/taskplane/sidecar-telemetry.ts +25 -10
- package/extensions/taskplane/supervisor.ts +477 -270
- package/extensions/taskplane/task-executor-core.ts +178 -53
- package/extensions/taskplane/types.ts +209 -108
- package/extensions/taskplane/verification.ts +27 -22
- package/extensions/taskplane/waves.ts +59 -43
- package/extensions/taskplane/workspace.ts +14 -12
- package/extensions/taskplane/worktree.ts +218 -196
- 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,13 +169,40 @@ 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()
|
|
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
|
}
|
|
165
180
|
|
|
166
181
|
// ── Step-Segment Mapping (Phase A: segment-scoped worker visibility) ────
|
|
167
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Authoritative segment-scope mode for a single worker iteration.
|
|
185
|
+
*
|
|
186
|
+
* - `FULL_TASK`: the worker sees the entire PROMPT.md, all steps, all checkboxes.
|
|
187
|
+
* No `Active segment ID` / `Your checkboxes for this step` prose is injected.
|
|
188
|
+
* Segment-related environment variables (`TASKPLANE_ACTIVE_SEGMENT_ID`,
|
|
189
|
+
* `TASKPLANE_SEGMENT_ID`) are hard-cleared so that runtime tools keyed on
|
|
190
|
+
* them (e.g., `request_segment_expansion`) cannot accidentally register.
|
|
191
|
+
*
|
|
192
|
+
* - `SEGMENT_SCOPED`: the worker is iterating a specific segment of a
|
|
193
|
+
* multi-segment task. Only that segment's steps and checkboxes are shown;
|
|
194
|
+
* `Active segment ID` is announced; segment-related env vars carry the
|
|
195
|
+
* active `segmentId`; the segment-overlay system prompt is appended.
|
|
196
|
+
*
|
|
197
|
+
* This is the single authoritative flag for the segment-scope decision
|
|
198
|
+
* (TP-196 / #502). Call sites should derive their behaviour from this mode
|
|
199
|
+
* rather than re-evaluating the underlying boolean conditions, which prevents
|
|
200
|
+
* the multiple branches drifting out of sync.
|
|
201
|
+
*
|
|
202
|
+
* @since TP-196
|
|
203
|
+
*/
|
|
204
|
+
export type SegmentScopeMode = "FULL_TASK" | "SEGMENT_SCOPED";
|
|
205
|
+
|
|
168
206
|
/** A group of checkboxes scoped to a single repo within a step. */
|
|
169
207
|
export interface SegmentCheckboxGroup {
|
|
170
208
|
repoId: string;
|
|
@@ -365,7 +403,6 @@ export interface PreflightCheck {
|
|
|
365
403
|
hint?: string;
|
|
366
404
|
}
|
|
367
405
|
|
|
368
|
-
|
|
369
406
|
// ── Defaults ─────────────────────────────────────────────────────────
|
|
370
407
|
|
|
371
408
|
export const DEFAULT_ORCHESTRATOR_CONFIG: OrchestratorConfig = {
|
|
@@ -428,7 +465,6 @@ export const DEFAULT_TASK_RUNNER_CONFIG: TaskRunnerConfig = {
|
|
|
428
465
|
model_fallback: "inherit",
|
|
429
466
|
};
|
|
430
467
|
|
|
431
|
-
|
|
432
468
|
// ── Helpers ──────────────────────────────────────────────────────────
|
|
433
469
|
|
|
434
470
|
export function freshBatchState(): BatchState {
|
|
@@ -598,7 +634,12 @@ export interface RemoveAllWorktreesResult {
|
|
|
598
634
|
/** All per-worktree outcomes in order */
|
|
599
635
|
outcomes: RemoveWorktreeOutcome[];
|
|
600
636
|
/** Branches preserved (had unmerged commits) */
|
|
601
|
-
preserved: Array<{
|
|
637
|
+
preserved: Array<{
|
|
638
|
+
branch: string;
|
|
639
|
+
savedBranch: string;
|
|
640
|
+
laneNumber: number;
|
|
641
|
+
unmergedCount?: number;
|
|
642
|
+
}>;
|
|
602
643
|
}
|
|
603
644
|
|
|
604
645
|
// ── Discovery Types ──────────────────────────────────────────────────
|
|
@@ -656,7 +697,6 @@ export interface DiscoveryResult {
|
|
|
656
697
|
errors: DiscoveryError[];
|
|
657
698
|
}
|
|
658
699
|
|
|
659
|
-
|
|
660
700
|
// ── Wave Computation Types ───────────────────────────────────────────
|
|
661
701
|
|
|
662
702
|
/** Dependency graph: adjacency list (task → tasks it depends on) */
|
|
@@ -683,7 +723,6 @@ export interface WaveComputationResult {
|
|
|
683
723
|
segmentPlans?: TaskSegmentPlanMap;
|
|
684
724
|
}
|
|
685
725
|
|
|
686
|
-
|
|
687
726
|
// ── Lane Allocation (Phase 3) ────────────────────────────────────────
|
|
688
727
|
|
|
689
728
|
/**
|
|
@@ -760,7 +799,6 @@ export interface AllocatedLane {
|
|
|
760
799
|
repoId?: string;
|
|
761
800
|
}
|
|
762
801
|
|
|
763
|
-
|
|
764
802
|
// ── Execution Types & Contracts ──────────────────────────────────────
|
|
765
803
|
|
|
766
804
|
/**
|
|
@@ -921,7 +959,8 @@ export type ExecutionErrorCode =
|
|
|
921
959
|
| "EXEC_TASK_STAGE_FAILED"
|
|
922
960
|
| "EXEC_TASK_COMMIT_FAILED"
|
|
923
961
|
| "EXEC_TMUX_NOT_AVAILABLE"
|
|
924
|
-
| "EXEC_WORKTREE_MISSING"
|
|
962
|
+
| "EXEC_WORKTREE_MISSING"
|
|
963
|
+
| "EXEC_MISSING_TASK_FOLDER";
|
|
925
964
|
|
|
926
965
|
/** Typed error for lane execution failures. */
|
|
927
966
|
export class ExecutionError extends Error {
|
|
@@ -938,7 +977,6 @@ export class ExecutionError extends Error {
|
|
|
938
977
|
}
|
|
939
978
|
}
|
|
940
979
|
|
|
941
|
-
|
|
942
980
|
// ── Monitoring Types & Contracts ─────────────────────────────────────
|
|
943
981
|
|
|
944
982
|
/**
|
|
@@ -1050,7 +1088,6 @@ export interface MtimeTracker {
|
|
|
1050
1088
|
stallTimerStart: number | null;
|
|
1051
1089
|
}
|
|
1052
1090
|
|
|
1053
|
-
|
|
1054
1091
|
// ── Wave Execution Types & Contracts ─────────────────────────────────
|
|
1055
1092
|
|
|
1056
1093
|
/**
|
|
@@ -1122,7 +1159,6 @@ export interface WaveExecutionResult {
|
|
|
1122
1159
|
} | null;
|
|
1123
1160
|
}
|
|
1124
1161
|
|
|
1125
|
-
|
|
1126
1162
|
// ── Orchestrator Runtime State ───────────────────────────────────────
|
|
1127
1163
|
|
|
1128
1164
|
/**
|
|
@@ -1135,7 +1171,16 @@ export interface WaveExecutionResult {
|
|
|
1135
1171
|
* → paused (via /orch-pause)
|
|
1136
1172
|
* Any active state → idle (via cleanup after completion/failure)
|
|
1137
1173
|
*/
|
|
1138
|
-
export type OrchBatchPhase =
|
|
1174
|
+
export type OrchBatchPhase =
|
|
1175
|
+
| "idle"
|
|
1176
|
+
| "launching"
|
|
1177
|
+
| "planning"
|
|
1178
|
+
| "executing"
|
|
1179
|
+
| "merging"
|
|
1180
|
+
| "paused"
|
|
1181
|
+
| "stopped"
|
|
1182
|
+
| "completed"
|
|
1183
|
+
| "failed";
|
|
1139
1184
|
|
|
1140
1185
|
/**
|
|
1141
1186
|
* Runtime state for a batch execution.
|
|
@@ -1288,14 +1333,17 @@ export function freshOrchBatchState(): OrchBatchRuntimeState {
|
|
|
1288
1333
|
};
|
|
1289
1334
|
}
|
|
1290
1335
|
|
|
1291
|
-
|
|
1292
1336
|
// ── Merge Types ──────────────────────────────────────────────────────
|
|
1293
1337
|
|
|
1294
1338
|
/**
|
|
1295
1339
|
* Valid merge result statuses.
|
|
1296
1340
|
* Matches the contract in .pi/agents/task-merger.md.
|
|
1297
1341
|
*/
|
|
1298
|
-
export type MergeResultStatus =
|
|
1342
|
+
export type MergeResultStatus =
|
|
1343
|
+
| "SUCCESS"
|
|
1344
|
+
| "CONFLICT_RESOLVED"
|
|
1345
|
+
| "CONFLICT_UNRESOLVED"
|
|
1346
|
+
| "BUILD_FAILURE";
|
|
1299
1347
|
|
|
1300
1348
|
/** All valid status strings for runtime validation. */
|
|
1301
1349
|
export const VALID_MERGE_STATUSES: ReadonlySet<string> = new Set([
|
|
@@ -1686,7 +1734,6 @@ export interface MergeSessionHealthState {
|
|
|
1686
1734
|
deadEmitted: boolean;
|
|
1687
1735
|
}
|
|
1688
1736
|
|
|
1689
|
-
|
|
1690
1737
|
// ── Merge Retry Policy Matrix (TP-033 Step 2) ───────────────────────
|
|
1691
1738
|
|
|
1692
1739
|
/**
|
|
@@ -1743,7 +1790,9 @@ export interface MergeRetryPolicy {
|
|
|
1743
1790
|
*
|
|
1744
1791
|
* @since TP-033
|
|
1745
1792
|
*/
|
|
1746
|
-
export const MERGE_RETRY_POLICY_MATRIX: Readonly<
|
|
1793
|
+
export const MERGE_RETRY_POLICY_MATRIX: Readonly<
|
|
1794
|
+
Record<MergeFailureClassification, MergeRetryPolicy>
|
|
1795
|
+
> = {
|
|
1747
1796
|
verification_new_failure: {
|
|
1748
1797
|
retriable: true,
|
|
1749
1798
|
maxAttempts: 1,
|
|
@@ -1788,7 +1837,6 @@ export const MERGE_FAILURE_CLASSIFICATIONS: readonly MergeFailureClassification[
|
|
|
1788
1837
|
"git_lock_file",
|
|
1789
1838
|
] as const;
|
|
1790
1839
|
|
|
1791
|
-
|
|
1792
1840
|
// ── Tier 0 Watchdog Recovery Types (TP-039) ──────────────────────────
|
|
1793
1841
|
|
|
1794
1842
|
/**
|
|
@@ -1923,7 +1971,11 @@ export interface EscalationContext {
|
|
|
1923
1971
|
*
|
|
1924
1972
|
* @since TP-039
|
|
1925
1973
|
*/
|
|
1926
|
-
export function tier0ScopeKey(
|
|
1974
|
+
export function tier0ScopeKey(
|
|
1975
|
+
pattern: Tier0RecoveryPattern,
|
|
1976
|
+
taskId: string,
|
|
1977
|
+
waveIndex: number,
|
|
1978
|
+
): string {
|
|
1927
1979
|
return `t0:${pattern}:${taskId}:w${waveIndex}`;
|
|
1928
1980
|
}
|
|
1929
1981
|
|
|
@@ -2066,7 +2118,6 @@ export interface EngineEvent {
|
|
|
2066
2118
|
*/
|
|
2067
2119
|
export type EngineEventCallback = (event: EngineEvent) => void;
|
|
2068
2120
|
|
|
2069
|
-
|
|
2070
2121
|
// ── Supervisor Alert Types (TP-076) ──────────────────────────────────
|
|
2071
2122
|
|
|
2072
2123
|
/**
|
|
@@ -2278,7 +2329,10 @@ export function buildSupervisorSegmentFrontierSnapshot(
|
|
|
2278
2329
|
preferredSegmentId?: string | null,
|
|
2279
2330
|
): SupervisorSegmentFrontierSnapshot | undefined {
|
|
2280
2331
|
const orderedSegmentIds = Array.isArray(segmentIds)
|
|
2281
|
-
? segmentIds.filter(
|
|
2332
|
+
? segmentIds.filter(
|
|
2333
|
+
(segmentId): segmentId is string =>
|
|
2334
|
+
typeof segmentId === "string" && segmentId.trim().length > 0,
|
|
2335
|
+
)
|
|
2282
2336
|
: [];
|
|
2283
2337
|
if (orderedSegmentIds.length === 0) return undefined;
|
|
2284
2338
|
|
|
@@ -2289,16 +2343,17 @@ export function buildSupervisorSegmentFrontierSnapshot(
|
|
|
2289
2343
|
}
|
|
2290
2344
|
}
|
|
2291
2345
|
|
|
2292
|
-
const resolvedActiveSegmentId =
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2346
|
+
const resolvedActiveSegmentId =
|
|
2347
|
+
activeSegmentId && orderedSegmentIds.includes(activeSegmentId)
|
|
2348
|
+
? activeSegmentId
|
|
2349
|
+
: preferredSegmentId && orderedSegmentIds.includes(preferredSegmentId)
|
|
2350
|
+
? preferredSegmentId
|
|
2351
|
+
: null;
|
|
2297
2352
|
|
|
2298
2353
|
const segments = orderedSegmentIds.map((segmentId) => {
|
|
2299
2354
|
const persisted = bySegmentId.get(segmentId);
|
|
2300
|
-
const status: PersistedSegmentStatus =
|
|
2301
|
-
?? (resolvedActiveSegmentId === segmentId ? "running" : "pending");
|
|
2355
|
+
const status: PersistedSegmentStatus =
|
|
2356
|
+
persisted?.status ?? (resolvedActiveSegmentId === segmentId ? "running" : "pending");
|
|
2302
2357
|
return {
|
|
2303
2358
|
segmentId,
|
|
2304
2359
|
repoId: persisted ? parseSegmentIdRepo(persisted) : "unknown",
|
|
@@ -2307,11 +2362,12 @@ export function buildSupervisorSegmentFrontierSnapshot(
|
|
|
2307
2362
|
};
|
|
2308
2363
|
});
|
|
2309
2364
|
|
|
2310
|
-
const terminalSegments = segments.filter(
|
|
2311
|
-
segment
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2365
|
+
const terminalSegments = segments.filter(
|
|
2366
|
+
(segment) =>
|
|
2367
|
+
segment.status === "succeeded" ||
|
|
2368
|
+
segment.status === "failed" ||
|
|
2369
|
+
segment.status === "stalled" ||
|
|
2370
|
+
segment.status === "skipped",
|
|
2315
2371
|
).length;
|
|
2316
2372
|
|
|
2317
2373
|
return {
|
|
@@ -2346,7 +2402,6 @@ export function buildEngineEventBase(
|
|
|
2346
2402
|
};
|
|
2347
2403
|
}
|
|
2348
2404
|
|
|
2349
|
-
|
|
2350
2405
|
/**
|
|
2351
2406
|
* Decision output from the merge retry policy evaluator.
|
|
2352
2407
|
*
|
|
@@ -2383,50 +2438,50 @@ export interface MergeRetryDecision {
|
|
|
2383
2438
|
*/
|
|
2384
2439
|
export type MergeRetryLoopOutcome =
|
|
2385
2440
|
| {
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2441
|
+
/** Retry succeeded — caller should continue normal post-merge flow */
|
|
2442
|
+
kind: "retry_succeeded";
|
|
2443
|
+
mergeResult: MergeWaveResult;
|
|
2444
|
+
/** Classification of the failure that was retried */
|
|
2445
|
+
classification: MergeFailureClassification | null;
|
|
2446
|
+
/** Scope key used for retry counter tracking */
|
|
2447
|
+
scopeKey: string;
|
|
2448
|
+
/** Last retry decision (carries attempt/maxAttempts for event emission) */
|
|
2449
|
+
lastDecision: MergeRetryDecision;
|
|
2450
|
+
}
|
|
2396
2451
|
| {
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2452
|
+
/** Safe-stop triggered during retry — caller should break the wave loop */
|
|
2453
|
+
kind: "safe_stop";
|
|
2454
|
+
mergeResult: MergeWaveResult;
|
|
2455
|
+
/** Classification of the failure that was retried */
|
|
2456
|
+
classification: MergeFailureClassification | null;
|
|
2457
|
+
/** Scope key used for retry counter tracking */
|
|
2458
|
+
scopeKey: string;
|
|
2459
|
+
/** Last retry decision (carries attempt/maxAttempts for event emission) */
|
|
2460
|
+
lastDecision: MergeRetryDecision;
|
|
2461
|
+
errorMessage: string;
|
|
2462
|
+
notifyMessage: string;
|
|
2463
|
+
}
|
|
2409
2464
|
| {
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2465
|
+
/**
|
|
2466
|
+
* Retry exhausted or failure is non-retriable — caller should
|
|
2467
|
+
* force `paused` regardless of on_merge_failure config.
|
|
2468
|
+
*/
|
|
2469
|
+
kind: "exhausted";
|
|
2470
|
+
mergeResult: MergeWaveResult;
|
|
2471
|
+
classification: MergeFailureClassification | null;
|
|
2472
|
+
scopeKey: string;
|
|
2473
|
+
lastDecision: MergeRetryDecision;
|
|
2474
|
+
errorMessage: string;
|
|
2475
|
+
notifyMessage: string;
|
|
2476
|
+
}
|
|
2422
2477
|
| {
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2478
|
+
/** No retry attempted (unclassifiable or non-retriable with 0 attempts).
|
|
2479
|
+
* Caller should fall through to standard on_merge_failure policy. */
|
|
2480
|
+
kind: "no_retry";
|
|
2481
|
+
mergeResult: MergeWaveResult;
|
|
2482
|
+
classification: MergeFailureClassification | null;
|
|
2483
|
+
scopeKey: string;
|
|
2484
|
+
};
|
|
2430
2485
|
|
|
2431
2486
|
/**
|
|
2432
2487
|
* Callbacks provided to `applyMergeRetryLoop()` for side effects
|
|
@@ -2510,7 +2565,6 @@ export interface OrchDashboardViewModel {
|
|
|
2510
2565
|
failurePolicy: string | null; // e.g., "stop-wave" if stopped by policy
|
|
2511
2566
|
}
|
|
2512
2567
|
|
|
2513
|
-
|
|
2514
2568
|
// ── State Persistence Types (TS-009) ─────────────────────────────────
|
|
2515
2569
|
|
|
2516
2570
|
// ── v3 Resilience & Diagnostics Sections (TP-030) ────────────────────
|
|
@@ -2832,7 +2886,13 @@ export interface PersistedTaskRecord {
|
|
|
2832
2886
|
*
|
|
2833
2887
|
* @since v4 (TP-081)
|
|
2834
2888
|
*/
|
|
2835
|
-
export type PersistedSegmentStatus =
|
|
2889
|
+
export type PersistedSegmentStatus =
|
|
2890
|
+
| "pending"
|
|
2891
|
+
| "running"
|
|
2892
|
+
| "succeeded"
|
|
2893
|
+
| "failed"
|
|
2894
|
+
| "stalled"
|
|
2895
|
+
| "skipped";
|
|
2836
2896
|
|
|
2837
2897
|
/**
|
|
2838
2898
|
* Persisted record of a single segment's execution state.
|
|
@@ -3095,7 +3155,6 @@ export interface PersistedBatchState {
|
|
|
3095
3155
|
_extraFields?: Record<string, unknown>;
|
|
3096
3156
|
}
|
|
3097
3157
|
|
|
3098
|
-
|
|
3099
3158
|
// ── Resume (TS-009 Step 4) ───────────────────────────────────────────
|
|
3100
3159
|
|
|
3101
3160
|
/**
|
|
@@ -3313,10 +3372,7 @@ export const DURATION_BASE_MINUTES = 30;
|
|
|
3313
3372
|
* Get estimated duration in minutes for a task size.
|
|
3314
3373
|
* Uses explicit mapping, falling back to weight × base.
|
|
3315
3374
|
*/
|
|
3316
|
-
export function getTaskDurationMinutes(
|
|
3317
|
-
size: string,
|
|
3318
|
-
sizeWeights: Record<string, number>,
|
|
3319
|
-
): number {
|
|
3375
|
+
export function getTaskDurationMinutes(size: string, sizeWeights: Record<string, number>): number {
|
|
3320
3376
|
if (SIZE_DURATION_MINUTES[size] !== undefined) {
|
|
3321
3377
|
return SIZE_DURATION_MINUTES[size];
|
|
3322
3378
|
}
|
|
@@ -3324,7 +3380,6 @@ export function getTaskDurationMinutes(
|
|
|
3324
3380
|
return weight * DURATION_BASE_MINUTES;
|
|
3325
3381
|
}
|
|
3326
3382
|
|
|
3327
|
-
|
|
3328
3383
|
// ── Batch History ────────────────────────────────────────────────────
|
|
3329
3384
|
|
|
3330
3385
|
/** Token counts for a task, wave, or batch. */
|
|
@@ -3341,8 +3396,8 @@ export interface BatchTaskSummary {
|
|
|
3341
3396
|
taskId: string;
|
|
3342
3397
|
taskName: string;
|
|
3343
3398
|
status: "succeeded" | "failed" | "skipped" | "blocked" | "stalled" | "pending";
|
|
3344
|
-
wave: number;
|
|
3345
|
-
lane: number;
|
|
3399
|
+
wave: number; // 1-based
|
|
3400
|
+
lane: number; // 1-based
|
|
3346
3401
|
durationMs: number;
|
|
3347
3402
|
tokens: TokenCounts;
|
|
3348
3403
|
exitReason: string | null;
|
|
@@ -3350,8 +3405,8 @@ export interface BatchTaskSummary {
|
|
|
3350
3405
|
|
|
3351
3406
|
/** Per-wave summary for history. */
|
|
3352
3407
|
export interface BatchWaveSummary {
|
|
3353
|
-
wave: number;
|
|
3354
|
-
tasks: string[];
|
|
3408
|
+
wave: number; // 1-based
|
|
3409
|
+
tasks: string[]; // task IDs
|
|
3355
3410
|
mergeStatus: "succeeded" | "failed" | "partial" | "skipped";
|
|
3356
3411
|
durationMs: number;
|
|
3357
3412
|
tokens: TokenCounts;
|
|
@@ -3380,7 +3435,6 @@ export interface BatchHistorySummary {
|
|
|
3380
3435
|
/** Max number of batch history entries to retain. */
|
|
3381
3436
|
export const BATCH_HISTORY_MAX_ENTRIES = 100;
|
|
3382
3437
|
|
|
3383
|
-
|
|
3384
3438
|
// ── Workspace Mode Types ─────────────────────────────────────────────
|
|
3385
3439
|
|
|
3386
3440
|
/**
|
|
@@ -3518,7 +3572,6 @@ export interface ExecutionContext {
|
|
|
3518
3572
|
pointer: PointerResolution | null;
|
|
3519
3573
|
}
|
|
3520
3574
|
|
|
3521
|
-
|
|
3522
3575
|
// ── Workspace Validation Error Types ─────────────────────────────────
|
|
3523
3576
|
|
|
3524
3577
|
/**
|
|
@@ -3560,7 +3613,7 @@ export type WorkspaceConfigErrorCode =
|
|
|
3560
3613
|
| "WORKSPACE_TASK_AREA_OUTSIDE_TASKS_ROOT"
|
|
3561
3614
|
| "WORKSPACE_SETUP_REQUIRED"
|
|
3562
3615
|
| "WORKSPACE_DUPLICATE_REPO_PATH"
|
|
3563
|
-
| "WORKSPACE_SCHEMA_INVALID"
|
|
3616
|
+
| "WORKSPACE_SCHEMA_INVALID"; /**
|
|
3564
3617
|
* Typed error class for workspace configuration failures.
|
|
3565
3618
|
*
|
|
3566
3619
|
* Thrown during workspace config loading/validation when the config file
|
|
@@ -3577,7 +3630,12 @@ export class WorkspaceConfigError extends Error {
|
|
|
3577
3630
|
/** Optional filesystem path related to the error */
|
|
3578
3631
|
relatedPath?: string;
|
|
3579
3632
|
|
|
3580
|
-
constructor(
|
|
3633
|
+
constructor(
|
|
3634
|
+
code: WorkspaceConfigErrorCode,
|
|
3635
|
+
message: string,
|
|
3636
|
+
repoId?: string,
|
|
3637
|
+
relatedPath?: string,
|
|
3638
|
+
) {
|
|
3581
3639
|
super(message);
|
|
3582
3640
|
this.name = "WorkspaceConfigError";
|
|
3583
3641
|
this.code = code;
|
|
@@ -3586,7 +3644,6 @@ export class WorkspaceConfigError extends Error {
|
|
|
3586
3644
|
}
|
|
3587
3645
|
}
|
|
3588
3646
|
|
|
3589
|
-
|
|
3590
3647
|
// ── Pointer Resolution Types ─────────────────────────────────────────
|
|
3591
3648
|
|
|
3592
3649
|
/**
|
|
@@ -3653,7 +3710,6 @@ export interface PointerResolution {
|
|
|
3653
3710
|
warning?: string;
|
|
3654
3711
|
}
|
|
3655
3712
|
|
|
3656
|
-
|
|
3657
3713
|
// ── Workspace Defaults ───────────────────────────────────────────────
|
|
3658
3714
|
|
|
3659
3715
|
/**
|
|
@@ -3697,7 +3753,6 @@ export function createRepoModeContext(
|
|
|
3697
3753
|
};
|
|
3698
3754
|
}
|
|
3699
3755
|
|
|
3700
|
-
|
|
3701
3756
|
// ── Agent Mailbox Types (TP-089) ─────────────────────────────────────
|
|
3702
3757
|
|
|
3703
3758
|
/**
|
|
@@ -3735,7 +3790,12 @@ export type MailboxMessageType = "steer" | "query" | "abort" | "info" | "reply"
|
|
|
3735
3790
|
* @since TP-089
|
|
3736
3791
|
*/
|
|
3737
3792
|
export const MAILBOX_MESSAGE_TYPES: ReadonlySet<string> = new Set<MailboxMessageType>([
|
|
3738
|
-
"steer",
|
|
3793
|
+
"steer",
|
|
3794
|
+
"query",
|
|
3795
|
+
"abort",
|
|
3796
|
+
"info",
|
|
3797
|
+
"reply",
|
|
3798
|
+
"escalate",
|
|
3739
3799
|
]);
|
|
3740
3800
|
|
|
3741
3801
|
/**
|
|
@@ -3838,7 +3898,10 @@ export type RuntimeAgentStatus =
|
|
|
3838
3898
|
|
|
3839
3899
|
/** Set of terminal agent statuses (process is no longer alive). @since TP-102 */
|
|
3840
3900
|
export const TERMINAL_AGENT_STATUSES: ReadonlySet<RuntimeAgentStatus> = new Set([
|
|
3841
|
-
"exited",
|
|
3901
|
+
"exited",
|
|
3902
|
+
"crashed",
|
|
3903
|
+
"timed_out",
|
|
3904
|
+
"killed",
|
|
3842
3905
|
]);
|
|
3843
3906
|
|
|
3844
3907
|
/**
|
|
@@ -4173,7 +4236,11 @@ export function runtimeRoot(stateRoot: string, batchId: string): string {
|
|
|
4173
4236
|
*
|
|
4174
4237
|
* @since TP-102
|
|
4175
4238
|
*/
|
|
4176
|
-
export function runtimeAgentDir(
|
|
4239
|
+
export function runtimeAgentDir(
|
|
4240
|
+
stateRoot: string,
|
|
4241
|
+
batchId: string,
|
|
4242
|
+
agentId: RuntimeAgentId,
|
|
4243
|
+
): string {
|
|
4177
4244
|
return `${stateRoot}/.pi/runtime/${batchId}/agents/${agentId}`;
|
|
4178
4245
|
}
|
|
4179
4246
|
|
|
@@ -4182,7 +4249,11 @@ export function runtimeAgentDir(stateRoot: string, batchId: string, agentId: Run
|
|
|
4182
4249
|
*
|
|
4183
4250
|
* @since TP-102
|
|
4184
4251
|
*/
|
|
4185
|
-
export function runtimeManifestPath(
|
|
4252
|
+
export function runtimeManifestPath(
|
|
4253
|
+
stateRoot: string,
|
|
4254
|
+
batchId: string,
|
|
4255
|
+
agentId: RuntimeAgentId,
|
|
4256
|
+
): string {
|
|
4186
4257
|
return `${runtimeAgentDir(stateRoot, batchId, agentId)}/manifest.json`;
|
|
4187
4258
|
}
|
|
4188
4259
|
|
|
@@ -4191,7 +4262,11 @@ export function runtimeManifestPath(stateRoot: string, batchId: string, agentId:
|
|
|
4191
4262
|
*
|
|
4192
4263
|
* @since TP-102
|
|
4193
4264
|
*/
|
|
4194
|
-
export function runtimeAgentEventsPath(
|
|
4265
|
+
export function runtimeAgentEventsPath(
|
|
4266
|
+
stateRoot: string,
|
|
4267
|
+
batchId: string,
|
|
4268
|
+
agentId: RuntimeAgentId,
|
|
4269
|
+
): string {
|
|
4195
4270
|
return `${runtimeAgentDir(stateRoot, batchId, agentId)}/events.jsonl`;
|
|
4196
4271
|
}
|
|
4197
4272
|
|
|
@@ -4200,7 +4275,11 @@ export function runtimeAgentEventsPath(stateRoot: string, batchId: string, agent
|
|
|
4200
4275
|
*
|
|
4201
4276
|
* @since TP-102
|
|
4202
4277
|
*/
|
|
4203
|
-
export function runtimeLaneSnapshotPath(
|
|
4278
|
+
export function runtimeLaneSnapshotPath(
|
|
4279
|
+
stateRoot: string,
|
|
4280
|
+
batchId: string,
|
|
4281
|
+
laneNumber: number,
|
|
4282
|
+
): string {
|
|
4204
4283
|
return `${stateRoot}/.pi/runtime/${batchId}/lanes/lane-${laneNumber}.json`;
|
|
4205
4284
|
}
|
|
4206
4285
|
|
|
@@ -4244,7 +4323,11 @@ export interface RuntimeMergeSnapshot {
|
|
|
4244
4323
|
*
|
|
4245
4324
|
* @since TP-164
|
|
4246
4325
|
*/
|
|
4247
|
-
export function runtimeMergeSnapshotPath(
|
|
4326
|
+
export function runtimeMergeSnapshotPath(
|
|
4327
|
+
stateRoot: string,
|
|
4328
|
+
batchId: string,
|
|
4329
|
+
mergeNumber: number,
|
|
4330
|
+
): string {
|
|
4248
4331
|
return `${stateRoot}/.pi/runtime/${batchId}/lanes/merge-${mergeNumber}.json`;
|
|
4249
4332
|
}
|
|
4250
4333
|
|
|
@@ -4309,15 +4392,28 @@ export function validateAgentManifest(manifest: unknown): string[] {
|
|
|
4309
4392
|
if (typeof m.role !== "string") errors.push("role must be a string");
|
|
4310
4393
|
else {
|
|
4311
4394
|
const validRoles: ReadonlySet<string> = new Set(["worker", "reviewer", "merger", "lane-runner"]);
|
|
4312
|
-
if (!validRoles.has(m.role as string))
|
|
4395
|
+
if (!validRoles.has(m.role as string))
|
|
4396
|
+
errors.push(`role must be one of: ${[...validRoles].join(", ")}`);
|
|
4313
4397
|
}
|
|
4314
|
-
if (typeof m.pid !== "number" || !Number.isFinite(m.pid) || m.pid <= 0)
|
|
4315
|
-
|
|
4316
|
-
if (typeof m.
|
|
4398
|
+
if (typeof m.pid !== "number" || !Number.isFinite(m.pid) || m.pid <= 0)
|
|
4399
|
+
errors.push("pid must be a positive finite number");
|
|
4400
|
+
if (typeof m.parentPid !== "number" || !Number.isFinite(m.parentPid) || m.parentPid <= 0)
|
|
4401
|
+
errors.push("parentPid must be a positive finite number");
|
|
4402
|
+
if (typeof m.startedAt !== "number" || !Number.isFinite(m.startedAt))
|
|
4403
|
+
errors.push("startedAt must be a finite number");
|
|
4317
4404
|
if (typeof m.status !== "string") errors.push("status must be a string");
|
|
4318
4405
|
else {
|
|
4319
|
-
const validStatuses: ReadonlySet<string> = new Set([
|
|
4320
|
-
|
|
4406
|
+
const validStatuses: ReadonlySet<string> = new Set([
|
|
4407
|
+
"spawning",
|
|
4408
|
+
"running",
|
|
4409
|
+
"wrapping_up",
|
|
4410
|
+
"exited",
|
|
4411
|
+
"crashed",
|
|
4412
|
+
"timed_out",
|
|
4413
|
+
"killed",
|
|
4414
|
+
]);
|
|
4415
|
+
if (!validStatuses.has(m.status as string))
|
|
4416
|
+
errors.push(`status must be one of: ${[...validStatuses].join(", ")}`);
|
|
4321
4417
|
}
|
|
4322
4418
|
if (typeof m.cwd !== "string" || !m.cwd) errors.push("cwd must be a non-empty string");
|
|
4323
4419
|
if (typeof m.repoId !== "string") errors.push("repoId must be a string");
|
|
@@ -4339,7 +4435,13 @@ export function validatePacketPaths(packet: unknown): string[] {
|
|
|
4339
4435
|
}
|
|
4340
4436
|
const p = packet as Record<string, unknown>;
|
|
4341
4437
|
|
|
4342
|
-
for (const field of [
|
|
4438
|
+
for (const field of [
|
|
4439
|
+
"promptPath",
|
|
4440
|
+
"statusPath",
|
|
4441
|
+
"donePath",
|
|
4442
|
+
"reviewsDir",
|
|
4443
|
+
"taskFolder",
|
|
4444
|
+
] as const) {
|
|
4343
4445
|
if (typeof p[field] !== "string" || !(p[field] as string)) {
|
|
4344
4446
|
errors.push(`${field} must be a non-empty string`);
|
|
4345
4447
|
}
|
|
@@ -4347,4 +4449,3 @@ export function validatePacketPaths(packet: unknown): string[] {
|
|
|
4347
4449
|
|
|
4348
4450
|
return errors;
|
|
4349
4451
|
}
|
|
4350
|
-
|