pi-goal-list-loop-audit 0.34.10 → 0.34.12
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.
|
@@ -202,7 +202,14 @@ export function buildStatusText(state: State, audit?: AuditDisplayProgress | nul
|
|
|
202
202
|
const kind = pauseKind(g);
|
|
203
203
|
if (kind === "decision") return `glla: ${paint(theme, "accent", "⏸ decision needed")}${heldSuffix}`;
|
|
204
204
|
if (kind === "error") return `glla: ${paint(theme, "error", `⏸ action needed — ${truncate(g.pauseReason ?? "", 30)}`)}${heldSuffix}`;
|
|
205
|
-
if (kind === "wait")
|
|
205
|
+
if (kind === "wait") {
|
|
206
|
+
// v0.34.12: live countdown (the UI ticker keeps rendering through a
|
|
207
|
+
// timed wait) — "resumes in 23m" beats a static clock time, and a
|
|
208
|
+
// passed resumeAt says "resuming…" instead of lying about the past.
|
|
209
|
+
const rms = g.pauseResumeAt ? Date.parse(g.pauseResumeAt) - now : Number.NaN;
|
|
210
|
+
const when = Number.isNaN(rms) ? "" : rms <= 0 ? " · resuming…" : ` · resumes in ${fmtElapsed(rms)}`;
|
|
211
|
+
return `glla: ${paint(theme, "dim", `⏳ waiting${when}`)}${heldSuffix}`;
|
|
212
|
+
}
|
|
206
213
|
const label = `paused ⏸ ${truncate(g.pauseReason ?? "", 40)}`;
|
|
207
214
|
return `glla: ${paint(theme, pauseIsError(g) ? "error" : "warning", label)}${heldSuffix}`;
|
|
208
215
|
}
|
package/extensions/loops/goal.ts
CHANGED
|
@@ -630,15 +630,38 @@ let heartbeatTimer: NodeJS.Timeout | null = null;
|
|
|
630
630
|
|
|
631
631
|
const ZOMBIE_RUN_SILENT_MS = 20 * 60_000;
|
|
632
632
|
const ZOMBIE_RUN_ALERT_THROTTLE_MS = 10 * 60_000;
|
|
633
|
+
// v0.34.11: unanswered-continuation watchdog. Hellhunter 2026-08-01: at a
|
|
634
|
+
// list-transition completion boundary pi ACCEPTED every continuation
|
|
635
|
+
// (sendMessage never threw; session reported idle) but started NO turn —
|
|
636
|
+
// transcript frozen, tokens flat, 10+ minutes of refires into the void.
|
|
637
|
+
// Same family as the post-compaction dropped trigger (v0.26.5), but the
|
|
638
|
+
// pending-latch watchdog needs idle&&pending and pi reported no pending
|
|
639
|
+
// here, and the zombie watchdog needs busy — this shape falls between both
|
|
640
|
+
// chairs. Disarm signal = real activity (agent_end/tool_call) AFTER the
|
|
641
|
+
// last send; a landed turn — even a lazy text-only one — disarms it.
|
|
642
|
+
const CONTINUATION_UNANSWERED_MS = 150_000;
|
|
643
|
+
const CONTINUATION_UNANSWERED_THROTTLE_MS = 300_000;
|
|
644
|
+
// v0.34.12: eager-continuation settle delay. Hellhunter 2026-08-01 (post-
|
|
645
|
+
// restart): every turn cycle paid a 60s heartbeat tax because the eager
|
|
646
|
+
// continuation fires AT agent_end — exactly pi's turn-teardown blackhole
|
|
647
|
+
// window — so it vanished and the 60s heartbeat refire did the real work.
|
|
648
|
+
// Ledger showed the tell: goal_continuation_sent pairs + a refire per
|
|
649
|
+
// cycle. Sending 2.5s AFTER agent_end lets teardown settle; the send lands
|
|
650
|
+
// and the next turn starts immediately. 2.5s per turn beats 60s per turn.
|
|
651
|
+
const EAGER_CONTINUATION_SETTLE_MS = Number(process.env.GLLA_EAGER_SETTLE_MS ?? 2_500);
|
|
633
652
|
// v0.29.19: dead-turn caps (agent_end exemption path). 6 consecutive
|
|
634
653
|
// provider-error turns = a real outage, not bad luck — stop honestly.
|
|
635
654
|
// 3 consecutive user aborts = the user means it (user aborts mean STOP).
|
|
636
655
|
const LOOP_MAX_CONSECUTIVE_ERRORS = 6;
|
|
637
656
|
const LOOP_MAX_CONSECUTIVE_ABORTS = 3;
|
|
638
657
|
|
|
658
|
+
let lastRealActivityAt = 0;
|
|
659
|
+
let lastContinuationSentAt = 0;
|
|
660
|
+
let lastUnansweredAlertAt = 0;
|
|
661
|
+
|
|
639
662
|
function noteActivity(real = false): void {
|
|
640
663
|
lastActivityAt = Date.now();
|
|
641
|
-
if (real) consecutiveStalls = 0;
|
|
664
|
+
if (real) { consecutiveStalls = 0; lastRealActivityAt = lastActivityAt; }
|
|
642
665
|
}
|
|
643
666
|
|
|
644
667
|
function isSupervising(): boolean {
|
|
@@ -703,7 +726,9 @@ function startUITicker(): void {
|
|
|
703
726
|
if (uiTicker) return;
|
|
704
727
|
uiTicker = setInterval(() => {
|
|
705
728
|
const ctx = freshCtx();
|
|
706
|
-
|
|
729
|
+
// v0.34.12: keep ticking during a timed wait-pause too — the status
|
|
730
|
+
// line counts down to resumeAt live (pully field request 2026-08-01).
|
|
731
|
+
if (ctx && (isSupervising() || (state.goal?.status === "paused" && !!state.goal.pauseResumeAt))) refreshUI(ctx);
|
|
707
732
|
}, 1_000);
|
|
708
733
|
uiTicker.unref?.();
|
|
709
734
|
}
|
|
@@ -932,6 +957,25 @@ function heartbeatTick(): void {
|
|
|
932
957
|
notifyExternal(ctx, `glla: zombie run suspected (${Math.round(streamSilentMs / 60000)} min busy-silent) — press Esc to abort.`);
|
|
933
958
|
return;
|
|
934
959
|
}
|
|
960
|
+
// v0.34.11: unanswered-continuation watchdog — pi took the send but no
|
|
961
|
+
// turn started (no agent_end, no tool call, no stream). Re-sends don't
|
|
962
|
+
// unstick a dropped trigger (hegemon law) — this alert's job is to say
|
|
963
|
+
// the cure LOUDLY at ~2.5 min instead of leaving a silent 20-30 min gap
|
|
964
|
+
// before the zombie/wedge alerts. Does NOT return: the heartbeat refire
|
|
965
|
+
// below keeps sending underneath in case pi unsticks by itself.
|
|
966
|
+
if (
|
|
967
|
+
isSupervising() &&
|
|
968
|
+
lastContinuationSentAt > 0 &&
|
|
969
|
+
lastRealActivityAt < lastContinuationSentAt &&
|
|
970
|
+
Date.now() - lastContinuationSentAt >= CONTINUATION_UNANSWERED_MS &&
|
|
971
|
+
Date.now() - lastUnansweredAlertAt >= CONTINUATION_UNANSWERED_THROTTLE_MS
|
|
972
|
+
) {
|
|
973
|
+
lastUnansweredAlertAt = Date.now();
|
|
974
|
+
appendLedger(ctx.cwd, "continuation_unanswered", { silentMs: Date.now() - lastContinuationSentAt });
|
|
975
|
+
const msg = `glla: pi accepted the continuation ${Math.round((Date.now() - lastContinuationSentAt) / 60_000)}m ago but NO turn has started — no tool calls, no tokens, transcript frozen (the turn trigger is wedged; same pi failure family as the post-compaction blackhole). Re-sends don't unstick it. Cure: /reload — autoresume re-fires the ${isLoopActive() ? "loop" : "goal/list item"} automatically.`;
|
|
976
|
+
ctx.ui.notify(msg, "warning");
|
|
977
|
+
notifyExternal(ctx, msg);
|
|
978
|
+
}
|
|
935
979
|
// v0.29.1: stranded-audit recovery. A goal left in "auditing" with NO
|
|
936
980
|
// in-flight audit means the auditor's result never landed (wedged queue
|
|
937
981
|
// ate the tool result; compaction/restart mid-audit). Field-observed in
|
|
@@ -1164,6 +1208,7 @@ function sendContinuation(goalId: string): void {
|
|
|
1164
1208
|
if (resync) postCompactResyncPending = false; // consumed only by a landed send
|
|
1165
1209
|
continuationRearmStreak = 0; continuationRearmSince = 0; // v0.28.5 (E3): a landed send clears the storm
|
|
1166
1210
|
appendLedger(ctx.cwd, "goal_continuation_sent", { goalId });
|
|
1211
|
+
lastContinuationSentAt = Date.now();
|
|
1167
1212
|
} catch (err) {
|
|
1168
1213
|
appendLedger(ctx.cwd, "goal_continuation_send_failed", { goalId, error: err instanceof Error ? err.message : String(err) });
|
|
1169
1214
|
// v0.26.7: stale runtime = terminal (sends can never land); anything
|
|
@@ -2845,6 +2890,7 @@ function sendLoopTurn(): void {
|
|
|
2845
2890
|
// refires with zero visibility into whether sends were landing.
|
|
2846
2891
|
loopRearmStreak = 0; loopRearmSince = 0; // v0.28.5 (E3): a landed turn clears the storm
|
|
2847
2892
|
appendLedger(ctx.cwd, "loop_turn_sent", { iteration: loop.iteration });
|
|
2893
|
+
lastContinuationSentAt = Date.now();
|
|
2848
2894
|
} catch (err) {
|
|
2849
2895
|
// stale API — next agent_end reschedules (but if none comes, the
|
|
2850
2896
|
// heartbeat's stall escalation stops the spin — v0.26.1).
|
|
@@ -6675,7 +6721,8 @@ export default function (pi: ExtensionAPI): void {
|
|
|
6675
6721
|
// pause/cancel, the stall watchdog, the 5-consecutive-errors pause, or
|
|
6676
6722
|
// the token guard — never via an elapsed-time cutoff.
|
|
6677
6723
|
|
|
6678
|
-
|
|
6724
|
+
// v0.34.12: NOT immediately — agent_end is the blackhole boundary.
|
|
6725
|
+
scheduleContinuation(ctx, false, EAGER_CONTINUATION_SETTLE_MS);
|
|
6679
6726
|
});
|
|
6680
6727
|
|
|
6681
6728
|
pi.on("tool_call", (event: any) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goal-list-loop-audit",
|
|
3
|
-
"version": "0.34.
|
|
3
|
+
"version": "0.34.12",
|
|
4
4
|
"description": "Mission control for autonomous pi: interview-drafted goals, an audited task queue, and forever-loops (metric, spec, project-audit) that run for hours. An isolated extension-less auditor re-verifies every completion with raw evidence; confirmed drafts, decision pauses and consent gates keep you in charge.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "dracon",
|