taskplane 0.24.2 → 0.24.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.
@@ -610,7 +610,7 @@ export const DEFAULT_ORCHESTRATOR_SECTION: OrchestratorSection = {
610
610
  onTaskFailure: "skip-dependents",
611
611
  onMergeFailure: "pause",
612
612
  stallTimeout: 30,
613
- maxWorkerMinutes: 30,
613
+ maxWorkerMinutes: 120,
614
614
  abortGracePeriod: 60,
615
615
  },
616
616
  monitoring: {
@@ -2214,7 +2214,7 @@ export async function executeLaneV2(
2214
2214
  projectName: config.project?.name || "project",
2215
2215
  maxIterations: 20,
2216
2216
  noProgressLimit: 3,
2217
- maxWorkerMinutes: config.failure?.maxWorkerMinutes || 30,
2217
+ maxWorkerMinutes: config.failure?.maxWorkerMinutes || 120,
2218
2218
  warnPercent: 85,
2219
2219
  killPercent: 95,
2220
2220
  onSupervisorAlert,
@@ -286,30 +286,32 @@ export async function executeTaskV2(
286
286
  let iterationTelemetry: Partial<AgentHostResult> = {};
287
287
 
288
288
  const spawned = spawnAgent(hostOpts, undefined, (telemetry) => {
289
- // Context pressure check
290
- if (telemetry.contextUsage) {
291
- const pct = telemetry.contextUsage.percent;
292
- if (pct >= config.warnPercent) {
293
- const msg = `Wrap up (context ${Math.round(pct)}%)`;
294
- if (!existsSync(wrapUpFile)) writeFileSync(wrapUpFile, msg);
295
- }
296
- if (pct >= config.killPercent) {
297
- workerKillReason = "context";
298
- spawned.kill();
289
+ try {
290
+ // Context pressure check
291
+ if (telemetry.contextUsage) {
292
+ const pct = telemetry.contextUsage.percent;
293
+ if (pct >= config.warnPercent) {
294
+ const msg = `Wrap up (context ${Math.round(pct)}%)`;
295
+ if (!existsSync(wrapUpFile)) writeFileSync(wrapUpFile, msg);
296
+ }
297
+ if (pct >= config.killPercent) {
298
+ workerKillReason = "context";
299
+ spawned.kill();
300
+ }
299
301
  }
300
- }
301
302
 
302
- iterationTelemetry = telemetry;
303
- lastTelemetry = telemetry;
304
- // Emit lane snapshot
305
- emitSnapshot(config, taskId, "running", telemetry, statusPath);
303
+ iterationTelemetry = telemetry;
304
+ lastTelemetry = telemetry;
305
+ // Emit lane snapshot
306
+ emitSnapshot(config, taskId, "running", telemetry, statusPath);
307
+ } catch { /* non-fatal: telemetry callback must never crash the engine */ }
306
308
  });
307
309
 
308
310
  // Reviewer telemetry is written by the worker bridge during review_step.
309
311
  // Poll snapshot refresh independently from worker message_end cadence so
310
312
  // the dashboard sees reviewer activity while tool calls are in-flight.
311
313
  const reviewerRefresh = setInterval(() => {
312
- emitSnapshot(config, taskId, "running", iterationTelemetry, statusPath);
314
+ try { emitSnapshot(config, taskId, "running", iterationTelemetry, statusPath); } catch { /* non-fatal */ }
313
315
  }, 1000);
314
316
 
315
317
  let workerResult: AgentHostResult;
@@ -605,6 +607,12 @@ export function readReviewerTelemetrySnapshot(
605
607
  }
606
608
  }
607
609
 
610
+ /**
611
+ * Emit a lane snapshot to disk. NON-THROWING by contract — all errors are
612
+ * caught and logged. This function is called from setInterval callbacks
613
+ * and onTelemetry callbacks where an unhandled throw would trigger
614
+ * uncaughtException and crash the engine-worker process.
615
+ */
608
616
  function emitSnapshot(
609
617
  config: LaneRunnerConfig,
610
618
  taskId: string,
@@ -612,51 +620,56 @@ function emitSnapshot(
612
620
  telemetry: Partial<AgentHostResult>,
613
621
  statusPath: string,
614
622
  ): void {
615
- // Parse progress from STATUS.md
616
- let progress: RuntimeTaskProgress | null = null;
617
623
  try {
618
- const content = readFileSync(statusPath, "utf-8");
619
- const parsed = parseStatusMd(content);
620
- const currentStepMatch = content.match(/\*\*Current Step:\*\*\s*(.+)/);
621
- const checked = parsed.steps.reduce((sum, s) => sum + s.totalChecked, 0);
622
- const total = parsed.steps.reduce((sum, s) => sum + s.totalItems, 0);
623
- progress = {
624
- currentStep: currentStepMatch?.[1]?.trim() || "Unknown",
625
- checked,
626
- total,
627
- iteration: parsed.iteration,
628
- reviews: parsed.reviewCounter,
624
+ // Parse progress from STATUS.md
625
+ let progress: RuntimeTaskProgress | null = null;
626
+ try {
627
+ const content = readFileSync(statusPath, "utf-8");
628
+ const parsed = parseStatusMd(content);
629
+ const currentStepMatch = content.match(/\*\*Current Step:\*\*\s*(.+)/);
630
+ const checked = parsed.steps.reduce((sum, s) => sum + s.totalChecked, 0);
631
+ const total = parsed.steps.reduce((sum, s) => sum + s.totalItems, 0);
632
+ progress = {
633
+ currentStep: currentStepMatch?.[1]?.trim() || "Unknown",
634
+ checked,
635
+ total,
636
+ iteration: parsed.iteration,
637
+ reviews: parsed.reviewCounter,
638
+ };
639
+ } catch { /* best effort */ }
640
+
641
+ const reviewerSnapshot = readReviewerTelemetrySnapshot(config, statusPath);
642
+
643
+ const snapshot: RuntimeLaneSnapshot = {
644
+ batchId: config.batchId,
645
+ laneNumber: config.laneNumber,
646
+ laneId: `lane-${config.laneNumber}`,
647
+ repoId: config.repoId,
648
+ taskId,
649
+ segmentId: null,
650
+ status,
651
+ worker: {
652
+ agentId: buildRuntimeAgentId(config.agentIdPrefix, config.laneNumber, "worker"),
653
+ status: mapLaneSnapshotStatusToWorkerStatus(status),
654
+ elapsedMs: telemetry.durationMs ?? 0,
655
+ toolCalls: telemetry.toolCalls ?? 0,
656
+ contextPct: telemetry.contextUsage?.percent ?? 0,
657
+ costUsd: telemetry.costUsd ?? 0,
658
+ lastTool: telemetry.lastTool ?? "",
659
+ inputTokens: telemetry.inputTokens ?? 0,
660
+ outputTokens: telemetry.outputTokens ?? 0,
661
+ cacheReadTokens: telemetry.cacheReadTokens ?? 0,
662
+ cacheWriteTokens: telemetry.cacheWriteTokens ?? 0,
663
+ },
664
+ reviewer: reviewerSnapshot,
665
+ progress,
666
+ updatedAt: Date.now(),
629
667
  };
630
- } catch { /* best effort */ }
631
-
632
- const reviewerSnapshot = readReviewerTelemetrySnapshot(config, statusPath);
633
-
634
- const snapshot: RuntimeLaneSnapshot = {
635
- batchId: config.batchId,
636
- laneNumber: config.laneNumber,
637
- laneId: `lane-${config.laneNumber}`,
638
- repoId: config.repoId,
639
- taskId,
640
- segmentId: null,
641
- status,
642
- worker: {
643
- agentId: buildRuntimeAgentId(config.agentIdPrefix, config.laneNumber, "worker"),
644
- status: mapLaneSnapshotStatusToWorkerStatus(status),
645
- elapsedMs: telemetry.durationMs ?? 0,
646
- toolCalls: telemetry.toolCalls ?? 0,
647
- contextPct: telemetry.contextUsage?.percent ?? 0,
648
- costUsd: telemetry.costUsd ?? 0,
649
- lastTool: telemetry.lastTool ?? "",
650
- inputTokens: telemetry.inputTokens ?? 0,
651
- outputTokens: telemetry.outputTokens ?? 0,
652
- cacheReadTokens: telemetry.cacheReadTokens ?? 0,
653
- cacheWriteTokens: telemetry.cacheWriteTokens ?? 0,
654
- },
655
- reviewer: reviewerSnapshot,
656
- progress,
657
- updatedAt: Date.now(),
658
- };
659
668
 
660
- writeLaneSnapshot(config.stateRoot, config.batchId, config.laneNumber, snapshot as any);
669
+ writeLaneSnapshot(config.stateRoot, config.batchId, config.laneNumber, snapshot as any);
670
+ } catch {
671
+ // Non-fatal: snapshot is telemetry, not execution-critical.
672
+ // Swallow to prevent uncaughtException crash in setInterval/callback contexts.
673
+ }
661
674
  }
662
675
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskplane",
3
- "version": "0.24.2",
3
+ "version": "0.24.3",
4
4
  "description": "AI agent orchestration for pi — parallel task execution with checkpoint discipline",
5
5
  "keywords": [
6
6
  "pi-package",