taskplane 0.23.8 → 0.23.10

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.
@@ -14,8 +14,21 @@ import { fileURLToPath } from "node:url";
14
14
  import { dirname, join } from "node:path";
15
15
 
16
16
  const __dirname = dirname(fileURLToPath(import.meta.url));
17
- // TP-115: Disable jiti filesystem cache to prevent stale compiled code
18
- // after npm update. Without this, jiti serves old cached .mjs even when
19
- // the .ts source files have been updated by a new package version.
20
- const jiti = createJiti(import.meta.url, { cache: false });
17
+
18
+ // TP-115: Purge jiti cache for taskplane modules before loading.
19
+ // Pi's main process caches compiled .ts .mjs at $TEMP/jiti/.
20
+ // The engine-worker fork must compile fresh from current source files,
21
+ // not use stale cached versions from Pi's startup.
22
+ import { readdirSync, unlinkSync } from "node:fs";
23
+ import { tmpdir } from "node:os";
24
+ try {
25
+ const cacheDir = join(tmpdir(), "jiti");
26
+ for (const f of readdirSync(cacheDir)) {
27
+ if (f.startsWith("taskplane-") && f.endsWith(".mjs")) {
28
+ try { unlinkSync(join(cacheDir, f)); } catch {}
29
+ }
30
+ }
31
+ } catch { /* cache dir may not exist */ }
32
+
33
+ const jiti = createJiti(import.meta.url);
21
34
  await jiti.import(join(__dirname, "engine-worker.ts"));
@@ -2268,7 +2268,7 @@ export async function executeOrchBatch(
2268
2268
  const w = snap.worker || {};
2269
2269
  const r = snap.reviewer || {};
2270
2270
  // Key by session name (match lane record) for per-task lookup
2271
- const laneRec = batchState.lanes.find((l: { laneNumber: number }) => l.laneNumber === snap.laneNumber);
2271
+ const laneRec = (batchState.lanes || []).find((l: { laneNumber: number }) => l.laneNumber === snap.laneNumber);
2272
2272
  const key = laneRec?.tmuxSessionName || `lane-${snap.laneNumber}`;
2273
2273
  laneTokens.set(key, {
2274
2274
  input: (w.inputTokens || 0) + (r.inputTokens || 0),
@@ -2282,6 +2282,7 @@ export async function executeOrchBatch(
2282
2282
  }
2283
2283
  } catch { /* runtime dir may not exist */ }
2284
2284
 
2285
+
2285
2286
  // Legacy fallback: lane-state-*.json sidecars (only if V2 found nothing)
2286
2287
  if (laneTokens.size === 0) {
2287
2288
  try {
@@ -2320,7 +2321,11 @@ export async function executeOrchBatch(
2320
2321
  const durationMs = (to.startTime && to.endTime) ? (to.endTime - to.startTime) : 0;
2321
2322
 
2322
2323
  // Get tokens for this lane (cumulative — shared across tasks in same lane)
2323
- const tokens = laneTokens.get(to.sessionName) || { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, costUsd: 0 };
2324
+ // TP-115: V2 sessionName includes role suffix (-worker/-reviewer) but
2325
+ // laneTokens is keyed by tmuxSessionName (no suffix). Try both.
2326
+ const tokens = laneTokens.get(to.sessionName)
2327
+ || laneTokens.get(to.sessionName?.replace(/-(?:worker|reviewer)$/, ""))
2328
+ || { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, costUsd: 0 };
2324
2329
 
2325
2330
  return {
2326
2331
  taskId: to.taskId,
@@ -1,5 +1,3 @@
1
- // DEBUG: module load marker
2
- try { require('fs').writeFileSync('/c/dev/taskplane/.pi/tp-lane-runner-loaded.txt', 'loaded at ' + new Date().toISOString() + ' from ' + __filename); } catch(e) { try { require('fs').writeFileSync('/c/dev/taskplane/.pi/tp-lane-runner-loaded.txt', 'loaded at ' + new Date().toISOString() + ' err: ' + e); } catch {} }
3
1
  /**
4
2
  * Lane Runner — Headless per-lane execution for Runtime V2
5
3
  *
@@ -282,8 +280,6 @@ export async function executeTaskV2(
282
280
  // Context pressure: write wrap-up signal before kill
283
281
  let workerKillReason: "context" | "timer" | null = null;
284
282
 
285
- // DEBUG: verify this code path executes
286
- try { writeFileSync(config.stateRoot + '/.pi/tp-debug-pre-spawn.json', JSON.stringify({ ts: Date.now(), iter: totalIterations, taskId })); } catch { /* */ }
287
283
  const spawned = spawnAgent(hostOpts, undefined, (telemetry) => {
288
284
  // Context pressure check
289
285
  if (telemetry.contextUsage) {
@@ -307,8 +303,6 @@ export async function executeTaskV2(
307
303
 
308
304
  // TP-115: Update lastTelemetry with definitive final values from AgentHostResult
309
305
  lastTelemetry = workerResult;
310
- // DEBUG
311
- try { writeFileSync(config.stateRoot + '/.pi/tp-debug-workerResult.json', JSON.stringify({ iter: totalIterations, cost: workerResult.costUsd, tools: workerResult.toolCalls, input: workerResult.inputTokens, keys: Object.keys(workerResult) }, null, 2)); } catch { /* */ }
312
306
 
313
307
  // Clean up wrap-up signal
314
308
  if (existsSync(wrapUpFile)) try { unlinkSync(wrapUpFile); } catch { /* ignore */ }
@@ -12,6 +12,7 @@ import { resolveOperatorId } from "./naming.ts";
12
12
  import { MERGE_POLL_INTERVAL_MS, MERGE_RESULT_GRACE_MS, MERGE_RESULT_READ_RETRIES, MERGE_RESULT_READ_RETRY_DELAY_MS, MERGE_SPAWN_RETRY_MAX, MERGE_TIMEOUT_MAX_RETRIES, MERGE_TIMEOUT_MS, MERGE_HEALTH_POLL_INTERVAL_MS, MERGE_HEALTH_WARNING_THRESHOLD_MS, MERGE_HEALTH_STUCK_THRESHOLD_MS, MERGE_HEALTH_CAPTURE_LINES, MergeError, VALID_MERGE_STATUSES, buildEngineEventBase } from "./types.ts";
13
13
  import type { AllocatedLane, LaneExecutionResult, MergeLaneResult, MergeResult, MergeResultStatus, MergeWaveResult, OrchestratorConfig, RepoMergeOutcome, TaskRunnerConfig, TransactionRecord, TransactionStatus, VerificationBaselineResult, WaveExecutionResult, WorkspaceConfig, MergeHealthStatus, MergeHealthEventType, MergeSessionSnapshot, MergeSessionHealthState, EngineEvent, OrchBatchPhase } from "./types.ts";
14
14
  import { resolveBaseBranch, resolveRepoRoot } from "./waves.ts";
15
+ import { readManifest, writeManifest, buildRegistrySnapshot, writeRegistrySnapshot } from "./process-registry.ts";
15
16
  import { generateMergeWorktreePath, sleepAsync, sleepSync } from "./worktree.ts";
16
17
  import { getCurrentBranch, runGit } from "./git.ts";
17
18
  import { ORCH_MESSAGES } from "./messages.ts";
@@ -798,7 +799,7 @@ export async function spawnMergeAgentV2(
798
799
  // Store the kill handle for external cleanup (pause/abort).
799
800
  // The promise runs in background — caller uses waitForMergeResult()
800
801
  // to poll for the result file, same contract as the TMUX path.
801
- activeMergeAgents.set(sessionName, { promise, kill });
802
+ activeMergeAgents.set(sessionName, { promise, kill, stateRoot: stateRoot ?? repoRoot, batchId: bid });
802
803
 
803
804
  // Fire-and-forget: the background promise handles exit logging
804
805
  promise.then(result => {
@@ -816,17 +817,30 @@ export async function spawnMergeAgentV2(
816
817
  }
817
818
 
818
819
  /** Active V2 merge agent handles for cleanup/abort. @since TP-108 */
819
- const activeMergeAgents = new Map<string, { promise: Promise<AgentHostResult>; kill: () => void }>();
820
+ const activeMergeAgents = new Map<string, { promise: Promise<AgentHostResult>; kill: () => void; stateRoot?: string; batchId?: string }>();
820
821
 
821
822
  /**
822
823
  * Kill a V2 merge agent if it's still running.
823
824
  * Used by pause/abort/cleanup flows.
824
825
  * @since TP-108
825
826
  */
826
- export function killMergeAgentV2(sessionName: string): boolean {
827
+ export function killMergeAgentV2(sessionName: string, cleanExit?: boolean): boolean {
827
828
  const handle = activeMergeAgents.get(sessionName);
828
829
  if (handle) {
829
830
  handle.kill();
831
+ // TP-115: On clean post-result cleanup, update manifest to "exited"
832
+ // so dashboard shows correct status instead of "killed".
833
+ if (cleanExit && handle.stateRoot && handle.batchId) {
834
+ try {
835
+ const manifest = readManifest(handle.stateRoot, handle.batchId, sessionName as any);
836
+ if (manifest) {
837
+ manifest.status = "exited";
838
+ writeManifest(handle.stateRoot, manifest);
839
+ const snapshot = buildRegistrySnapshot(handle.stateRoot, handle.batchId);
840
+ writeRegistrySnapshot(handle.stateRoot, snapshot);
841
+ }
842
+ } catch { /* best effort */ }
843
+ }
830
844
  activeMergeAgents.delete(sessionName);
831
845
  return true;
832
846
  }
@@ -928,7 +942,7 @@ export async function waitForMergeResult(
928
942
  });
929
943
  // Clean up agent (may still be running post-write)
930
944
  if (isV2) {
931
- killMergeAgentV2(sessionName);
945
+ killMergeAgentV2(sessionName, true);
932
946
  } else if (await tmuxHasSessionAsync(sessionName)) {
933
947
  await tmuxKillSessionAsync(sessionName);
934
948
  }
@@ -967,7 +981,7 @@ export async function waitForMergeResult(
967
981
  });
968
982
  // Clean up agent if still alive
969
983
  if (isV2) {
970
- killMergeAgentV2(sessionName);
984
+ killMergeAgentV2(sessionName, true);
971
985
  } else if (await tmuxHasSessionAsync(sessionName)) {
972
986
  await tmuxKillSessionAsync(sessionName);
973
987
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskplane",
3
- "version": "0.23.8",
3
+ "version": "0.23.10",
4
4
  "description": "AI agent orchestration for pi — parallel task execution with checkpoint discipline",
5
5
  "keywords": [
6
6
  "pi-package",