taskplane 0.22.2 → 0.22.3

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.
@@ -154,6 +154,8 @@ interface TaskState {
154
154
  persistentReviewerKill: (() => void) | null;
155
155
  /** TP-057: Signal counter for the persistent reviewer (monotonically increasing). */
156
156
  persistentReviewerSignalNum: number;
157
+ /** Reviewer respawn counter — circuit breaker to prevent infinite respawn loops. */
158
+ reviewerRespawnCount: number;
157
159
  totalIterations: number;
158
160
  stepStatuses: Map<number, StepInfo>;
159
161
  }
@@ -172,7 +174,7 @@ function freshState(): TaskState {
172
174
  reviewerInputTokens: 0, reviewerOutputTokens: 0, reviewerCacheReadTokens: 0, reviewerCacheWriteTokens: 0,
173
175
  reviewerCostUsd: 0, reviewerContextPct: 0, reviewerProc: null, reviewerTimer: null,
174
176
  reviewCounter: 0,
175
- persistentReviewerSession: null, persistentReviewerKill: null, persistentReviewerSignalNum: 0,
177
+ persistentReviewerSession: null, persistentReviewerKill: null, persistentReviewerSignalNum: 0, reviewerRespawnCount: 0,
176
178
  totalIterations: 0, stepStatuses: new Map(),
177
179
  };
178
180
  }
@@ -2503,9 +2505,23 @@ export default function (pi: ExtensionAPI) {
2503
2505
 
2504
2506
  if (needsSpawn && state.persistentReviewerSession) {
2505
2507
  // Session was previously active but died — log fallback
2506
- console.error(`[task-runner] persistent reviewer session dead — respawning`);
2508
+ state.reviewerRespawnCount++;
2509
+ const MAX_REVIEWER_RESPAWNS = 3;
2510
+ if (state.reviewerRespawnCount > MAX_REVIEWER_RESPAWNS) {
2511
+ console.error(`[task-runner] reviewer respawn limit (${MAX_REVIEWER_RESPAWNS}) exceeded — skipping review`);
2512
+ logExecution(statusPath, `Reviewer R${num}`,
2513
+ `reviewer respawn limit exceeded (${state.reviewerRespawnCount}/${MAX_REVIEWER_RESPAWNS}) — skipping review`);
2514
+ state.persistentReviewerSession = null;
2515
+ state.persistentReviewerKill = null;
2516
+ state.persistentReviewerSignalNum = 0;
2517
+ return {
2518
+ content: [{ type: "text" as const, text: `⚠️ Reviewer respawn limit exceeded (${MAX_REVIEWER_RESPAWNS}). Review skipped — proceeding without review.` }],
2519
+ details: undefined,
2520
+ };
2521
+ }
2522
+ console.error(`[task-runner] persistent reviewer session dead — respawning (${state.reviewerRespawnCount}/${MAX_REVIEWER_RESPAWNS})`);
2507
2523
  logExecution(statusPath, `Reviewer R${num}`,
2508
- `persistent reviewer dead — respawning for ${reviewType} review`);
2524
+ `persistent reviewer dead — respawning for ${reviewType} review (${state.reviewerRespawnCount}/${MAX_REVIEWER_RESPAWNS})`);
2509
2525
  state.persistentReviewerSession = null;
2510
2526
  state.persistentReviewerKill = null;
2511
2527
  state.persistentReviewerSignalNum = 0;
@@ -2553,7 +2569,8 @@ export default function (pi: ExtensionAPI) {
2553
2569
  };
2554
2570
  } catch (err: any) {
2555
2571
  // ── Fallback: kill persistent session, try fresh spawn ──
2556
- console.error(`[task-runner] persistent reviewer error: ${err?.message || err}`);
2572
+ state.reviewerRespawnCount++;
2573
+ console.error(`[task-runner] persistent reviewer error (${state.reviewerRespawnCount}/3): ${err?.message || err}`);
2557
2574
  logExecution(statusPath, `Reviewer R${num}`,
2558
2575
  `persistent reviewer failed — falling back to fresh spawn: ${err?.message || err}`);
2559
2576
 
@@ -2565,6 +2582,17 @@ export default function (pi: ExtensionAPI) {
2565
2582
  state.persistentReviewerKill = null;
2566
2583
  state.persistentReviewerSignalNum = 0;
2567
2584
 
2585
+ // Circuit breaker — skip review if we've exhausted respawns
2586
+ if (state.reviewerRespawnCount > 3) {
2587
+ console.error(`[task-runner] reviewer respawn limit exceeded — skipping review`);
2588
+ logExecution(statusPath, `Reviewer R${num}`,
2589
+ `reviewer respawn limit exceeded — review skipped`);
2590
+ return {
2591
+ content: [{ type: "text" as const, text: `⚠️ Reviewer respawn limit exceeded. Review skipped — proceeding without review.` }],
2592
+ details: undefined,
2593
+ };
2594
+ }
2595
+
2568
2596
  // ── Fresh spawn fallback (original behavior) ────────
2569
2597
  try {
2570
2598
  const promptContent = readFileSync(requestPath, "utf-8");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskplane",
3
- "version": "0.22.2",
3
+ "version": "0.22.3",
4
4
  "description": "AI agent orchestration for pi — parallel task execution with checkpoint discipline",
5
5
  "keywords": [
6
6
  "pi-package",