pi-goal-list-loop-audit 0.25.4 โ 0.25.5
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/goal-loop-core.ts +56 -0
- package/extensions/loops/goal.ts +54 -19
- package/package.json +1 -1
|
@@ -1090,6 +1090,10 @@ export interface AuditLogEntry {
|
|
|
1090
1090
|
report: string;
|
|
1091
1091
|
impossibleReason?: string;
|
|
1092
1092
|
error?: string;
|
|
1093
|
+
/** v0.25.4 post-audit: how long the audit took, and whether the infra
|
|
1094
|
+
* retry fired. */
|
|
1095
|
+
durationMs?: number;
|
|
1096
|
+
retriedOnce?: boolean;
|
|
1093
1097
|
}
|
|
1094
1098
|
|
|
1095
1099
|
export function auditLogPath(cwd: string): string {
|
|
@@ -1145,3 +1149,55 @@ export function formatAuditLog(entries: AuditLogEntry[]): string {
|
|
|
1145
1149
|
})
|
|
1146
1150
|
.join("\n");
|
|
1147
1151
|
}
|
|
1152
|
+
|
|
1153
|
+
// =================================================================
|
|
1154
|
+
// v0.25.4 (post-audit fix): infra-failure retry-once-with-backoff
|
|
1155
|
+
// =================================================================
|
|
1156
|
+
|
|
1157
|
+
/** Which auditor infra errors are worth an automatic retry? User aborts
|
|
1158
|
+
* and missing-model config are NOT โ retrying can't help them. */
|
|
1159
|
+
export function isRetriableInfraError(error?: string): boolean {
|
|
1160
|
+
if (!error) return false;
|
|
1161
|
+
if (/aborted/i.test(error)) return false;
|
|
1162
|
+
if (/no model/i.test(error)) return false;
|
|
1163
|
+
return true;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
export interface InfraRetryOutcome<T> {
|
|
1167
|
+
result: T;
|
|
1168
|
+
retriedOnce: boolean;
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
/** Run the auditor; on a retriable infra failure, wait `backoffMs` and
|
|
1172
|
+
* retry EXACTLY once before reporting "auditor infrastructure error
|
|
1173
|
+
* (retried once)". The failed pair is never a verdict on the work. */
|
|
1174
|
+
export async function runWithInfraRetry<T extends { error?: string; approved: boolean; disapproved: boolean }>(
|
|
1175
|
+
run: () => Promise<T>,
|
|
1176
|
+
opts: { backoffMs?: number; sleep?: (ms: number) => Promise<void>; onRetry?: (error: string) => void } = {},
|
|
1177
|
+
): Promise<InfraRetryOutcome<T>> {
|
|
1178
|
+
const sleep = opts.sleep ?? ((ms: number) => new Promise<void>((r) => setTimeout(r, ms)));
|
|
1179
|
+
const first = await run();
|
|
1180
|
+
if (first.approved || first.disapproved || !isRetriableInfraError(first.error)) {
|
|
1181
|
+
return { result: first, retriedOnce: false };
|
|
1182
|
+
}
|
|
1183
|
+
opts.onRetry?.(first.error!);
|
|
1184
|
+
await sleep(opts.backoffMs ?? 5000);
|
|
1185
|
+
const second = await run();
|
|
1186
|
+
return { result: second, retriedOnce: true };
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
/** /glla audits default view: the ACTIVE goal's own audit history (the
|
|
1190
|
+
* surface the goal spec asked for), one line per verdict. */
|
|
1191
|
+
export function formatGoalAuditHistory(goal: { id: string; auditHistory?: Array<any> }): string {
|
|
1192
|
+
const history = goal.auditHistory ?? [];
|
|
1193
|
+
if (history.length === 0) return "(no audits on this goal yet)";
|
|
1194
|
+
return history
|
|
1195
|
+
.map((v) => {
|
|
1196
|
+
const glyph = v.approved ? (v.regressionShieldPassed === false ? "๐ก" : "โ") : v.impossible ? "โ" : v.disapproved ? "โ" : "โ ";
|
|
1197
|
+
const day = String(v.at ?? "").slice(5, 16).replace("T", " ");
|
|
1198
|
+
const elapsed = v.durationMs ? ` ยท ${Math.round(v.durationMs / 60000)}m` : "";
|
|
1199
|
+
const firstLine = (String(v.report ?? "").split("\n").find((l: string) => l.trim()) ?? "").trim().slice(0, 80);
|
|
1200
|
+
return `${glyph} ${day} ${v.model ?? "?"}${elapsed} โ ${firstLine}`;
|
|
1201
|
+
})
|
|
1202
|
+
.join("\n");
|
|
1203
|
+
}
|
package/extensions/loops/goal.ts
CHANGED
|
@@ -42,6 +42,8 @@ import {
|
|
|
42
42
|
appendAuditLog,
|
|
43
43
|
computeListDepth,
|
|
44
44
|
formatAuditLog,
|
|
45
|
+
formatGoalAuditHistory,
|
|
46
|
+
runWithInfraRetry,
|
|
45
47
|
readAuditLog,
|
|
46
48
|
stripThinkBlocks,
|
|
47
49
|
type AuditLogEntry,
|
|
@@ -1703,24 +1705,38 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
1703
1705
|
// Esc during the audit aborts this tool's signal โ threaded into the
|
|
1704
1706
|
// auditor session, which aborts cleanly and returns "Auditor aborted."
|
|
1705
1707
|
latestAuditProgress = { label: "starting", lastEventAt: Date.now() };
|
|
1706
|
-
const
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1708
|
+
const runAudit = () =>
|
|
1709
|
+
runGoalCompletionAuditor({
|
|
1710
|
+
ctx,
|
|
1711
|
+
goal: state.goal!,
|
|
1712
|
+
completionSummary: p.completionSummary,
|
|
1713
|
+
verificationSummary: p.verificationSummary,
|
|
1714
|
+
model: auditorModel,
|
|
1715
|
+
thinkingLevel: settings.auditorThinkingLevel ?? getSessionThinkingLevel(),
|
|
1716
|
+
signal: signal ?? undefined,
|
|
1717
|
+
onProgress: (progress) => {
|
|
1718
|
+
latestAuditProgress = {
|
|
1719
|
+
currentTool: progress.currentTool,
|
|
1720
|
+
label: progress.label,
|
|
1721
|
+
elapsedMs: progress.elapsedMs,
|
|
1722
|
+
lastEventAt: Date.now(),
|
|
1723
|
+
};
|
|
1724
|
+
refreshUI(ctx);
|
|
1725
|
+
},
|
|
1726
|
+
});
|
|
1727
|
+
// v0.25.4 (post-audit fix): a retriable infra failure (stream error,
|
|
1728
|
+
// auth blip โ NOT user abort, NOT missing model) gets ONE automatic
|
|
1729
|
+
// retry with backoff before we report "auditor infrastructure error
|
|
1730
|
+
// (retried once)". Neither attempt is a verdict on the work.
|
|
1731
|
+
const auditStartMs = Date.now();
|
|
1732
|
+
const { result, retriedOnce } = await runWithInfraRetry(runAudit, {
|
|
1733
|
+
onRetry: (err) => {
|
|
1734
|
+
latestAuditProgress = { label: `infra error (${err.slice(0, 40)}) โ retrying once`, lastEventAt: Date.now() };
|
|
1721
1735
|
refreshUI(ctx);
|
|
1736
|
+
appendLedger(ctx.cwd, "audit_infra_retry", { goalId: state.goal?.id, error: err.slice(0, 200) });
|
|
1722
1737
|
},
|
|
1723
1738
|
});
|
|
1739
|
+
const auditDurationMs = Date.now() - auditStartMs;
|
|
1724
1740
|
latestAuditProgress = null;
|
|
1725
1741
|
// Audit history: record REAL verdicts only โ a non-empty report is the
|
|
1726
1742
|
// evidence the auditor actually inspected something. Empty-report runs
|
|
@@ -1746,7 +1762,8 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
1746
1762
|
error: result.error,
|
|
1747
1763
|
regressionShieldPassed: result.regressionShieldPassed,
|
|
1748
1764
|
regressionShieldMissing: result.regressionShieldMissing,
|
|
1749
|
-
|
|
1765
|
+
durationMs: auditDurationMs,
|
|
1766
|
+
} as any);
|
|
1750
1767
|
// Cap history โ 39 infra errors taught us unbounded growth is real.
|
|
1751
1768
|
if (history.length > 20) history.splice(0, history.length - 20);
|
|
1752
1769
|
// v0.25.4: durable append-only audit log โ survives state-snapshot
|
|
@@ -1771,7 +1788,9 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
1771
1788
|
report: cleanOutput,
|
|
1772
1789
|
impossibleReason: result.impossibleReason,
|
|
1773
1790
|
error: result.error,
|
|
1774
|
-
|
|
1791
|
+
durationMs: auditDurationMs,
|
|
1792
|
+
retriedOnce,
|
|
1793
|
+
} as AuditLogEntry);
|
|
1775
1794
|
}
|
|
1776
1795
|
|
|
1777
1796
|
// Escape hatch: the user aborted the audit (Esc). Offer the explicit
|
|
@@ -1897,14 +1916,14 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
1897
1916
|
updateGoal({
|
|
1898
1917
|
status: "active",
|
|
1899
1918
|
auditHistory: history,
|
|
1900
|
-
pauseReason: `auditor infrastructure: ${result.error}`,
|
|
1919
|
+
pauseReason: `auditor infrastructure${retriedOnce ? " (retried once)" : ""}: ${result.error}`,
|
|
1901
1920
|
pauseSuggestedAction: "Fix the auditor model (/glla model=provider/id) and call complete_goal again โ your work was NOT judged",
|
|
1902
1921
|
}, ctx);
|
|
1903
1922
|
scheduleContinuation(ctx, true);
|
|
1904
1923
|
return {
|
|
1905
1924
|
content: [{
|
|
1906
1925
|
type: "text",
|
|
1907
|
-
text: `The auditor could not run (infrastructure, NOT a verdict): ${result.error}\nYour completion claim was not evaluated. Fix the auditor model with /glla model=provider/id and call complete_goal again โ do not change your deliverable for this.`,
|
|
1926
|
+
text: `The auditor could not run (infrastructure, NOT a verdict${retriedOnce ? "; retried once with backoff, both attempts failed" : ""}): ${result.error}\nYour completion claim was not evaluated. Fix the auditor model with /glla model=provider/id and call complete_goal again โ do not change your deliverable for this.`,
|
|
1908
1927
|
}],
|
|
1909
1928
|
details: {},
|
|
1910
1929
|
};
|
|
@@ -2785,12 +2804,28 @@ function cmdStats(args: string, ctx: ExtensionContext): void {
|
|
|
2785
2804
|
*/
|
|
2786
2805
|
function cmdAudits(args: string, ctx: ExtensionContext): void {
|
|
2787
2806
|
const full = /\bfull\b/.test(args);
|
|
2807
|
+
const all = /\b(?:all|global|log)\b/.test(args);
|
|
2788
2808
|
const nMatch = args.match(/\b(\d+)\b/);
|
|
2789
2809
|
if (full) {
|
|
2810
|
+
// Latest report โ active goal's history first, then the log.
|
|
2811
|
+
const fromGoal = state.goal?.auditHistory?.at(-1);
|
|
2812
|
+
if (fromGoal?.report) {
|
|
2813
|
+
ctx.ui.notify(`Latest audit on this goal โ ${fromGoal.model} (${fromGoal.at})\n${fromGoal.report}`, "info");
|
|
2814
|
+
return;
|
|
2815
|
+
}
|
|
2790
2816
|
const latest = readAuditLog(ctx.cwd).at(-1);
|
|
2791
2817
|
ctx.ui.notify(latest ? `Latest audit โ ${latest.verdict} (${latest.model}, ${latest.at})\n${latest.report}` : "No audits logged yet.", "info");
|
|
2792
2818
|
return;
|
|
2793
2819
|
}
|
|
2820
|
+
// Default: the ACTIVE goal's own audit history (with per-audit elapsed);
|
|
2821
|
+
// "all"/"global"/"log" browses the durable cross-goal log.
|
|
2822
|
+
if (!all && state.goal?.auditHistory && state.goal.auditHistory.length > 0) {
|
|
2823
|
+
ctx.ui.notify(
|
|
2824
|
+
`glla audits โ this goal's history (${state.goal.auditHistory.length} verdict(s); /glla audits all for the project log)\n${formatGoalAuditHistory(state.goal)}`,
|
|
2825
|
+
"info",
|
|
2826
|
+
);
|
|
2827
|
+
return;
|
|
2828
|
+
}
|
|
2794
2829
|
const n = nMatch ? Number(nMatch[1]) : 10;
|
|
2795
2830
|
const entries = readAuditLog(ctx.cwd, n);
|
|
2796
2831
|
ctx.ui.notify(`glla audits โ last ${entries.length} verdict(s) in ${ctx.cwd}\n${formatAuditLog(entries)}`, "info");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goal-list-loop-audit",
|
|
3
|
-
"version": "0.25.
|
|
3
|
+
"version": "0.25.5",
|
|
4
4
|
"description": "Goal. Loop. Audit. Done. โ 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 โ only the read tools needed to verify your goal.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "dracon",
|