pi-goal-list-loop-audit 0.28.24 → 0.28.26
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.
|
@@ -162,6 +162,8 @@ export interface HeartbeatInput {
|
|
|
162
162
|
/** Milliseconds since the last observed agent activity. */
|
|
163
163
|
msSinceActivity: number;
|
|
164
164
|
stallMs?: number;
|
|
165
|
+
/** v0.28.25: consecutive stall refires so far — spaces refires exponentially. */
|
|
166
|
+
consecutiveStalls?: number;
|
|
165
167
|
}
|
|
166
168
|
|
|
167
169
|
/** Should the heartbeat re-fire the continuation right now? */
|
|
@@ -169,7 +171,15 @@ export function shouldHeartbeatRefire(input: HeartbeatInput): boolean {
|
|
|
169
171
|
if (!input.supervising) return false;
|
|
170
172
|
if (!input.sessionIdle) return false;
|
|
171
173
|
if (input.timerPending) return false;
|
|
172
|
-
|
|
174
|
+
// v0.28.25: exponential spacing between stall refires — 1m, 2m, 4m, 8m
|
|
175
|
+
// (cap 8×). Field-observed in junk-runner: the flat 60s gate burned all
|
|
176
|
+
// 5 refires in ~4 minutes into a just-compacted session, pausing a
|
|
177
|
+
// resumable goal instead of giving the provider/queue time to recover.
|
|
178
|
+
// noteActivity() runs at each refire, so msSinceActivity measures the
|
|
179
|
+
// silence SINCE the last refire — scaling the threshold scales the gap.
|
|
180
|
+
const stallMs = input.stallMs ?? HEARTBEAT_STALL_MS;
|
|
181
|
+
const scale = 2 ** Math.min(input.consecutiveStalls ?? 0, 3);
|
|
182
|
+
return input.msSinceActivity >= stallMs * scale;
|
|
173
183
|
}
|
|
174
184
|
|
|
175
185
|
/**
|
|
@@ -176,6 +176,15 @@ export interface Goal {
|
|
|
176
176
|
* At 3 the goal pauses loudly — a broken auditor model must not spin a
|
|
177
177
|
* silent retry-forever loop. Cleared on any real auditor run. */
|
|
178
178
|
auditInfraStreak?: number;
|
|
179
|
+
/** v0.28.26: the completion claim captured when an audit attempt is
|
|
180
|
+
* quota-blocked. The quota retry re-runs the AUDITOR directly with this
|
|
181
|
+
* stored claim instead of re-engaging the agent — re-engaging produced a
|
|
182
|
+
* hallucinated-closure repetition loop in the field (π-games 2026-07-29:
|
|
183
|
+
* the agent concluded the goal was closed, stopped calling complete_goal,
|
|
184
|
+
* and repeated the same essay until the stall brake fired). Cleared when
|
|
185
|
+
* the retry resolves. Only consumed while paused with an "auditor quota:"
|
|
186
|
+
* reason, so a stale value is unreachable by construction. */
|
|
187
|
+
pendingCompletion?: { completionSummary?: string; verificationSummary?: string; at: string };
|
|
179
188
|
/** v0.25.0 (contract item 22): auditor objections extracted as TODOs when
|
|
180
189
|
* aggressiveMode keeps the goal active past the disapproval cap. Rendered
|
|
181
190
|
* into every continuation prompt until the next audit clears them. */
|
package/extensions/loops/goal.ts
CHANGED
|
@@ -471,6 +471,17 @@ let loopRearmStreak = 0;
|
|
|
471
471
|
// after the compact instead of giving pi room to recover.
|
|
472
472
|
let compactionGraceUntil = 0;
|
|
473
473
|
const COMPACTION_GRACE_MS = 3 * 60_000;
|
|
474
|
+
// v0.28.25: provider-error retry cadence. Field-observed in dracon-utilities
|
|
475
|
+
// (kimi, 19-session fleet on one provider account): a "concurrent request
|
|
476
|
+
// limit" 403 storm got 5 retries BACK-TO-BACK (delay 0 after each errored
|
|
477
|
+
// turn — the session is idle at agent_end, so scheduleContinuation fired
|
|
478
|
+
// instantly) and the brake then cycled on a flat 60s cooldown for 1h 38m.
|
|
479
|
+
// The condition clears on a minutes-to-fleet scale, not milliseconds:
|
|
480
|
+
// ladder the inter-error retries (5s, 15s, 45s, 90s, 3m — the 5-retry
|
|
481
|
+
// budget now spans ~5.5m) and escalate the brake cooldown per consecutive
|
|
482
|
+
// brake (1m, 2m, 4m, 8m, 16m cap). A successful turn resets both.
|
|
483
|
+
const ERROR_RETRY_LADDER_MS = [5_000, 15_000, 45_000, 90_000, 180_000];
|
|
484
|
+
let errorBrakeStreak = 0;
|
|
474
485
|
const SEND_REARM_LEDGER_EVERY = 600; // 600 × 50ms = 30s
|
|
475
486
|
const SEND_REARM_ESCALATE_AT = 6000; // 6000 × 50ms = 5 minutes
|
|
476
487
|
|
|
@@ -592,6 +603,7 @@ function heartbeatTick(): void {
|
|
|
592
603
|
timerPending: continuationTimer !== null || loopTimer !== null,
|
|
593
604
|
msSinceActivity: Date.now() - lastActivityAt,
|
|
594
605
|
stallMs: HEARTBEAT_STALL_MS,
|
|
606
|
+
consecutiveStalls,
|
|
595
607
|
});
|
|
596
608
|
// Wedge alert (v0.23.2): session BUSY but silent for the threshold —
|
|
597
609
|
// the classic hung-command case (a test suite that never exits holds
|
|
@@ -688,7 +700,7 @@ function freshCtx(): ExtensionContext | null {
|
|
|
688
700
|
}
|
|
689
701
|
}
|
|
690
702
|
|
|
691
|
-
function scheduleContinuation(ctx: ExtensionContext, force = false): void {
|
|
703
|
+
function scheduleContinuation(ctx: ExtensionContext, force = false, delayMs?: number): void {
|
|
692
704
|
if (!isActionableGoal()) return;
|
|
693
705
|
rememberCtx(ctx);
|
|
694
706
|
const goalId = state.goal!.id;
|
|
@@ -696,7 +708,7 @@ function scheduleContinuation(ctx: ExtensionContext, force = false): void {
|
|
|
696
708
|
clearContinuationTimer();
|
|
697
709
|
let delay = 0;
|
|
698
710
|
try {
|
|
699
|
-
delay = ctx.isIdle() && !ctx.hasPendingMessages() ? 0 : BACKOFF_IDLE_RETRY_MS;
|
|
711
|
+
delay = delayMs ?? (ctx.isIdle() && !ctx.hasPendingMessages() ? 0 : BACKOFF_IDLE_RETRY_MS);
|
|
700
712
|
} catch {
|
|
701
713
|
return;
|
|
702
714
|
}
|
|
@@ -935,6 +947,146 @@ function archiveCurrentGoal(ctx: ExtensionContext, status: Status, stopReason?:
|
|
|
935
947
|
}
|
|
936
948
|
}
|
|
937
949
|
|
|
950
|
+
/**
|
|
951
|
+
* v0.28.26: quota-window retry for a STORED completion claim. The auditor
|
|
952
|
+
* was quota-blocked at complete_goal time; the claim (completionSummary +
|
|
953
|
+
* verificationSummary) was persisted on the goal, and when the quota window
|
|
954
|
+
* elapses we re-run the AUDITOR directly — no agent turn. Re-engaging the
|
|
955
|
+
* agent to re-submit an unchanged claim produced a hallucinated-closure
|
|
956
|
+
* repetition loop in the field (π-games: the model concluded the goal was
|
|
957
|
+
* closed, repeated the same essay 4×+, stormed continuations, compacted 14×
|
|
958
|
+
* in 35 minutes, and burned the stall brake).
|
|
959
|
+
*
|
|
960
|
+
* Outcomes: approved → close + cascade (archiveCurrentGoal handles list
|
|
961
|
+
* advance + reviewer); quota again → re-pause with a fresh scheduled retry
|
|
962
|
+
* (claim preserved); anything else (disapproved, impossible, non-quota
|
|
963
|
+
* infra) → hand back to the agent: resume active + continuation, verdict
|
|
964
|
+
* durable in auditHistory.
|
|
965
|
+
*/
|
|
966
|
+
async function retryStoredCompletionAudit(ctx: ExtensionContext): Promise<void> {
|
|
967
|
+
const goal = state.goal;
|
|
968
|
+
if (!goal?.pendingCompletion) return;
|
|
969
|
+
if (completionAuditInFlight) return;
|
|
970
|
+
const liveCtx = freshCtx() ?? ctx;
|
|
971
|
+
const claim = goal.pendingCompletion;
|
|
972
|
+
updateGoal({ status: "auditing" }, liveCtx);
|
|
973
|
+
appendLedger(liveCtx.cwd, "goal_resumed", { via: "quota-retry-direct-audit" });
|
|
974
|
+
liveCtx.ui.notify("Auditor quota window elapsed — retrying the audit with your stored completion claim (no agent turn needed).", "info");
|
|
975
|
+
const settings = loadSettings(liveCtx.cwd);
|
|
976
|
+
const { model: auditorModel, error: modelError, via } = resolveAuditorModel(liveCtx, settings.auditorModel);
|
|
977
|
+
if (modelError) liveCtx.ui.notify(`Auditor model issue: ${modelError}`, "warning");
|
|
978
|
+
latestAuditProgress = { label: "quota-retry", lastEventAt: Date.now() };
|
|
979
|
+
completionAuditInFlight = true;
|
|
980
|
+
const auditStartMs = Date.now();
|
|
981
|
+
let result: Awaited<ReturnType<typeof runGoalCompletionAuditor>>;
|
|
982
|
+
try {
|
|
983
|
+
({ result } = await runWithInfraRetry(
|
|
984
|
+
() =>
|
|
985
|
+
runGoalCompletionAuditor({
|
|
986
|
+
ctx: liveCtx,
|
|
987
|
+
goal: state.goal!,
|
|
988
|
+
completionSummary: claim.completionSummary,
|
|
989
|
+
verificationSummary: claim.verificationSummary,
|
|
990
|
+
model: auditorModel,
|
|
991
|
+
thinkingLevel: settings.auditorThinkingLevel ?? getSessionThinkingLevel(),
|
|
992
|
+
onProgress: (progress) => {
|
|
993
|
+
latestAuditProgress = { currentTool: progress.currentTool, label: progress.label, elapsedMs: progress.elapsedMs, lastEventAt: Date.now() };
|
|
994
|
+
refreshUI(liveCtx);
|
|
995
|
+
},
|
|
996
|
+
}),
|
|
997
|
+
{ onRetry: (err) => appendLedger(liveCtx.cwd, "audit_infra_retry", { goalId: state.goal?.id, error: err.slice(0, 200) }) },
|
|
998
|
+
));
|
|
999
|
+
} finally {
|
|
1000
|
+
completionAuditInFlight = false;
|
|
1001
|
+
latestAuditProgress = null;
|
|
1002
|
+
}
|
|
1003
|
+
if (!state.goal) return; // aborted mid-audit
|
|
1004
|
+
|
|
1005
|
+
// Record the run in history (same compact shape as the tool path).
|
|
1006
|
+
const auditorRan = result.output.trim().length > 0;
|
|
1007
|
+
const history = state.goal.auditHistory ?? [];
|
|
1008
|
+
if (auditorRan) {
|
|
1009
|
+
result.output = stripThinkBlocks(result.output);
|
|
1010
|
+
history.push({
|
|
1011
|
+
at: nowIso(),
|
|
1012
|
+
approved: result.approved,
|
|
1013
|
+
disapproved: result.disapproved,
|
|
1014
|
+
impossible: result.impossible,
|
|
1015
|
+
impossibleReason: result.impossibleReason,
|
|
1016
|
+
model: result.model,
|
|
1017
|
+
thinkingLevel: result.thinkingLevel,
|
|
1018
|
+
report: result.output,
|
|
1019
|
+
error: result.error,
|
|
1020
|
+
regressionShieldPassed: result.regressionShieldPassed,
|
|
1021
|
+
regressionShieldMissing: result.regressionShieldMissing,
|
|
1022
|
+
durationMs: Date.now() - auditStartMs,
|
|
1023
|
+
} as any);
|
|
1024
|
+
if (history.length > 20) history.splice(0, history.length - 20);
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
if (result.approved) {
|
|
1028
|
+
updateGoal({ auditHistory: history, pendingCompletion: undefined }, liveCtx);
|
|
1029
|
+
const objective = state.goal.objective;
|
|
1030
|
+
archiveCurrentGoal(liveCtx, "complete", `auditor ${result.model} approved (quota-retry)`);
|
|
1031
|
+
liveCtx.ui.notify(`Goal complete — auditor ${result.model} approved on the quota retry.`, "info");
|
|
1032
|
+
notifyExternal(liveCtx, `Goal complete (auditor approved on quota-retry): ${objective.slice(0, 120)}`);
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
if (result.error && !result.disapproved && isQuotaError(result.error)) {
|
|
1037
|
+
// Still quota'd — re-pause with a fresh window, claim preserved.
|
|
1038
|
+
const settingsNow = loadSettings(liveCtx.cwd);
|
|
1039
|
+
const defaultSec = (settingsNow.quotaRetryMinutes ?? DEFAULT_QUOTA_RETRY_MINUTES) * 60;
|
|
1040
|
+
const quota = parseQuotaError(result.error, defaultSec);
|
|
1041
|
+
const retryMin = Math.max(1, Math.round(quota.retryAfterSec / 60));
|
|
1042
|
+
updateGoal({
|
|
1043
|
+
status: "paused",
|
|
1044
|
+
auditHistory: history,
|
|
1045
|
+
pauseKind: "wait",
|
|
1046
|
+
pauseResumeAt: new Date(Date.now() + quota.retryAfterSec * 1000).toISOString(),
|
|
1047
|
+
pauseReason: `auditor quota: ${result.error}`,
|
|
1048
|
+
pauseSuggestedAction: `Quota auto-retry in ${retryMin}m — or /goal resume to retry now`,
|
|
1049
|
+
}, liveCtx);
|
|
1050
|
+
appendLedger(liveCtx.cwd, "goal_paused", { reason: `auditor quota: retry in ${quota.retryAfterSec}s (stored-claim retry)` });
|
|
1051
|
+
liveCtx.ui.notify(`Auditor still quota-limited — next auto-retry in ${retryMin}m (your completion claim is stored; no action needed).`, "warning");
|
|
1052
|
+
scheduleQuotaRetry(liveCtx, quota.retryAfterSec, result.error, () => {
|
|
1053
|
+
if (state.goal && state.goal.status === "paused" && (state.goal.pauseReason ?? "").startsWith("auditor quota:") && state.goal.pendingCompletion) {
|
|
1054
|
+
void retryStoredCompletionAudit(liveCtx);
|
|
1055
|
+
}
|
|
1056
|
+
});
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
// Any other outcome — disapproved, impossible, non-quota infra — belongs
|
|
1061
|
+
// to the agent: resume and let the continuation drive the next step. The
|
|
1062
|
+
// verdict is durable in auditHistory + /goal status.
|
|
1063
|
+
updateGoal({
|
|
1064
|
+
status: "active",
|
|
1065
|
+
auditHistory: history,
|
|
1066
|
+
pendingCompletion: undefined,
|
|
1067
|
+
pauseReason: result.disapproved
|
|
1068
|
+
? `auditor disapproved on quota-retry — see /goal status`
|
|
1069
|
+
: result.impossible
|
|
1070
|
+
? `auditor verdict: IMPOSSIBLE on quota-retry — ${(result.impossibleReason ?? "").slice(0, 120)}`
|
|
1071
|
+
: `auditor infrastructure error on quota-retry: ${(result.error ?? "").slice(0, 120)}`,
|
|
1072
|
+
}, liveCtx);
|
|
1073
|
+
liveCtx.ui.notify(
|
|
1074
|
+
result.disapproved
|
|
1075
|
+
? `Auditor (quota-retry) DISAPPROVED — resuming; the report is in /goal status.`
|
|
1076
|
+
: result.impossible
|
|
1077
|
+
? `Auditor (quota-retry): goal IMPOSSIBLE — ${(result.impossibleReason ?? "").slice(0, 100)}. Resuming; consider /goal tweak.`
|
|
1078
|
+
: `Auditor (quota-retry) hit an infrastructure error — resuming; re-call complete_goal when ready.`,
|
|
1079
|
+
"warning",
|
|
1080
|
+
);
|
|
1081
|
+
appendLedger(liveCtx.cwd, "quota_retry_audit_verdict", {
|
|
1082
|
+
approved: false,
|
|
1083
|
+
disapproved: result.disapproved,
|
|
1084
|
+
impossible: result.impossible,
|
|
1085
|
+
error: result.error?.slice(0, 160),
|
|
1086
|
+
});
|
|
1087
|
+
scheduleContinuation(liveCtx, true);
|
|
1088
|
+
}
|
|
1089
|
+
|
|
938
1090
|
/**
|
|
939
1091
|
* v0.26.0: bind the reviewer to the live session. Sources for finding
|
|
940
1092
|
* extraction: the archived goal markdown + its audit reports + the
|
|
@@ -2562,6 +2714,9 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
2562
2714
|
status: "paused",
|
|
2563
2715
|
auditHistory: history,
|
|
2564
2716
|
auditInfraStreak: undefined, // quota reached the auditor — infra streak broken
|
|
2717
|
+
// v0.28.26: store the claim — the quota retry re-runs the
|
|
2718
|
+
// auditor DIRECTLY with it (no agent turn to confuse).
|
|
2719
|
+
pendingCompletion: { completionSummary: p.completionSummary, verificationSummary: p.verificationSummary, at: nowIso() },
|
|
2565
2720
|
pauseKind: "wait",
|
|
2566
2721
|
pauseResumeAt: new Date(Date.now() + quota.retryAfterSec * 1000).toISOString(),
|
|
2567
2722
|
pauseReason: `auditor quota: ${result.error}`,
|
|
@@ -2572,6 +2727,13 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
2572
2727
|
// Re-check: only auto-resume if STILL paused for the quota
|
|
2573
2728
|
// reason (a user /goal pause during the window is not stomped).
|
|
2574
2729
|
if (state.goal && state.goal.status === "paused" && (state.goal.pauseReason ?? "").startsWith("auditor quota:")) {
|
|
2730
|
+
// v0.28.26: a stored claim retries the AUDITOR directly — the
|
|
2731
|
+
// agent is not needed to re-submit an unchanged claim, and
|
|
2732
|
+
// re-engaging it produced hallucinated-closure loops.
|
|
2733
|
+
if (state.goal.pendingCompletion) {
|
|
2734
|
+
void retryStoredCompletionAudit(ctx);
|
|
2735
|
+
return;
|
|
2736
|
+
}
|
|
2575
2737
|
updateGoal({ status: "active" }, ctx);
|
|
2576
2738
|
appendLedger(ctx.cwd, "goal_resumed", { via: "quota-retry" });
|
|
2577
2739
|
if (resolveEffectiveAggressiveSettings(loadSettings(ctx.cwd)).aggressiveMode) {
|
|
@@ -4821,33 +4983,46 @@ export default function (pi: ExtensionAPI): void {
|
|
|
4821
4983
|
if (consecutiveErrorIterations >= 5) {
|
|
4822
4984
|
// v0.28.5 (E8): carry the REAL error text — the pause used to say
|
|
4823
4985
|
// literally "5 consecutive errors: error" (stopReason, not the
|
|
4824
|
-
// provider error). And give transient flakes ONE
|
|
4825
|
-
// (
|
|
4986
|
+
// provider error). And give transient flakes ONE auto-resume per brake
|
|
4987
|
+
// (escalating cooldown, reason re-checked) — the E8 incident lost 1.5h to a
|
|
4826
4988
|
// 60-second provider hiccup waiting on a manual /goal resume.
|
|
4827
4989
|
const detail = text.trim() ? ` (last: ${text.trim().replace(/\s+/g, " ").slice(0, 160)})` : "";
|
|
4828
4990
|
const reason = `5 consecutive errors${detail}`;
|
|
4991
|
+
// v0.28.25: the cooldown escalates per CONSECUTIVE brake — a fleet-wide
|
|
4992
|
+
// 403 window is not cleared by re-braking every 60 seconds.
|
|
4993
|
+
const cooldownMs = 60_000 * 2 ** Math.min(errorBrakeStreak, 4);
|
|
4994
|
+
const cooldownMin = Math.round(cooldownMs / 60_000);
|
|
4995
|
+
errorBrakeStreak++;
|
|
4829
4996
|
updateGoal({
|
|
4830
4997
|
status: "paused",
|
|
4831
4998
|
pauseKind: "wait",
|
|
4832
|
-
pauseResumeAt: new Date(Date.now() +
|
|
4999
|
+
pauseResumeAt: new Date(Date.now() + cooldownMs).toISOString(),
|
|
4833
5000
|
pauseReason: reason,
|
|
4834
|
-
pauseSuggestedAction:
|
|
5001
|
+
pauseSuggestedAction: `Transient provider flake? The goal auto-resumes once in ${cooldownMin}m if still paused for this reason — or /goal resume now.`,
|
|
4835
5002
|
}, ctx);
|
|
4836
5003
|
ctx.ui.notify(`Goal paused: ${reason}.`, "warning");
|
|
4837
5004
|
notifyExternal(ctx, `Goal paused: ${reason}.`);
|
|
4838
5005
|
appendLedger(ctx.cwd, "goal_paused", { reason });
|
|
4839
|
-
scheduleQuotaRetry(ctx,
|
|
5006
|
+
scheduleQuotaRetry(ctx, cooldownMs / 1000, reason, () => {
|
|
4840
5007
|
// Re-check: only auto-resume if STILL paused for the error brake
|
|
4841
5008
|
// (a user /goal pause during the window is not stomped).
|
|
4842
5009
|
if (state.goal && state.goal.status === "paused" && (state.goal.pauseReason ?? "").startsWith("5 consecutive errors")) {
|
|
4843
5010
|
updateGoal({ status: "active" }, ctx);
|
|
4844
5011
|
appendLedger(ctx.cwd, "goal_resumed", { via: "error-brake-retry" });
|
|
4845
|
-
ctx.ui.notify("Auto-resumed after the 5-error brake (
|
|
5012
|
+
ctx.ui.notify("Auto-resumed after the 5-error brake (cooldown elapsed).", "info");
|
|
4846
5013
|
scheduleContinuation(ctx, true);
|
|
4847
5014
|
}
|
|
4848
5015
|
}, "5 consecutive errors — auto-retry");
|
|
4849
5016
|
return;
|
|
4850
5017
|
}
|
|
5018
|
+
// v0.28.25: under the brake, the retry rides the exponential ladder —
|
|
5019
|
+
// NOT the immediate scheduleContinuation at the bottom of this handler
|
|
5020
|
+
// (an errored turn leaves the session idle, so the default delay is 0:
|
|
5021
|
+
// exactly how 5 retries fired back-to-back in dracon-utilities).
|
|
5022
|
+
const retryDelayMs = ERROR_RETRY_LADDER_MS[Math.min(consecutiveErrorIterations - 1, ERROR_RETRY_LADDER_MS.length - 1)];
|
|
5023
|
+
appendLedger(ctx.cwd, "error_retry_backoff", { attempt: consecutiveErrorIterations, delayMs: retryDelayMs });
|
|
5024
|
+
scheduleContinuation(ctx, true, retryDelayMs);
|
|
5025
|
+
return;
|
|
4851
5026
|
} else if (stopReason === "aborted") {
|
|
4852
5027
|
// v0.28.5 (E8): user aborts are not provider errors. Separate brake,
|
|
4853
5028
|
// honest message, and NO auto-resume — aborting five turns in a row
|
|
@@ -4868,6 +5043,7 @@ export default function (pi: ExtensionAPI): void {
|
|
|
4868
5043
|
} else {
|
|
4869
5044
|
consecutiveErrorIterations = 0;
|
|
4870
5045
|
consecutiveAbortIterations = 0;
|
|
5046
|
+
errorBrakeStreak = 0; // v0.28.25: a healthy turn clears the brake cooldown
|
|
4871
5047
|
}
|
|
4872
5048
|
|
|
4873
5049
|
// No wall-clock cap by design: a goal ends via completion, explicit
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goal-list-loop-audit",
|
|
3
|
-
"version": "0.28.
|
|
3
|
+
"version": "0.28.26",
|
|
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",
|
package/schemas/goal.schema.json
CHANGED
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
"interruptedAt": { "type": "string" },
|
|
54
54
|
"interruptedReason": { "type": "string" },
|
|
55
55
|
"auditInfraStreak": { "type": "number" },
|
|
56
|
+
"pendingCompletion": { "type": "object" },
|
|
56
57
|
"activePath": { "type": "string" },
|
|
57
58
|
"archivedPath": { "type": "string" },
|
|
58
59
|
"usage": {
|