taskplane 0.24.0 → 0.24.2
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 +9 -4
- package/dashboard/server.cjs +38 -20
- package/extensions/taskplane/agent-bridge-extension.ts +151 -13
- package/extensions/taskplane/config-loader.ts +1158 -1085
- package/extensions/taskplane/execution.ts +14 -6
- package/extensions/taskplane/lane-runner.ts +71 -2
- package/extensions/taskplane/process-registry.ts +1 -1
- package/package.json +1 -1
|
@@ -853,18 +853,26 @@ export async function resolveTaskMonitorState(
|
|
|
853
853
|
runtimeBackend?: RuntimeBackend,
|
|
854
854
|
v2Context?: { stateRoot: string; batchId: string; laneNumber: number },
|
|
855
855
|
): Promise<TaskMonitorSnapshot> {
|
|
856
|
-
// TP-115: Backend-aware liveness check.
|
|
856
|
+
// TP-115/TP-127: Backend-aware liveness check.
|
|
857
857
|
// V2: read the lane snapshot file written by lane-runner every second.
|
|
858
|
-
// Snapshot status is authoritative — no PID probing needed.
|
|
859
858
|
// If snapshot doesn't exist yet, assume alive (lane-runner startup race).
|
|
859
|
+
// If snapshot belongs to a different task, it's stale transition data from
|
|
860
|
+
// the previous wave/task and should be treated like startup grace (alive).
|
|
860
861
|
// Legacy: check lane-session liveness.
|
|
861
862
|
let sessionAlive: boolean;
|
|
862
863
|
if (runtimeBackend === "v2" && v2Context) {
|
|
863
864
|
const snap = readLaneSnapshot(v2Context.stateRoot, v2Context.batchId, v2Context.laneNumber);
|
|
864
|
-
if (snap == null) {
|
|
865
|
-
// Snapshot not written yet
|
|
866
|
-
// Assume alive
|
|
867
|
-
|
|
865
|
+
if (snap == null || snap.taskId !== taskId) {
|
|
866
|
+
// Snapshot not written yet OR snapshot still points to a prior task.
|
|
867
|
+
// Assume alive initially, but if stale for >30s consult the registry
|
|
868
|
+
// to avoid indefinite false "running" if the lane-runner died.
|
|
869
|
+
const staleMs = snap?.updatedAt ? (now - snap.updatedAt) : 0;
|
|
870
|
+
if (staleMs > 30_000) {
|
|
871
|
+
// Snapshot hasn't been updated for 30s+ — check registry as fallback
|
|
872
|
+
sessionAlive = isV2AgentAlive(sessionName, runtimeBackend);
|
|
873
|
+
} else {
|
|
874
|
+
sessionAlive = true;
|
|
875
|
+
}
|
|
868
876
|
} else {
|
|
869
877
|
sessionAlive = snap.status === "running";
|
|
870
878
|
}
|
|
@@ -283,6 +283,7 @@ export async function executeTaskV2(
|
|
|
283
283
|
|
|
284
284
|
// Context pressure: write wrap-up signal before kill
|
|
285
285
|
let workerKillReason: "context" | "timer" | null = null;
|
|
286
|
+
let iterationTelemetry: Partial<AgentHostResult> = {};
|
|
286
287
|
|
|
287
288
|
const spawned = spawnAgent(hostOpts, undefined, (telemetry) => {
|
|
288
289
|
// Context pressure check
|
|
@@ -298,12 +299,25 @@ export async function executeTaskV2(
|
|
|
298
299
|
}
|
|
299
300
|
}
|
|
300
301
|
|
|
302
|
+
iterationTelemetry = telemetry;
|
|
301
303
|
lastTelemetry = telemetry;
|
|
302
304
|
// Emit lane snapshot
|
|
303
305
|
emitSnapshot(config, taskId, "running", telemetry, statusPath);
|
|
304
306
|
});
|
|
305
307
|
|
|
306
|
-
|
|
308
|
+
// Reviewer telemetry is written by the worker bridge during review_step.
|
|
309
|
+
// Poll snapshot refresh independently from worker message_end cadence so
|
|
310
|
+
// the dashboard sees reviewer activity while tool calls are in-flight.
|
|
311
|
+
const reviewerRefresh = setInterval(() => {
|
|
312
|
+
emitSnapshot(config, taskId, "running", iterationTelemetry, statusPath);
|
|
313
|
+
}, 1000);
|
|
314
|
+
|
|
315
|
+
let workerResult: AgentHostResult;
|
|
316
|
+
try {
|
|
317
|
+
workerResult = await spawned.promise;
|
|
318
|
+
} finally {
|
|
319
|
+
clearInterval(reviewerRefresh);
|
|
320
|
+
}
|
|
307
321
|
|
|
308
322
|
// TP-115: Update lastTelemetry with definitive final values from AgentHostResult
|
|
309
323
|
lastTelemetry = workerResult;
|
|
@@ -538,6 +552,59 @@ function makeResult(
|
|
|
538
552
|
return result;
|
|
539
553
|
}
|
|
540
554
|
|
|
555
|
+
/** Max age for reviewer state file before it's considered stale (2 minutes). */
|
|
556
|
+
const REVIEWER_STATE_STALE_MS = 120_000;
|
|
557
|
+
|
|
558
|
+
export function readReviewerTelemetrySnapshot(
|
|
559
|
+
config: LaneRunnerConfig,
|
|
560
|
+
statusPath: string,
|
|
561
|
+
): (RuntimeAgentTelemetrySnapshot & { reviewType?: string; reviewStep?: number }) | null {
|
|
562
|
+
const reviewerPath = join(dirname(statusPath), ".reviewer-state.json");
|
|
563
|
+
if (!existsSync(reviewerPath)) return null;
|
|
564
|
+
|
|
565
|
+
try {
|
|
566
|
+
const raw = readFileSync(reviewerPath, "utf-8");
|
|
567
|
+
const parsed = JSON.parse(raw) as Partial<{
|
|
568
|
+
status: string;
|
|
569
|
+
elapsedMs: number;
|
|
570
|
+
toolCalls: number;
|
|
571
|
+
contextPct: number;
|
|
572
|
+
costUsd: number;
|
|
573
|
+
lastTool: string;
|
|
574
|
+
inputTokens: number;
|
|
575
|
+
outputTokens: number;
|
|
576
|
+
cacheReadTokens: number;
|
|
577
|
+
cacheWriteTokens: number;
|
|
578
|
+
updatedAt: number;
|
|
579
|
+
reviewType: string;
|
|
580
|
+
reviewStep: number;
|
|
581
|
+
}>;
|
|
582
|
+
|
|
583
|
+
if (parsed.status !== "running") return null;
|
|
584
|
+
|
|
585
|
+
// Stale guard: if updatedAt is present and older than threshold, ignore
|
|
586
|
+
if (parsed.updatedAt && (Date.now() - parsed.updatedAt) > REVIEWER_STATE_STALE_MS) return null;
|
|
587
|
+
|
|
588
|
+
return {
|
|
589
|
+
agentId: buildRuntimeAgentId(config.agentIdPrefix, config.laneNumber, "reviewer"),
|
|
590
|
+
status: "running",
|
|
591
|
+
elapsedMs: Number.isFinite(parsed.elapsedMs) ? Number(parsed.elapsedMs) : 0,
|
|
592
|
+
toolCalls: Number.isFinite(parsed.toolCalls) ? Number(parsed.toolCalls) : 0,
|
|
593
|
+
contextPct: Number.isFinite(parsed.contextPct) ? Number(parsed.contextPct) : 0,
|
|
594
|
+
costUsd: Number.isFinite(parsed.costUsd) ? Number(parsed.costUsd) : 0,
|
|
595
|
+
lastTool: typeof parsed.lastTool === "string" ? parsed.lastTool : "",
|
|
596
|
+
inputTokens: Number.isFinite(parsed.inputTokens) ? Number(parsed.inputTokens) : 0,
|
|
597
|
+
outputTokens: Number.isFinite(parsed.outputTokens) ? Number(parsed.outputTokens) : 0,
|
|
598
|
+
cacheReadTokens: Number.isFinite(parsed.cacheReadTokens) ? Number(parsed.cacheReadTokens) : 0,
|
|
599
|
+
cacheWriteTokens: Number.isFinite(parsed.cacheWriteTokens) ? Number(parsed.cacheWriteTokens) : 0,
|
|
600
|
+
reviewType: typeof parsed.reviewType === "string" ? parsed.reviewType : undefined,
|
|
601
|
+
reviewStep: Number.isFinite(parsed.reviewStep) ? Number(parsed.reviewStep) : undefined,
|
|
602
|
+
};
|
|
603
|
+
} catch {
|
|
604
|
+
return null;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
541
608
|
function emitSnapshot(
|
|
542
609
|
config: LaneRunnerConfig,
|
|
543
610
|
taskId: string,
|
|
@@ -562,6 +629,8 @@ function emitSnapshot(
|
|
|
562
629
|
};
|
|
563
630
|
} catch { /* best effort */ }
|
|
564
631
|
|
|
632
|
+
const reviewerSnapshot = readReviewerTelemetrySnapshot(config, statusPath);
|
|
633
|
+
|
|
565
634
|
const snapshot: RuntimeLaneSnapshot = {
|
|
566
635
|
batchId: config.batchId,
|
|
567
636
|
laneNumber: config.laneNumber,
|
|
@@ -583,7 +652,7 @@ function emitSnapshot(
|
|
|
583
652
|
cacheReadTokens: telemetry.cacheReadTokens ?? 0,
|
|
584
653
|
cacheWriteTokens: telemetry.cacheWriteTokens ?? 0,
|
|
585
654
|
},
|
|
586
|
-
reviewer:
|
|
655
|
+
reviewer: reviewerSnapshot,
|
|
587
656
|
progress,
|
|
588
657
|
updatedAt: Date.now(),
|
|
589
658
|
};
|
|
@@ -353,7 +353,7 @@ export function readLaneSnapshot(
|
|
|
353
353
|
stateRoot: string,
|
|
354
354
|
batchId: string,
|
|
355
355
|
laneNumber: number,
|
|
356
|
-
): { status: string; updatedAt?: number } | null {
|
|
356
|
+
): { taskId?: string | null; status: string; updatedAt?: number } | null {
|
|
357
357
|
try {
|
|
358
358
|
const p = runtimeLaneSnapshotPath(stateRoot, batchId, laneNumber);
|
|
359
359
|
if (!existsSync(p)) return null;
|