pi-goal-list-loop-audit 0.32.0 → 0.32.1
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/extensions/loops/goal.ts +61 -2
- package/package.json +1 -1
package/extensions/loops/goal.ts
CHANGED
|
@@ -668,6 +668,16 @@ let loopRearmStreak = 0;
|
|
|
668
668
|
// whose turn trigger was still dead — pausing a resumable goal 4 minutes
|
|
669
669
|
// after the compact instead of giving pi room to recover.
|
|
670
670
|
let compactionGraceUntil = 0;
|
|
671
|
+
// v0.32.1 (pi-goal-x's lesson — "recover from compacts smarter"): a compact
|
|
672
|
+
// leaves a RESUME DEBT, not just two fixed-offset settle probes that can both
|
|
673
|
+
// lose (field: hellhunter 4-min dangle 2026-07-31; polis stall same day).
|
|
674
|
+
// postCompactResumeOwed discharges only when a real turn starts (agent_start);
|
|
675
|
+
// every heartbeat tick past grace retries it. postCompactResyncPending arms a
|
|
676
|
+
// deterministic [POST-COMPACTION RESYNC] block on the next continuation/loop
|
|
677
|
+
// message (pi-goal-x's #5) so the compacted agent re-anchors on artifact
|
|
678
|
+
// state instead of lost chat history.
|
|
679
|
+
let postCompactResumeOwed = false;
|
|
680
|
+
let postCompactResyncPending = false;
|
|
671
681
|
const COMPACTION_GRACE_MS = 3 * 60_000;
|
|
672
682
|
// v0.28.25: provider-error retry cadence. Field-observed in dracon-utilities
|
|
673
683
|
// (kimi, 19-session fleet on one provider account): a "concurrent request
|
|
@@ -827,6 +837,24 @@ function heartbeatTick(): void {
|
|
|
827
837
|
if (!absorbStaleIfSuperseded(ctx)) goStaleTerminal(ctx, "heartbeat probe");
|
|
828
838
|
return;
|
|
829
839
|
}
|
|
840
|
+
// v0.32.1: post-compaction resume debt — retry on every heartbeat tick
|
|
841
|
+
// past grace until a turn actually starts. Fixed-offset settles alone
|
|
842
|
+
// can both lose (pi busy at 2s AND at grace+2s = a dangling chain).
|
|
843
|
+
if (postCompactResumeOwed && isSupervising() && !abortedStandDown) {
|
|
844
|
+
try {
|
|
845
|
+
if (ctx.isIdle() && !ctx.hasPendingMessages() && continuationTimer === null && loopTimer === null) {
|
|
846
|
+
if (isLoopActive()) {
|
|
847
|
+
appendLedger(ctx.cwd, "compaction_resume_owed_refire", { kind: "loop" });
|
|
848
|
+
scheduleLoopTick(ctx);
|
|
849
|
+
} else if (isActionableGoal()) {
|
|
850
|
+
appendLedger(ctx.cwd, "compaction_resume_owed_refire", { kind: "goal" });
|
|
851
|
+
scheduleContinuation(ctx, true);
|
|
852
|
+
} else {
|
|
853
|
+
postCompactResumeOwed = false; // nothing to resume — discharge
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
} catch { /* next tick */ }
|
|
857
|
+
}
|
|
830
858
|
// v0.29.16: zombie-run watchdog. pi reports BUSY (a run is "active") but
|
|
831
859
|
// zero stream events for 20 min = the provider stream hung silently —
|
|
832
860
|
// queued continuations can't land, and every other watchdog stays quiet
|
|
@@ -1048,11 +1076,13 @@ function sendContinuation(goalId: string): void {
|
|
|
1048
1076
|
}
|
|
1049
1077
|
if (!extensionApi || extensionApiStale) return;
|
|
1050
1078
|
try {
|
|
1079
|
+
const resync = postCompactResyncPending ? buildPostCompactResync() : "";
|
|
1051
1080
|
extensionApi.sendMessage({
|
|
1052
1081
|
customType: GOAL_EVENT_ENTRY,
|
|
1053
|
-
content: continuationPrompt(state.goal!),
|
|
1082
|
+
content: resync + continuationPrompt(state.goal!),
|
|
1054
1083
|
display: false,
|
|
1055
1084
|
}, { triggerTurn: true, deliverAs: "followUp" });
|
|
1085
|
+
if (resync) postCompactResyncPending = false; // consumed only by a landed send
|
|
1056
1086
|
continuationRearmStreak = 0; continuationRearmSince = 0; // v0.28.5 (E3): a landed send clears the storm
|
|
1057
1087
|
appendLedger(ctx.cwd, "goal_continuation_sent", { goalId });
|
|
1058
1088
|
} catch (err) {
|
|
@@ -1105,6 +1135,25 @@ function sendLengthContinue(ctx: ExtensionContext, consecutive: number): void {
|
|
|
1105
1135
|
}
|
|
1106
1136
|
}
|
|
1107
1137
|
|
|
1138
|
+
/** v0.32.1: deterministic post-compaction re-anchor (pi-goal-x's #5) —
|
|
1139
|
+
* prepended to the first continuation/loop message after a compact. */
|
|
1140
|
+
function buildPostCompactResync(): string {
|
|
1141
|
+
const lines: string[] = [
|
|
1142
|
+
"[POST-COMPACTION RESYNC] The transcript was just compacted. Trust the artifacts on disk and .pi-glla/ state — NOT your memory of the prior chat. Re-read files before editing them.",
|
|
1143
|
+
];
|
|
1144
|
+
if (state.goal) {
|
|
1145
|
+
lines.push(`Goal ${state.goal.id} — status ${state.goal.status}`);
|
|
1146
|
+
lines.push(`Objective: ${state.goal.objective.slice(0, 200)}`);
|
|
1147
|
+
const next = findNextPendingTask(state.goal.taskList?.tasks ?? []);
|
|
1148
|
+
if (next) lines.push(`Next pending task: \`${next.id}\` — ${next.title}`);
|
|
1149
|
+
const lastAudit = state.goal.auditHistory?.[state.goal.auditHistory.length - 1];
|
|
1150
|
+
if (lastAudit) lines.push(`Last audit: ${lastAudit.approved ? "APPROVED" : lastAudit.impossible ? "IMPOSSIBLE" : "disapproved"} (${lastAudit.at})`);
|
|
1151
|
+
} else if (state.loop?.active) {
|
|
1152
|
+
lines.push(`Loop: ${state.loop.target.slice(0, 160)} — iteration ${state.loop.iteration}`);
|
|
1153
|
+
}
|
|
1154
|
+
return lines.join("\n") + "\n\n";
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1108
1157
|
function continuationPrompt(goal: Goal): string {
|
|
1109
1158
|
// Read the .md file as the template, then substitute {{tokens}}.
|
|
1110
1159
|
// For v0.1.0 we inline-substitute so we don't need fs at runtime.
|
|
@@ -2617,11 +2666,13 @@ function sendLoopTurn(): void {
|
|
|
2617
2666
|
// instruction (metricless loops; metric loops already vary via values).
|
|
2618
2667
|
const variantNote = metricless ? continueVariant(loop.iteration) : "";
|
|
2619
2668
|
try {
|
|
2669
|
+
const loopResync = postCompactResyncPending ? buildPostCompactResync() : "";
|
|
2620
2670
|
extensionApi.sendMessage({
|
|
2621
2671
|
customType: GOAL_EVENT_ENTRY,
|
|
2622
|
-
content: loopPrompt(loop, regressionNote, strategyNote, boundsNote, interventionNote, variantNote),
|
|
2672
|
+
content: loopResync + loopPrompt(loop, regressionNote, strategyNote, boundsNote, interventionNote, variantNote),
|
|
2623
2673
|
display: false,
|
|
2624
2674
|
}, { triggerTurn: true, deliverAs: "followUp" });
|
|
2675
|
+
if (loopResync) postCompactResyncPending = false; // consumed only by a landed send
|
|
2625
2676
|
// v0.26.1: the send path is ledgered — the hegemon zombie spun 619
|
|
2626
2677
|
// refires with zero visibility into whether sends were landing.
|
|
2627
2678
|
loopRearmStreak = 0; loopRearmSince = 0; // v0.28.5 (E3): a landed turn clears the storm
|
|
@@ -5708,6 +5759,11 @@ export default function (pi: ExtensionAPI): void {
|
|
|
5708
5759
|
continuationRearmStreak = 0; continuationRearmSince = 0;
|
|
5709
5760
|
loopRearmStreak = 0; loopRearmSince = 0;
|
|
5710
5761
|
compactionGraceUntil = Date.now() + COMPACTION_GRACE_MS;
|
|
5762
|
+
// v0.32.1: arm the resume debt + the resync block (the settle probes
|
|
5763
|
+
// below stay as the fast path; the heartbeat now retries the debt on
|
|
5764
|
+
// EVERY post-grace tick until agent_start discharges it).
|
|
5765
|
+
postCompactResumeOwed = true;
|
|
5766
|
+
postCompactResyncPending = true;
|
|
5711
5767
|
const settle = setTimeout(() => {
|
|
5712
5768
|
const c = freshCtx();
|
|
5713
5769
|
if (!c) return;
|
|
@@ -6351,6 +6407,9 @@ export default function (pi: ExtensionAPI): void {
|
|
|
6351
6407
|
});
|
|
6352
6408
|
pi.on("agent_start", () => {
|
|
6353
6409
|
lastStreamActivityAt = Date.now();
|
|
6410
|
+
// v0.32.1: a real turn started — the post-compaction resume debt is
|
|
6411
|
+
// discharged (the heartbeat stops retrying it).
|
|
6412
|
+
postCompactResumeOwed = false;
|
|
6354
6413
|
});
|
|
6355
6414
|
pi.on("turn_start", () => {
|
|
6356
6415
|
lastStreamActivityAt = Date.now();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goal-list-loop-audit",
|
|
3
|
-
"version": "0.32.
|
|
3
|
+
"version": "0.32.1",
|
|
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",
|