taskplane 0.20.5 → 0.21.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/bin/rpc-wrapper.mjs +31 -0
- package/bin/taskplane.mjs +22 -10
- package/extensions/task-runner.ts +44 -16
- package/extensions/taskplane/engine-worker-entry.mjs +18 -0
- package/extensions/taskplane/extension.ts +129 -22
- package/package.json +2 -1
package/bin/rpc-wrapper.mjs
CHANGED
|
@@ -331,6 +331,8 @@ function createSessionState() {
|
|
|
331
331
|
currentTool: null,
|
|
332
332
|
error: null,
|
|
333
333
|
agentEnded: false,
|
|
334
|
+
/** Authoritative context usage from pi get_session_stats (null if unavailable) */
|
|
335
|
+
contextUsage: null,
|
|
334
336
|
};
|
|
335
337
|
}
|
|
336
338
|
|
|
@@ -414,6 +416,10 @@ function applyEvent(state, event) {
|
|
|
414
416
|
if (event.success === false && event.error) {
|
|
415
417
|
state.error = event.error;
|
|
416
418
|
}
|
|
419
|
+
// get_session_stats response — extract authoritative contextUsage
|
|
420
|
+
if (event.success === true && event.data?.contextUsage) {
|
|
421
|
+
state.contextUsage = event.data.contextUsage;
|
|
422
|
+
}
|
|
417
423
|
break;
|
|
418
424
|
}
|
|
419
425
|
|
|
@@ -455,6 +461,8 @@ function buildExitSummary(state, exitCode, exitSignal, errorOverride, startTime)
|
|
|
455
461
|
durationSec,
|
|
456
462
|
lastToolCall: state.lastToolCall,
|
|
457
463
|
error: finalError,
|
|
464
|
+
// Authoritative context usage from pi ≥ 0.63.0 (null if unavailable)
|
|
465
|
+
contextUsage: state.contextUsage || null,
|
|
458
466
|
};
|
|
459
467
|
|
|
460
468
|
return redactSummary(rawSummary);
|
|
@@ -614,6 +622,22 @@ function closeStdin() {
|
|
|
614
622
|
}
|
|
615
623
|
}
|
|
616
624
|
|
|
625
|
+
/**
|
|
626
|
+
* Query pi for authoritative session stats including contextUsage.
|
|
627
|
+
* Available in pi ≥ 0.63.0 (RPC get_session_stats exposes contextUsage).
|
|
628
|
+
* Safe to call on older versions — the command is ignored or returns
|
|
629
|
+
* without the field, and state.contextUsage stays null.
|
|
630
|
+
*/
|
|
631
|
+
function querySessionStats() {
|
|
632
|
+
try {
|
|
633
|
+
if (proc.stdin && !proc.stdin.destroyed) {
|
|
634
|
+
proc.stdin.write(JSON.stringify({ type: "get_session_stats" }) + "\n");
|
|
635
|
+
}
|
|
636
|
+
} catch {
|
|
637
|
+
// stdin may be closed — ignore
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
617
641
|
// ── Route RPC events ─────────────────────────────────────────────────
|
|
618
642
|
|
|
619
643
|
function handleEvent(event) {
|
|
@@ -628,6 +652,13 @@ function handleEvent(event) {
|
|
|
628
652
|
// Side effects that depend on the event type (IO, stdin lifecycle, display)
|
|
629
653
|
switch (event.type) {
|
|
630
654
|
case "message_end":
|
|
655
|
+
displayProgress(state);
|
|
656
|
+
// Query pi for authoritative context usage (pi ≥ 0.63.0).
|
|
657
|
+
// Falls back gracefully: older pi versions ignore the command
|
|
658
|
+
// or return a response without contextUsage — state.contextUsage stays null.
|
|
659
|
+
querySessionStats();
|
|
660
|
+
break;
|
|
661
|
+
|
|
631
662
|
case "tool_execution_start":
|
|
632
663
|
displayProgress(state);
|
|
633
664
|
break;
|
package/bin/taskplane.mjs
CHANGED
|
@@ -1193,6 +1193,15 @@ async function cmdInit(args) {
|
|
|
1193
1193
|
{ skipIfExists: !force, label: ".pi/taskplane-workspace.yaml" },
|
|
1194
1194
|
);
|
|
1195
1195
|
|
|
1196
|
+
// ── Gitignore enforcement in config repo (Scenario D) ───
|
|
1197
|
+
// Ensure .gitignore exists even when reusing existing config
|
|
1198
|
+
const gitignoreResult = ensureGitignoreEntries(configRepoRoot, { dryRun: false, prefix: ".taskplane/" });
|
|
1199
|
+
if (gitignoreResult.created) {
|
|
1200
|
+
console.log(` ${c.green}create${c.reset} ${configRepo}/.gitignore`);
|
|
1201
|
+
} else if (gitignoreResult.added.length > 0) {
|
|
1202
|
+
console.log(` ${c.green}update${c.reset} ${configRepo}/.gitignore (${gitignoreResult.added.length} entries added)`);
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1196
1205
|
console.log(`\n${OK} ${c.bold}Workspace pointer created.${c.reset}\n`);
|
|
1197
1206
|
console.log(` Config: ${c.cyan}${configRepo}/.taskplane/${c.reset}`);
|
|
1198
1207
|
console.log(` Pointer: ${c.cyan}.pi/taskplane-pointer.json${c.reset}`);
|
|
@@ -2510,18 +2519,20 @@ function cmdDoctor() {
|
|
|
2510
2519
|
console.log();
|
|
2511
2520
|
const hasUnifiedJson = fs.existsSync(path.join(configLocation.root, configLocation.prefix, "taskplane-config.json"));
|
|
2512
2521
|
const configFiles = [
|
|
2513
|
-
{ path: "taskplane-config.json", required: false },
|
|
2514
|
-
|
|
2515
|
-
{ path: "task-
|
|
2516
|
-
{ path: "
|
|
2517
|
-
{ path: "agents/task-
|
|
2518
|
-
{ path: "agents/task-
|
|
2519
|
-
{ path: "agents/
|
|
2520
|
-
|
|
2522
|
+
{ path: "taskplane-config.json", required: false, hide: false },
|
|
2523
|
+
// YAML configs are legacy fallback — hide when taskplane-config.json exists
|
|
2524
|
+
{ path: "task-runner.yaml", required: !hasUnifiedJson, hide: hasUnifiedJson },
|
|
2525
|
+
{ path: "task-orchestrator.yaml", required: !hasUnifiedJson, hide: hasUnifiedJson },
|
|
2526
|
+
{ path: "agents/task-worker.md", required: true, hide: false },
|
|
2527
|
+
{ path: "agents/task-reviewer.md", required: true, hide: false },
|
|
2528
|
+
{ path: "agents/task-merger.md", required: true, hide: false },
|
|
2529
|
+
// supervisor.md is created by /orch; taskplane.json is created at runtime
|
|
2530
|
+
{ path: "agents/supervisor.md", required: false, hide: true },
|
|
2531
|
+
{ path: "taskplane.json", required: false, hide: true },
|
|
2521
2532
|
];
|
|
2522
2533
|
|
|
2523
2534
|
let missingRequiredConfigs = 0;
|
|
2524
|
-
for (const { path: relPath, required } of configFiles) {
|
|
2535
|
+
for (const { path: relPath, required, hide } of configFiles) {
|
|
2525
2536
|
const fullPath = path.join(configLocation.root, configLocation.prefix, relPath);
|
|
2526
2537
|
const displayPath = `${configLocation.label}/${relPath}`;
|
|
2527
2538
|
const exists = fs.existsSync(fullPath);
|
|
@@ -2531,7 +2542,8 @@ function cmdDoctor() {
|
|
|
2531
2542
|
console.log(` ${FAIL} ${displayPath} missing`);
|
|
2532
2543
|
missingRequiredConfigs++;
|
|
2533
2544
|
issues++;
|
|
2534
|
-
} else {
|
|
2545
|
+
} else if (!hide) {
|
|
2546
|
+
// Show optional files only when they're relevant (not superseded)
|
|
2535
2547
|
console.log(` ${WARN} ${displayPath} missing ${c.dim}(optional)${c.reset}`);
|
|
2536
2548
|
}
|
|
2537
2549
|
}
|
|
@@ -1367,6 +1367,8 @@ interface SidecarTelemetryDelta {
|
|
|
1367
1367
|
lastRetryError: string;
|
|
1368
1368
|
/** Whether any sidecar events were parsed in this tick (used for callback gating) */
|
|
1369
1369
|
hadEvents: boolean;
|
|
1370
|
+
/** Authoritative context usage from pi get_session_stats (pi ≥ 0.63.0, null if unavailable) */
|
|
1371
|
+
contextUsage: { percentUsed: number; totalTokens: number; maxTokens: number } | null;
|
|
1370
1372
|
}
|
|
1371
1373
|
|
|
1372
1374
|
/**
|
|
@@ -1385,7 +1387,7 @@ function tailSidecarJsonl(filePath: string, tailState: SidecarTailState): Sideca
|
|
|
1385
1387
|
inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0,
|
|
1386
1388
|
cost: 0, latestTotalTokens: 0, toolCalls: 0, lastTool: "",
|
|
1387
1389
|
retryActive: tailState.retryActive, retriesStarted: 0, lastRetryError: "",
|
|
1388
|
-
hadEvents: false,
|
|
1390
|
+
hadEvents: false, contextUsage: null,
|
|
1389
1391
|
};
|
|
1390
1392
|
|
|
1391
1393
|
// Gracefully handle missing file (wrapper hasn't written yet)
|
|
@@ -1496,6 +1498,21 @@ function tailSidecarJsonl(filePath: string, tailState: SidecarTailState): Sideca
|
|
|
1496
1498
|
tailState.retryActive = false;
|
|
1497
1499
|
break;
|
|
1498
1500
|
}
|
|
1501
|
+
|
|
1502
|
+
case "response": {
|
|
1503
|
+
// get_session_stats response from pi ≥ 0.63.0 — authoritative context usage
|
|
1504
|
+
if (event.success === true && event.data?.contextUsage) {
|
|
1505
|
+
const cu = event.data.contextUsage;
|
|
1506
|
+
if (typeof cu.percentUsed === "number") {
|
|
1507
|
+
delta.contextUsage = {
|
|
1508
|
+
percentUsed: cu.percentUsed,
|
|
1509
|
+
totalTokens: cu.totalTokens || 0,
|
|
1510
|
+
maxTokens: cu.maxTokens || 0,
|
|
1511
|
+
};
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
break;
|
|
1515
|
+
}
|
|
1499
1516
|
}
|
|
1500
1517
|
}
|
|
1501
1518
|
|
|
@@ -2405,8 +2422,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
2405
2422
|
state.reviewerLastTool = delta.lastTool;
|
|
2406
2423
|
}
|
|
2407
2424
|
|
|
2408
|
-
// Context %
|
|
2409
|
-
if (delta.
|
|
2425
|
+
// Context % — prefer authoritative contextUsage (pi ≥ 0.63.0)
|
|
2426
|
+
if (delta.contextUsage) {
|
|
2427
|
+
state.reviewerContextPct = delta.contextUsage.percentUsed;
|
|
2428
|
+
} else if (delta.latestTotalTokens > 0 && contextWindow > 0) {
|
|
2410
2429
|
state.reviewerContextPct = (delta.latestTotalTokens / contextWindow) * 100;
|
|
2411
2430
|
}
|
|
2412
2431
|
|
|
@@ -2566,7 +2585,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
2566
2585
|
state.reviewerCostUsd += delta.cost;
|
|
2567
2586
|
state.reviewerToolCount += delta.toolCalls;
|
|
2568
2587
|
if (delta.lastTool) state.reviewerLastTool = delta.lastTool;
|
|
2569
|
-
|
|
2588
|
+
// Context % — prefer authoritative contextUsage (pi ≥ 0.63.0)
|
|
2589
|
+
if (delta.contextUsage) {
|
|
2590
|
+
state.reviewerContextPct = delta.contextUsage.percentUsed;
|
|
2591
|
+
} else if (delta.latestTotalTokens > 0 && contextWindow > 0) {
|
|
2570
2592
|
state.reviewerContextPct = (delta.latestTotalTokens / contextWindow) * 100;
|
|
2571
2593
|
}
|
|
2572
2594
|
writeLaneState(state);
|
|
@@ -3140,18 +3162,24 @@ export default function (pi: ExtensionAPI) {
|
|
|
3140
3162
|
state.workerLastRetryError = delta.lastRetryError;
|
|
3141
3163
|
}
|
|
3142
3164
|
|
|
3143
|
-
// Context %
|
|
3144
|
-
//
|
|
3145
|
-
|
|
3146
|
-
const pct =
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
if (pct
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3165
|
+
// Context % — prefer authoritative contextUsage from pi ≥ 0.63.0,
|
|
3166
|
+
// fall back to manual calculation from totalTokens + cacheRead.
|
|
3167
|
+
{
|
|
3168
|
+
const pct = delta.contextUsage
|
|
3169
|
+
? delta.contextUsage.percentUsed
|
|
3170
|
+
: (delta.latestTotalTokens > 0 && contextWindow > 0)
|
|
3171
|
+
? (delta.latestTotalTokens / contextWindow) * 100
|
|
3172
|
+
: 0;
|
|
3173
|
+
if (pct > 0) {
|
|
3174
|
+
state.workerContextPct = pct;
|
|
3175
|
+
if (pct >= warnPct) {
|
|
3176
|
+
writeWrapUpSignal(`Wrap up (context ${Math.round(pct)}%)`);
|
|
3177
|
+
}
|
|
3178
|
+
if (pct >= killPct && state.workerStatus === "running") {
|
|
3179
|
+
console.error(`[task-runner] tmux worker: context limit (${Math.round(pct)}%) — killing session '${sessionName}'`);
|
|
3180
|
+
killReason = "context";
|
|
3181
|
+
spawned.kill();
|
|
3182
|
+
}
|
|
3155
3183
|
}
|
|
3156
3184
|
}
|
|
3157
3185
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fork entry point for the engine child process.
|
|
3
|
+
*
|
|
4
|
+
* Node v25 blocks .ts files inside node_modules regardless of flags.
|
|
5
|
+
* This .mjs file loads cleanly (no TypeScript processing needed), then
|
|
6
|
+
* uses jiti to import engine-worker.ts — bypassing Node's restriction.
|
|
7
|
+
*
|
|
8
|
+
* jiti is the same TypeScript runtime loader that Pi uses to load
|
|
9
|
+
* extensions. It transforms .ts files itself, independent of Node's
|
|
10
|
+
* --experimental-strip-types support.
|
|
11
|
+
*/
|
|
12
|
+
import { createJiti } from "jiti";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
import { dirname, join } from "node:path";
|
|
15
|
+
|
|
16
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const jiti = createJiti(import.meta.url);
|
|
18
|
+
await jiti.import(join(__dirname, "engine-worker.ts"));
|
|
@@ -5,8 +5,7 @@ import { execSync, execFileSync } from "child_process";
|
|
|
5
5
|
import { writeFileSync, unlinkSync, mkdirSync, existsSync, readdirSync } from "fs";
|
|
6
6
|
import { join, dirname } from "path";
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
8
|
-
|
|
9
|
-
// Re-enable when engine-worker ships as pre-compiled .js bundle.
|
|
8
|
+
import { fork, type ChildProcess } from "child_process";
|
|
10
9
|
|
|
11
10
|
// Direct imports — avoid barrel (index.ts) to prevent loading the entire module graph.
|
|
12
11
|
// Each import targets the specific module where the symbol is defined.
|
|
@@ -898,7 +897,7 @@ function resolveEngineWorkerPath(): string {
|
|
|
898
897
|
} catch {
|
|
899
898
|
thisDir = __dirname;
|
|
900
899
|
}
|
|
901
|
-
return join(thisDir, "engine-worker.
|
|
900
|
+
return join(thisDir, "engine-worker-entry.mjs");
|
|
902
901
|
}
|
|
903
902
|
|
|
904
903
|
/**
|
|
@@ -936,14 +935,24 @@ export function startBatchInWorker(
|
|
|
936
935
|
updateWidget: () => void,
|
|
937
936
|
onMonitorUpdate?: (state: import("./types.ts").MonitorState) => void,
|
|
938
937
|
onTerminal?: () => void,
|
|
939
|
-
): null {
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
938
|
+
): ChildProcess | null {
|
|
939
|
+
const workerPath = resolveEngineWorkerPath();
|
|
940
|
+
|
|
941
|
+
let child: ChildProcess;
|
|
942
|
+
try {
|
|
943
|
+
// Fork a child process to run the engine in a separate isolate.
|
|
944
|
+
// The entry point is a .mjs file that uses jiti to load .ts files,
|
|
945
|
+
// bypassing Node v25's restriction on .ts in node_modules.
|
|
946
|
+
child = fork(workerPath, [], {
|
|
947
|
+
env: { ...process.env, TASKPLANE_ENGINE_FORK: "1" },
|
|
948
|
+
serialization: "advanced",
|
|
949
|
+
});
|
|
950
|
+
} catch (spawnErr: unknown) {
|
|
951
|
+
const errMsg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);
|
|
952
|
+
ctx.ui.notify(
|
|
953
|
+
`⚠️ Engine process spawn failed: ${errMsg}\n Falling back to main-thread execution.`,
|
|
954
|
+
"warning",
|
|
955
|
+
);
|
|
947
956
|
// Construct fallback engine function from workerData and run on main thread
|
|
948
957
|
const wsConfig = wkData.workspaceConfig
|
|
949
958
|
? deserializeWorkspaceConfig(wkData.workspaceConfig)
|
|
@@ -976,6 +985,91 @@ export function startBatchInWorker(
|
|
|
976
985
|
startBatchAsync(fallbackFn, batchState, ctx, updateWidget, onTerminal);
|
|
977
986
|
return null;
|
|
978
987
|
}
|
|
988
|
+
|
|
989
|
+
// Send workerData as first IPC message
|
|
990
|
+
child.send({ type: "init", data: wkData });
|
|
991
|
+
|
|
992
|
+
// Terminal settlement guard (R001 §3): ensures onTerminal fires at most once.
|
|
993
|
+
let settled = false;
|
|
994
|
+
const settle = () => {
|
|
995
|
+
if (settled) return;
|
|
996
|
+
settled = true;
|
|
997
|
+
onTerminal?.();
|
|
998
|
+
};
|
|
999
|
+
|
|
1000
|
+
child.on("message", (msg: WorkerToMainMessage) => {
|
|
1001
|
+
switch (msg.type) {
|
|
1002
|
+
case "notify":
|
|
1003
|
+
ctx.ui.notify(msg.msg, msg.level);
|
|
1004
|
+
updateWidget();
|
|
1005
|
+
break;
|
|
1006
|
+
|
|
1007
|
+
case "monitor-update":
|
|
1008
|
+
onMonitorUpdate?.(msg.state);
|
|
1009
|
+
break;
|
|
1010
|
+
|
|
1011
|
+
case "engine-event":
|
|
1012
|
+
break;
|
|
1013
|
+
|
|
1014
|
+
case "state-sync":
|
|
1015
|
+
applySerializedState(batchState, msg.state);
|
|
1016
|
+
updateWidget();
|
|
1017
|
+
break;
|
|
1018
|
+
|
|
1019
|
+
case "complete":
|
|
1020
|
+
applySerializedState(batchState, msg.state);
|
|
1021
|
+
updateWidget();
|
|
1022
|
+
settle();
|
|
1023
|
+
break;
|
|
1024
|
+
|
|
1025
|
+
case "error":
|
|
1026
|
+
if (batchState.phase !== "completed" && batchState.phase !== "failed") {
|
|
1027
|
+
batchState.phase = "failed";
|
|
1028
|
+
batchState.endedAt = Date.now();
|
|
1029
|
+
batchState.errors.push(`Unhandled engine error: ${msg.message}`);
|
|
1030
|
+
}
|
|
1031
|
+
ctx.ui.notify(
|
|
1032
|
+
`❌ Engine crashed with unhandled error: ${msg.message}\n` +
|
|
1033
|
+
` Batch ${batchState.batchId} marked as failed.`,
|
|
1034
|
+
"error",
|
|
1035
|
+
);
|
|
1036
|
+
updateWidget();
|
|
1037
|
+
break;
|
|
1038
|
+
}
|
|
1039
|
+
});
|
|
1040
|
+
|
|
1041
|
+
child.on("error", (err: Error) => {
|
|
1042
|
+
if (batchState.phase !== "completed" && batchState.phase !== "failed") {
|
|
1043
|
+
batchState.phase = "failed";
|
|
1044
|
+
batchState.endedAt = Date.now();
|
|
1045
|
+
batchState.errors.push(`Engine process error: ${err.message}`);
|
|
1046
|
+
}
|
|
1047
|
+
ctx.ui.notify(
|
|
1048
|
+
`❌ Engine process error: ${err.message}\n` +
|
|
1049
|
+
` Batch ${batchState.batchId} marked as failed.`,
|
|
1050
|
+
"error",
|
|
1051
|
+
);
|
|
1052
|
+
updateWidget();
|
|
1053
|
+
settle();
|
|
1054
|
+
});
|
|
1055
|
+
|
|
1056
|
+
child.on("exit", (code: number | null) => {
|
|
1057
|
+
if (code !== 0 && !settled) {
|
|
1058
|
+
if (batchState.phase !== "completed" && batchState.phase !== "failed") {
|
|
1059
|
+
batchState.phase = "failed";
|
|
1060
|
+
batchState.endedAt = Date.now();
|
|
1061
|
+
batchState.errors.push(`Engine process exited with code ${code}`);
|
|
1062
|
+
}
|
|
1063
|
+
ctx.ui.notify(
|
|
1064
|
+
`❌ Engine process exited unexpectedly (code ${code}).`,
|
|
1065
|
+
"error",
|
|
1066
|
+
);
|
|
1067
|
+
updateWidget();
|
|
1068
|
+
}
|
|
1069
|
+
settle();
|
|
1070
|
+
});
|
|
1071
|
+
|
|
1072
|
+
return child;
|
|
979
1073
|
}
|
|
980
1074
|
|
|
981
1075
|
// ── TP-043 R002-2: Integration Executor Builder ─────────────────────
|
|
@@ -1292,9 +1386,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
1292
1386
|
let orchWidgetCtx: ExtensionContext | undefined;
|
|
1293
1387
|
let latestMonitorState: MonitorState | null = null;
|
|
1294
1388
|
|
|
1295
|
-
// ── TP-071: Active engine
|
|
1296
|
-
//
|
|
1297
|
-
let activeWorker: null = null;
|
|
1389
|
+
// ── TP-071: Active engine child process ──────────────────────────
|
|
1390
|
+
// Tracked so pause/abort can send control messages to the engine.
|
|
1391
|
+
let activeWorker: ChildProcess | null = null;
|
|
1298
1392
|
|
|
1299
1393
|
// ── Supervisor State (TP-041) ────────────────────────────────────
|
|
1300
1394
|
let supervisorState = freshSupervisorState();
|
|
@@ -1911,8 +2005,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
1911
2005
|
return ORCH_MESSAGES.pauseAlreadyPaused(orchBatchState.batchId);
|
|
1912
2006
|
}
|
|
1913
2007
|
orchBatchState.pauseSignal.paused = true;
|
|
1914
|
-
// TP-071: Forward pause to engine process (
|
|
1915
|
-
|
|
2008
|
+
// TP-071: Forward pause to engine process (its pauseSignal is separate)
|
|
2009
|
+
activeWorker?.send({ type: "pause" });
|
|
1916
2010
|
updateOrchWidget();
|
|
1917
2011
|
return ORCH_MESSAGES.pauseActivated(orchBatchState.batchId);
|
|
1918
2012
|
}
|
|
@@ -2102,10 +2196,16 @@ export default function (pi: ExtensionAPI) {
|
|
|
2102
2196
|
orchBatchState.pauseSignal.paused = true;
|
|
2103
2197
|
messages.push(" ✓ Pause signal set on in-memory batch state");
|
|
2104
2198
|
}
|
|
2105
|
-
// TP-071: Forward pause to engine
|
|
2106
|
-
if (
|
|
2107
|
-
|
|
2108
|
-
|
|
2199
|
+
// TP-071: Forward pause to engine and kill on hard abort
|
|
2200
|
+
if (activeWorker) {
|
|
2201
|
+
activeWorker.send({ type: "pause" });
|
|
2202
|
+
if (hard) {
|
|
2203
|
+
activeWorker.kill();
|
|
2204
|
+
activeWorker = null;
|
|
2205
|
+
messages.push(" ✓ Engine process killed (hard abort)");
|
|
2206
|
+
} else {
|
|
2207
|
+
messages.push(" ✓ Pause signal forwarded to engine process");
|
|
2208
|
+
}
|
|
2109
2209
|
}
|
|
2110
2210
|
|
|
2111
2211
|
// Step 3: Check what we're aborting
|
|
@@ -3182,8 +3282,15 @@ export default function (pi: ExtensionAPI) {
|
|
|
3182
3282
|
// Ensure supervisor lockfile/heartbeat are cleaned up on normal session exit.
|
|
3183
3283
|
// This avoids leaving a live-looking lock when the process exits cleanly.
|
|
3184
3284
|
pi.on("session_end", async () => {
|
|
3185
|
-
// TP-071: Kill engine process on session exit
|
|
3186
|
-
|
|
3285
|
+
// TP-071: Kill engine process on session exit
|
|
3286
|
+
if (activeWorker) {
|
|
3287
|
+
try {
|
|
3288
|
+
activeWorker.kill();
|
|
3289
|
+
activeWorker = null;
|
|
3290
|
+
} catch {
|
|
3291
|
+
// Best effort — process may already be dead
|
|
3292
|
+
}
|
|
3293
|
+
}
|
|
3187
3294
|
try {
|
|
3188
3295
|
await deactivateSupervisor(pi, supervisorState);
|
|
3189
3296
|
} catch {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "taskplane",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "AI agent orchestration for pi — parallel task execution with checkpoint discipline",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"@sinclair/typebox": "*"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
+
"jiti": "^2.6.1",
|
|
46
47
|
"yaml": "^2.4.0"
|
|
47
48
|
},
|
|
48
49
|
"license": "MIT",
|