taskplane 0.22.12 → 0.22.13
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/dashboard/public/app.js +108 -55
- package/dashboard/public/style.css +18 -0
- package/dashboard/server.cjs +29 -3
- package/extensions/task-runner.ts +170 -21
- package/extensions/taskplane/cleanup.ts +30 -9
- package/extensions/taskplane/engine.ts +11 -12
- package/extensions/taskplane/execution.ts +20 -3
- package/extensions/taskplane/extension.ts +499 -8
- package/extensions/taskplane/supervisor-primer.md +6 -0
- package/package.json +1 -1
|
@@ -37,6 +37,8 @@ export interface PostIntegrateCleanupResult {
|
|
|
37
37
|
promptFilesDeleted: number;
|
|
38
38
|
/** Number of mailbox batch directories deleted (0 or 1) */
|
|
39
39
|
mailboxDirsDeleted: number;
|
|
40
|
+
/** Number of context-snapshot batch directories deleted (0 or 1) */
|
|
41
|
+
snapshotDirsDeleted: number;
|
|
40
42
|
/** Warnings from non-fatal cleanup failures */
|
|
41
43
|
warnings: string[];
|
|
42
44
|
}
|
|
@@ -61,6 +63,7 @@ export function cleanupPostIntegrate(stateRoot: string, batchId: string): PostIn
|
|
|
61
63
|
mergeFilesDeleted: 0,
|
|
62
64
|
promptFilesDeleted: 0,
|
|
63
65
|
mailboxDirsDeleted: 0,
|
|
66
|
+
snapshotDirsDeleted: 0,
|
|
64
67
|
warnings: [],
|
|
65
68
|
};
|
|
66
69
|
|
|
@@ -134,6 +137,17 @@ export function cleanupPostIntegrate(stateRoot: string, batchId: string): PostIn
|
|
|
134
137
|
}
|
|
135
138
|
}
|
|
136
139
|
|
|
140
|
+
// ── Context snapshots directory (.pi/context-snapshots/{batchId}/) ──────
|
|
141
|
+
const snapshotBatchDir = join(stateRoot, ".pi", "context-snapshots", batchId);
|
|
142
|
+
if (existsSync(snapshotBatchDir)) {
|
|
143
|
+
try {
|
|
144
|
+
rmSync(snapshotBatchDir, { recursive: true, force: true });
|
|
145
|
+
result.snapshotDirsDeleted = 1;
|
|
146
|
+
} catch (err: unknown) {
|
|
147
|
+
result.warnings.push(`Failed to delete context-snapshots directory ${snapshotBatchDir}: ${(err as Error).message}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
137
151
|
return result;
|
|
138
152
|
}
|
|
139
153
|
|
|
@@ -142,7 +156,7 @@ export function cleanupPostIntegrate(stateRoot: string, batchId: string): PostIn
|
|
|
142
156
|
*/
|
|
143
157
|
export function formatPostIntegrateCleanup(result: PostIntegrateCleanupResult): string {
|
|
144
158
|
const parts: string[] = [];
|
|
145
|
-
const totalDeleted = result.telemetryFilesDeleted + result.mergeFilesDeleted + result.promptFilesDeleted + result.mailboxDirsDeleted;
|
|
159
|
+
const totalDeleted = result.telemetryFilesDeleted + result.mergeFilesDeleted + result.promptFilesDeleted + result.mailboxDirsDeleted + result.snapshotDirsDeleted;
|
|
146
160
|
|
|
147
161
|
if (totalDeleted > 0) {
|
|
148
162
|
const segments: string[] = [];
|
|
@@ -150,6 +164,7 @@ export function formatPostIntegrateCleanup(result: PostIntegrateCleanupResult):
|
|
|
150
164
|
if (result.mergeFilesDeleted > 0) segments.push(`${result.mergeFilesDeleted} merge`);
|
|
151
165
|
if (result.promptFilesDeleted > 0) segments.push(`${result.promptFilesDeleted} prompt`);
|
|
152
166
|
if (result.mailboxDirsDeleted > 0) segments.push(`${result.mailboxDirsDeleted} mailbox`);
|
|
167
|
+
if (result.snapshotDirsDeleted > 0) segments.push(`${result.snapshotDirsDeleted} snapshots`);
|
|
153
168
|
parts.push(`🧹 Cleaned up ${totalDeleted} artifact file(s): ${segments.join(", ")}`);
|
|
154
169
|
}
|
|
155
170
|
|
|
@@ -274,13 +289,13 @@ export function sweepStaleArtifacts(
|
|
|
274
289
|
(name.startsWith("merge-request-") && name.endsWith(".txt")),
|
|
275
290
|
);
|
|
276
291
|
|
|
277
|
-
// Sweep stale
|
|
278
|
-
const
|
|
279
|
-
|
|
292
|
+
// Sweep stale batch directories under a parent (mailbox, context-snapshots)
|
|
293
|
+
const sweepBatchDirs = (parentDir: string, label: string): void => {
|
|
294
|
+
if (!existsSync(parentDir)) return;
|
|
280
295
|
try {
|
|
281
|
-
const entries = readdirSync(
|
|
296
|
+
const entries = readdirSync(parentDir);
|
|
282
297
|
for (const entry of entries) {
|
|
283
|
-
const entryPath = join(
|
|
298
|
+
const entryPath = join(parentDir, entry);
|
|
284
299
|
try {
|
|
285
300
|
const stat = statSync(entryPath);
|
|
286
301
|
if (!stat.isDirectory()) continue;
|
|
@@ -289,13 +304,19 @@ export function sweepStaleArtifacts(
|
|
|
289
304
|
result.staleDirsDeleted++;
|
|
290
305
|
}
|
|
291
306
|
} catch (err: unknown) {
|
|
292
|
-
result.warnings.push(`Failed to process
|
|
307
|
+
result.warnings.push(`Failed to process ${label} dir ${entry}: ${(err as Error).message}`);
|
|
293
308
|
}
|
|
294
309
|
}
|
|
295
310
|
} catch (err: unknown) {
|
|
296
|
-
result.warnings.push(`Failed to read
|
|
311
|
+
result.warnings.push(`Failed to read ${label} directory ${parentDir}: ${(err as Error).message}`);
|
|
297
312
|
}
|
|
298
|
-
}
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
// Sweep stale mailbox batch directories (.pi/mailbox/{batchId}/)
|
|
316
|
+
sweepBatchDirs(join(stateRoot, ".pi", MAILBOX_DIR_NAME), "mailbox");
|
|
317
|
+
|
|
318
|
+
// Sweep stale context-snapshot batch directories (.pi/context-snapshots/{batchId}/)
|
|
319
|
+
sweepBatchDirs(join(stateRoot, ".pi", "context-snapshots"), "context-snapshots");
|
|
299
320
|
|
|
300
321
|
return result;
|
|
301
322
|
}
|
|
@@ -1088,18 +1088,6 @@ export async function executeOrchBatch(
|
|
|
1088
1088
|
continue;
|
|
1089
1089
|
}
|
|
1090
1090
|
|
|
1091
|
-
onNotify(
|
|
1092
|
-
ORCH_MESSAGES.orchWaveStart(waveIdx + 1, rawWaves.length, waveTasks.length, Math.min(waveTasks.length, orchConfig.orchestrator.max_lanes)),
|
|
1093
|
-
"info",
|
|
1094
|
-
);
|
|
1095
|
-
|
|
1096
|
-
// TP-040: Emit wave_start event
|
|
1097
|
-
emitEvent(stateRoot, {
|
|
1098
|
-
...buildEngineEventBase("wave_start", batchState.batchId, waveIdx, batchState.phase),
|
|
1099
|
-
taskIds: waveTasks,
|
|
1100
|
-
laneCount: Math.min(waveTasks.length, orchConfig.orchestrator.max_lanes),
|
|
1101
|
-
}, onEngineEvent);
|
|
1102
|
-
|
|
1103
1091
|
const handleWaveMonitorUpdate: MonitorUpdateCallback = (monitorState) => {
|
|
1104
1092
|
const changed = syncTaskOutcomesFromMonitor(monitorState, allTaskOutcomes);
|
|
1105
1093
|
if (changed) {
|
|
@@ -1112,6 +1100,17 @@ export async function executeOrchBatch(
|
|
|
1112
1100
|
const onLanesAllocatedCb = (lanes: AllocatedLane[]) => {
|
|
1113
1101
|
latestAllocatedLanes = lanes;
|
|
1114
1102
|
batchState.currentLanes = lanes;
|
|
1103
|
+
|
|
1104
|
+
// Emit wave_start with actual lane count (post-affinity grouping)
|
|
1105
|
+
onNotify(
|
|
1106
|
+
ORCH_MESSAGES.orchWaveStart(waveIdx + 1, rawWaves.length, waveTasks.length, lanes.length),
|
|
1107
|
+
"info",
|
|
1108
|
+
);
|
|
1109
|
+
emitEvent(stateRoot, {
|
|
1110
|
+
...buildEngineEventBase("wave_start", batchState.batchId, waveIdx, batchState.phase),
|
|
1111
|
+
taskIds: waveTasks,
|
|
1112
|
+
laneCount: lanes.length,
|
|
1113
|
+
}, onEngineEvent);
|
|
1115
1114
|
// TP-029: Track repos from newly allocated lanes for cleanup coverage
|
|
1116
1115
|
for (const lane of lanes) {
|
|
1117
1116
|
const laneRepoRoot = resolveRepoRoot(lane.repoId, repoRoot, workspaceConfig);
|
|
@@ -594,9 +594,26 @@ export function buildTmuxSpawnArgs(
|
|
|
594
594
|
piCommand = `${envParts} pi --no-session -e ${shellQuote(taskRunnerExtPath)}`;
|
|
595
595
|
}
|
|
596
596
|
|
|
597
|
-
//
|
|
598
|
-
//
|
|
599
|
-
//
|
|
597
|
+
// TP-095: Capture lane session stderr to a log file (#339).
|
|
598
|
+
// When the lane session (rpc-wrapper → pi → task-runner) dies, stderr is
|
|
599
|
+
// lost to tmux scrollback. Redirect stderr to a persistent log file
|
|
600
|
+
// co-located with telemetry so the supervisor can diagnose lane deaths.
|
|
601
|
+
//
|
|
602
|
+
// We append stderr to a file using `2>>`. This captures all stderr output
|
|
603
|
+
// from rpc-wrapper (which includes pi stderr forwarding, progress display,
|
|
604
|
+
// and crash diagnostics). The tmux pane loses live stderr visibility, but
|
|
605
|
+
// the dashboard provides live monitoring and the file preserves everything
|
|
606
|
+
// for post-mortem analysis.
|
|
607
|
+
//
|
|
608
|
+
// Appended to piCommand (not the tmux shell wrapper) to target the
|
|
609
|
+
// node/rpc-wrapper process specifically. This avoids the fragile shell
|
|
610
|
+
// redirection issues that previously caused spawn failures on Windows.
|
|
611
|
+
if (sidecarPath) {
|
|
612
|
+
// Derive stderr log path from sidecar path:
|
|
613
|
+
// .pi/telemetry/{basename}.jsonl → .pi/telemetry/{basename}-stderr.log
|
|
614
|
+
const stderrLogPath = sidecarPath.replace(/\.jsonl$/, "-stderr.log");
|
|
615
|
+
piCommand = `${piCommand} 2>> ${shellQuote(stderrLogPath)}`;
|
|
616
|
+
}
|
|
600
617
|
|
|
601
618
|
const tmuxWorktreePath = toTmuxPath(worktreePath);
|
|
602
619
|
const wrappedCommand = `cd ${shellQuote(tmuxWorktreePath)} && ${piCommand}`;
|