pi-goal-list-loop-audit 0.29.14 → 0.29.16
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.
|
@@ -388,7 +388,12 @@ function loopLines(l: LoopState, now: number, theme?: DisplayTheme, width?: numb
|
|
|
388
388
|
`${paint(theme, "accent", "●")} ${truncate(l.target, budgetFor(width, 3, 64))}`,
|
|
389
389
|
`├─ loop ${arrow} iter ${l.iteration}/${l.maxIterations > 0 ? l.maxIterations : "∞"} · ${fmtElapsed(now - Date.parse(l.startedAt))}`,
|
|
390
390
|
`├─ best ${best} · last ${l.lastValue ?? "n/a"} · ${stall}`,
|
|
391
|
-
|
|
391
|
+
// v0.29.15: the audit loop's measure is orchestrator-owned shell — the
|
|
392
|
+
// raw grep reads like leaked internals ("that weird line"). Name what
|
|
393
|
+
// it measures instead; user-authored measures still show raw.
|
|
394
|
+
`└─ ${paint(theme, "dim", l.kind === "audit"
|
|
395
|
+
? "metric: closed findings ('- [x]' count)"
|
|
396
|
+
: truncate(l.measureCmd, budgetFor(width, 3, 56)))}`,
|
|
392
397
|
];
|
|
393
398
|
if (l.branchName) lines.push(`⎇ ${paint(theme, "muted", truncate(l.branchName, budgetFor(width, 3, 50)))}`);
|
|
394
399
|
return lines;
|
package/extensions/loops/goal.ts
CHANGED
|
@@ -424,6 +424,15 @@ const countedLoopTokenMessages = new Set<string>();
|
|
|
424
424
|
|
|
425
425
|
// Heartbeat self-watchdog state: liveness is the loop's own job.
|
|
426
426
|
let lastActivityAt = Date.now();
|
|
427
|
+
// v0.29.16: zombie-run watchdog clock — updated ONLY by genuine pi stream
|
|
428
|
+
// events (message_update / tool_call / agent_start / turn_start /
|
|
429
|
+
// agent_end), never by heartbeat-internal bookkeeping. A session pi
|
|
430
|
+
// reports as BUSY with zero stream events for N min is a hung provider
|
|
431
|
+
// stream (pi has no read timeout): continuations queue into the void and
|
|
432
|
+
// the busy flag conceals the wedge from every other watchdog (field:
|
|
433
|
+
// hellhunter + hegemon 2026-07-30, MiniMax streams died silently).
|
|
434
|
+
let lastStreamActivityAt = Date.now();
|
|
435
|
+
let lastZombieAlertAt = 0;
|
|
427
436
|
let lastWedgeAlertAt = 0;
|
|
428
437
|
let heartbeatNudges = 0;
|
|
429
438
|
// v0.28.4 (P3): skip nudge accounting for the first agent_end turns after a
|
|
@@ -445,6 +454,9 @@ let carryoverResolved = true;
|
|
|
445
454
|
let completionAuditInFlight = false;
|
|
446
455
|
let heartbeatTimer: NodeJS.Timeout | null = null;
|
|
447
456
|
|
|
457
|
+
const ZOMBIE_RUN_SILENT_MS = 20 * 60_000;
|
|
458
|
+
const ZOMBIE_RUN_ALERT_THROTTLE_MS = 10 * 60_000;
|
|
459
|
+
|
|
448
460
|
function noteActivity(real = false): void {
|
|
449
461
|
lastActivityAt = Date.now();
|
|
450
462
|
if (real) consecutiveStalls = 0;
|
|
@@ -653,6 +665,20 @@ function heartbeatTick(): void {
|
|
|
653
665
|
// escalation would PAUSE the goal — silently cancelling the
|
|
654
666
|
// interruptedAt → hold-on-restart promise the footer shows.
|
|
655
667
|
if (probeExtensionApiStale()) { goStaleTerminal(ctx, "heartbeat probe"); return; }
|
|
668
|
+
// v0.29.16: zombie-run watchdog. pi reports BUSY (a run is "active") but
|
|
669
|
+
// zero stream events for 20 min = the provider stream hung silently —
|
|
670
|
+
// queued continuations can't land, and every other watchdog stays quiet
|
|
671
|
+
// because busy≠wedged. Detection + loud guidance only: aborting a turn
|
|
672
|
+
// is the user's call (consent line); Esc frees the queue and the
|
|
673
|
+
// heartbeat refires the goal/loop by itself.
|
|
674
|
+
const streamSilentMs = Date.now() - lastStreamActivityAt;
|
|
675
|
+
if (isSupervising() && !idle && streamSilentMs >= ZOMBIE_RUN_SILENT_MS && Date.now() - lastZombieAlertAt >= ZOMBIE_RUN_ALERT_THROTTLE_MS) {
|
|
676
|
+
lastZombieAlertAt = Date.now();
|
|
677
|
+
appendLedger(ctx.cwd, "zombie_run_suspected", { streamSilentMs, pending });
|
|
678
|
+
ctx.ui.notify(`glla: the session has been BUSY with zero stream activity for ${Math.round(streamSilentMs / 60000)} min — the provider stream is hung (pi never times it out; queued continuations can't land). Press Esc to abort the zombie turn — the goal/loop refires itself.`, "warning");
|
|
679
|
+
notifyExternal(ctx, `glla: zombie run suspected (${Math.round(streamSilentMs / 60000)} min busy-silent) — press Esc to abort.`);
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
656
682
|
// v0.29.1: stranded-audit recovery. A goal left in "auditing" with NO
|
|
657
683
|
// in-flight audit means the auditor's result never landed (wedged queue
|
|
658
684
|
// ate the tool result; compaction/restart mid-audit). Field-observed in
|
|
@@ -5467,6 +5493,7 @@ export default function (pi: ExtensionAPI): void {
|
|
|
5467
5493
|
// continuation loop.
|
|
5468
5494
|
if (isForeignCtx(ctx)) return;
|
|
5469
5495
|
noteActivity(true);
|
|
5496
|
+
lastStreamActivityAt = Date.now();
|
|
5470
5497
|
// v0.27.2: folded-in length-continue (standalone pi-length-continue is
|
|
5471
5498
|
// deprecated). A response cut by the per-response output cap is NOT a
|
|
5472
5499
|
// completed turn (no telemetry), NOT a stall (no no-tool nudge), and
|
|
@@ -5734,9 +5761,22 @@ export default function (pi: ExtensionAPI): void {
|
|
|
5734
5761
|
pi.on("tool_call", () => {
|
|
5735
5762
|
toolCallsThisTurn++;
|
|
5736
5763
|
noteActivity(true);
|
|
5764
|
+
lastStreamActivityAt = Date.now();
|
|
5737
5765
|
// v0.24.0: count loop-iteration tool calls (narration-only detection).
|
|
5738
5766
|
if (isLoopActive()) {
|
|
5739
5767
|
state.loop!.toolsThisTurn = (state.loop!.toolsThisTurn ?? 0) + 1;
|
|
5740
5768
|
}
|
|
5741
5769
|
});
|
|
5770
|
+
|
|
5771
|
+
// v0.29.16: stream liveness for the zombie-run watchdog — deltas, run
|
|
5772
|
+
// starts, and turn starts all prove the provider stream is alive.
|
|
5773
|
+
pi.on("message_update", () => {
|
|
5774
|
+
lastStreamActivityAt = Date.now();
|
|
5775
|
+
});
|
|
5776
|
+
pi.on("agent_start", () => {
|
|
5777
|
+
lastStreamActivityAt = Date.now();
|
|
5778
|
+
});
|
|
5779
|
+
pi.on("turn_start", () => {
|
|
5780
|
+
lastStreamActivityAt = Date.now();
|
|
5781
|
+
});
|
|
5742
5782
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goal-list-loop-audit",
|
|
3
|
-
"version": "0.29.
|
|
3
|
+
"version": "0.29.16",
|
|
4
4
|
"description": "Goal. Loop. Audit. Done. \u2014 a pi-coding-agent extension that supervises long-running work, with isolated auditor on each completion. Beat bamboozling by design: the auditor runs in a fresh session with no extensions, no skills, no editor \u2014 only the read tools needed to verify your goal.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "dracon",
|