pi-goal-list-loop-audit 0.25.3 → 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.
|
@@ -159,7 +159,9 @@ function buildGoalAuditorPrompt(goal: Goal, completionSummary: string | null | u
|
|
|
159
159
|
? ["4. Verify that the executor has satisfied every item in the <verification_contract>. If any item is missing or weakly addressed, disapprove."]
|
|
160
160
|
: []),
|
|
161
161
|
"5. Explain missing or weak evidence, especially scaffold-vs-final quality gaps.",
|
|
162
|
-
"6.
|
|
162
|
+
"6. When you disapprove, end the report body with a '## Required fixes' section: one line per blocking gap, each an actionable instruction the executor can complete (most critical first). This tail is what the executor sees first — make it self-sufficient.",
|
|
163
|
+
"7. Write the report in English, and never emit <think> blocks or fragments — your reasoning stays private; the report is the verdict plus evidence.",
|
|
164
|
+
"8. End with exactly <approved/> only if the objective is truly complete; <impossible>reason</impossible> if it can never be satisfied as stated; otherwise end with exactly <disapproved/>.",
|
|
163
165
|
...(goal.verificationContract?.trim()
|
|
164
166
|
? [
|
|
165
167
|
"",
|
|
@@ -309,8 +309,14 @@ export const DEFAULT_AUDIT_FEEDBACK_CHARS = 0;
|
|
|
309
309
|
* Bound the auditor report returned to the executor after disapproval.
|
|
310
310
|
* A limit of 0 explicitly means "show the full report".
|
|
311
311
|
*/
|
|
312
|
+
/** Executor-visible excerpt of a disapproval report. Full by default
|
|
313
|
+
* (maxChars 0). When capped, keep the TAIL: since v0.25.4 the auditor
|
|
314
|
+
* ends disapprovals with the actionable `## Required fixes` section —
|
|
315
|
+
* head-slicing would cut exactly what the executor needs. */
|
|
312
316
|
export function auditFeedbackExcerpt(output: string, maxChars: number): string {
|
|
313
|
-
|
|
317
|
+
if (maxChars === 0 || output.length <= maxChars) return output;
|
|
318
|
+
return `[head truncated — full report via /goal status]
|
|
319
|
+
…${output.slice(-maxChars)}`;
|
|
314
320
|
}
|
|
315
321
|
|
|
316
322
|
export interface ListItem {
|
|
@@ -400,10 +406,19 @@ export interface State {
|
|
|
400
406
|
/** v0.24.2: count TRAILING consecutive disapprovals (the disapproval-cap
|
|
401
407
|
* input). Shield-blocks (approved:true) and infra errors (neither flag)
|
|
402
408
|
* break the streak — they are not verdicts on the work. */
|
|
409
|
+
/** v0.24.2: count TRAILING consecutive disapprovals (the disapproval-cap
|
|
410
|
+
* input). Shield-blocks (approved:true) break the streak — the work was
|
|
411
|
+
* judged good. v0.25.4: pure infra errors (error set, neither verdict
|
|
412
|
+
* flag) are TRANSPARENT, not streak-breakers — the auditor never judged
|
|
413
|
+
* the work, so D,D,infra,D is still 3 trailing disapprovals (before, 39
|
|
414
|
+
* hegemon-style infra errors would reset the cap and re-open infinite
|
|
415
|
+
* re-continuation). */
|
|
403
416
|
export function countTrailingDisapprovals(history: AuditVerdict[]): number {
|
|
404
417
|
let n = 0;
|
|
405
418
|
for (let i = history.length - 1; i >= 0; i--) {
|
|
406
|
-
|
|
419
|
+
const v = history[i]!;
|
|
420
|
+
if (v.disapproved) n++;
|
|
421
|
+
else if (v.error && !v.approved) continue; // infra: not a verdict
|
|
407
422
|
else break;
|
|
408
423
|
}
|
|
409
424
|
return n;
|
|
@@ -1044,3 +1059,145 @@ export function formatListDepth(stats: ListDepthStats): string {
|
|
|
1044
1059
|
if (stats.durationSamples > 0) lines.push(`avg item duration: ${fmtAge(stats.avgDurationMs!)} (from last ${stats.durationSamples} archived)`);
|
|
1045
1060
|
return lines.join("\n");
|
|
1046
1061
|
}
|
|
1062
|
+
|
|
1063
|
+
// =================================================================
|
|
1064
|
+
// v0.25.4: auditor polish — durable audit log, think-block hygiene,
|
|
1065
|
+
// actionable-tail slicing, infra-transparent streaks
|
|
1066
|
+
// =================================================================
|
|
1067
|
+
|
|
1068
|
+
/** Strip think-block leakage from auditor reports before storage/display.
|
|
1069
|
+
* Motivation (wild, 2026-07-25): MiniMax-M3 reports arrive with
|
|
1070
|
+
* `<think>...</think>` bodies, stray `</think>` fragments, and non-English
|
|
1071
|
+
* reasoning spillover — the executor's feedback should be the verdict,
|
|
1072
|
+
* not the auditor's private monologue. */
|
|
1073
|
+
export function stripThinkBlocks(text: string): string {
|
|
1074
|
+
return text
|
|
1075
|
+
.replace(/<think>[\s\S]*?<\/think>/gi, "")
|
|
1076
|
+
.replace(/<\/?think>/gi, "")
|
|
1077
|
+
.replace(/<200b>/g, "") // stray partial-tag artifact seen in the wild
|
|
1078
|
+
.replace(/^\s+/, "");
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
/** One durable audit-log entry — survives state-snapshot rotation, so
|
|
1082
|
+
* /glla audits can answer "where are we weak" across the whole project. */
|
|
1083
|
+
export interface AuditLogEntry {
|
|
1084
|
+
at: string;
|
|
1085
|
+
goalId: string;
|
|
1086
|
+
objective: string;
|
|
1087
|
+
verdict: "approved" | "disapproved" | "impossible" | "shield_blocked" | "error";
|
|
1088
|
+
model: string;
|
|
1089
|
+
thinkingLevel: string;
|
|
1090
|
+
report: string;
|
|
1091
|
+
impossibleReason?: string;
|
|
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;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
export function auditLogPath(cwd: string): string {
|
|
1100
|
+
return path.join(cwd, ".pi-glla", "audits.jsonl");
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
export function appendAuditLog(cwd: string, entry: AuditLogEntry): void {
|
|
1104
|
+
try {
|
|
1105
|
+
ensureDirs(cwd);
|
|
1106
|
+
fs.appendFileSync(auditLogPath(cwd), JSON.stringify(entry) + "\n");
|
|
1107
|
+
} catch {
|
|
1108
|
+
/* log best-effort — never block the verdict path */
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
export function readAuditLog(cwd: string, limit?: number): AuditLogEntry[] {
|
|
1113
|
+
let raw: string;
|
|
1114
|
+
try {
|
|
1115
|
+
raw = fs.readFileSync(auditLogPath(cwd), "utf-8");
|
|
1116
|
+
} catch {
|
|
1117
|
+
return [];
|
|
1118
|
+
}
|
|
1119
|
+
const out: AuditLogEntry[] = [];
|
|
1120
|
+
for (const line of raw.split("\n")) {
|
|
1121
|
+
const t = line.trim();
|
|
1122
|
+
if (!t) continue;
|
|
1123
|
+
try {
|
|
1124
|
+
const e = JSON.parse(t);
|
|
1125
|
+
if (e && typeof e.goalId === "string" && typeof e.verdict === "string") out.push(e as AuditLogEntry);
|
|
1126
|
+
} catch {
|
|
1127
|
+
/* skip malformed */
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
return limit !== undefined ? out.slice(-limit) : out;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
const VERDICT_GLYPH: Record<AuditLogEntry["verdict"], string> = {
|
|
1134
|
+
approved: "✔",
|
|
1135
|
+
disapproved: "✖",
|
|
1136
|
+
impossible: "⛔",
|
|
1137
|
+
shield_blocked: "🛡",
|
|
1138
|
+
error: "⚠",
|
|
1139
|
+
};
|
|
1140
|
+
|
|
1141
|
+
/** /glla audits list view: one line per verdict, newest last. */
|
|
1142
|
+
export function formatAuditLog(entries: AuditLogEntry[]): string {
|
|
1143
|
+
if (entries.length === 0) return "(no audits logged yet — the log starts with the next verdict)";
|
|
1144
|
+
return entries
|
|
1145
|
+
.map((e) => {
|
|
1146
|
+
const day = e.at.slice(5, 16).replace("T", " ");
|
|
1147
|
+
const firstLine = (e.report.split("\n").find((l) => l.trim()) ?? "").trim().slice(0, 90);
|
|
1148
|
+
return `${VERDICT_GLYPH[e.verdict]} ${day} [${e.goalId.slice(-6)}] ${e.model} — ${firstLine}`;
|
|
1149
|
+
})
|
|
1150
|
+
.join("\n");
|
|
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
|
+
}
|
|
@@ -72,6 +72,9 @@ export interface AuditDisplayProgress {
|
|
|
72
72
|
currentTool?: string;
|
|
73
73
|
label?: string;
|
|
74
74
|
elapsedMs?: number;
|
|
75
|
+
/** v0.25.4: last progress-event time — the widget flags auditor-quiet
|
|
76
|
+
* stalls when this goes stale while the audit is in flight. */
|
|
77
|
+
lastEventAt?: number;
|
|
75
78
|
}
|
|
76
79
|
|
|
77
80
|
/**
|
|
@@ -175,7 +178,12 @@ function goalLines(g: Goal, state: State, audit: AuditDisplayProgress | null | u
|
|
|
175
178
|
const lines = [head, `├─ ${isList ? "list item · " : ""}${statusWord} · ${fmtElapsed(now - Date.parse(g.createdAt))}${tokens}`];
|
|
176
179
|
if (g.status === "auditing") {
|
|
177
180
|
lines.push(`├─ auditor: ${audit?.label ?? "running"}${audit?.currentTool ? ` · ${truncate(audit.currentTool, 30)}` : ""}`);
|
|
178
|
-
|
|
181
|
+
// v0.25.4: auditor-quiet stall — progress events stopped arriving
|
|
182
|
+
// while the audit is in flight (hung model call, stuck tool).
|
|
183
|
+
const quietMs = audit?.lastEventAt !== undefined ? now - audit.lastEventAt : 0;
|
|
184
|
+
if (quietMs > 3 * 60_000) {
|
|
185
|
+
lines.push(`└─ ${paint(theme, "warning", `auditor quiet ${fmtElapsed(quietMs)} — may be stuck; Esc aborts, verdict is not counted`)}`);
|
|
186
|
+
} else if (audit?.elapsedMs) lines.push(`└─ ${paint(theme, "dim", `${fmtElapsed(audit.elapsedMs)} in isolated session`)}`);
|
|
179
187
|
else lines.push(`└─ ${paint(theme, "dim", "isolated session, read-only tools")}`);
|
|
180
188
|
return lines;
|
|
181
189
|
}
|
package/extensions/loops/goal.ts
CHANGED
|
@@ -39,7 +39,14 @@ import {
|
|
|
39
39
|
isFullAuditObjective,
|
|
40
40
|
lastShippedAtMs,
|
|
41
41
|
resolveEffectiveAggressiveSettings,
|
|
42
|
+
appendAuditLog,
|
|
42
43
|
computeListDepth,
|
|
44
|
+
formatAuditLog,
|
|
45
|
+
formatGoalAuditHistory,
|
|
46
|
+
runWithInfraRetry,
|
|
47
|
+
readAuditLog,
|
|
48
|
+
stripThinkBlocks,
|
|
49
|
+
type AuditLogEntry,
|
|
43
50
|
ledgerPath,
|
|
44
51
|
crossRecommendMode,
|
|
45
52
|
formatListDepth,
|
|
@@ -1697,24 +1704,39 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
1697
1704
|
ctx.ui.notify(`Auditor running (isolated session, model: ${via ?? "setting"})…`, "info");
|
|
1698
1705
|
// Esc during the audit aborts this tool's signal → threaded into the
|
|
1699
1706
|
// auditor session, which aborts cleanly and returns "Auditor aborted."
|
|
1700
|
-
latestAuditProgress = { label: "starting" };
|
|
1701
|
-
const
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1707
|
+
latestAuditProgress = { label: "starting", lastEventAt: Date.now() };
|
|
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() };
|
|
1715
1735
|
refreshUI(ctx);
|
|
1736
|
+
appendLedger(ctx.cwd, "audit_infra_retry", { goalId: state.goal?.id, error: err.slice(0, 200) });
|
|
1716
1737
|
},
|
|
1717
1738
|
});
|
|
1739
|
+
const auditDurationMs = Date.now() - auditStartMs;
|
|
1718
1740
|
latestAuditProgress = null;
|
|
1719
1741
|
// Audit history: record REAL verdicts only — a non-empty report is the
|
|
1720
1742
|
// evidence the auditor actually inspected something. Empty-report runs
|
|
@@ -1723,6 +1745,11 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
1723
1745
|
const auditorRan = result.output.trim().length > 0;
|
|
1724
1746
|
const history = state.goal.auditHistory ?? [];
|
|
1725
1747
|
if (auditorRan) {
|
|
1748
|
+
// v0.25.4: strip think-block leakage (MiniMax-M3 `</think>`
|
|
1749
|
+
// fragments + reasoning spillover) before anything stores or
|
|
1750
|
+
// displays the report.
|
|
1751
|
+
const cleanOutput = stripThinkBlocks(result.output);
|
|
1752
|
+
result.output = cleanOutput;
|
|
1726
1753
|
history.push({
|
|
1727
1754
|
at: nowIso(),
|
|
1728
1755
|
approved: result.approved,
|
|
@@ -1731,13 +1758,39 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
1731
1758
|
impossibleReason: result.impossibleReason,
|
|
1732
1759
|
model: result.model,
|
|
1733
1760
|
thinkingLevel: result.thinkingLevel,
|
|
1734
|
-
report:
|
|
1761
|
+
report: cleanOutput,
|
|
1735
1762
|
error: result.error,
|
|
1736
1763
|
regressionShieldPassed: result.regressionShieldPassed,
|
|
1737
1764
|
regressionShieldMissing: result.regressionShieldMissing,
|
|
1738
|
-
|
|
1765
|
+
durationMs: auditDurationMs,
|
|
1766
|
+
} as any);
|
|
1739
1767
|
// Cap history — 39 infra errors taught us unbounded growth is real.
|
|
1740
1768
|
if (history.length > 20) history.splice(0, history.length - 20);
|
|
1769
|
+
// v0.25.4: durable append-only audit log — survives state-snapshot
|
|
1770
|
+
// rotation; the review surface for "where are we weak".
|
|
1771
|
+
const verdict: AuditLogEntry["verdict"] =
|
|
1772
|
+
result.error && !result.approved && !result.disapproved
|
|
1773
|
+
? "error"
|
|
1774
|
+
: result.approved && result.regressionShieldPassed === false
|
|
1775
|
+
? "shield_blocked"
|
|
1776
|
+
: result.approved
|
|
1777
|
+
? "approved"
|
|
1778
|
+
: result.impossible
|
|
1779
|
+
? "impossible"
|
|
1780
|
+
: "disapproved";
|
|
1781
|
+
appendAuditLog(ctx.cwd, {
|
|
1782
|
+
at: nowIso(),
|
|
1783
|
+
goalId: state.goal.id,
|
|
1784
|
+
objective: state.goal.objective.slice(0, 200),
|
|
1785
|
+
verdict,
|
|
1786
|
+
model: result.model,
|
|
1787
|
+
thinkingLevel: result.thinkingLevel ?? "(default)",
|
|
1788
|
+
report: cleanOutput,
|
|
1789
|
+
impossibleReason: result.impossibleReason,
|
|
1790
|
+
error: result.error,
|
|
1791
|
+
durationMs: auditDurationMs,
|
|
1792
|
+
retriedOnce,
|
|
1793
|
+
} as AuditLogEntry);
|
|
1741
1794
|
}
|
|
1742
1795
|
|
|
1743
1796
|
// Escape hatch: the user aborted the audit (Esc). Offer the explicit
|
|
@@ -1863,14 +1916,14 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
1863
1916
|
updateGoal({
|
|
1864
1917
|
status: "active",
|
|
1865
1918
|
auditHistory: history,
|
|
1866
|
-
pauseReason: `auditor infrastructure: ${result.error}`,
|
|
1919
|
+
pauseReason: `auditor infrastructure${retriedOnce ? " (retried once)" : ""}: ${result.error}`,
|
|
1867
1920
|
pauseSuggestedAction: "Fix the auditor model (/glla model=provider/id) and call complete_goal again — your work was NOT judged",
|
|
1868
1921
|
}, ctx);
|
|
1869
1922
|
scheduleContinuation(ctx, true);
|
|
1870
1923
|
return {
|
|
1871
1924
|
content: [{
|
|
1872
1925
|
type: "text",
|
|
1873
|
-
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.`,
|
|
1874
1927
|
}],
|
|
1875
1928
|
details: {},
|
|
1876
1929
|
};
|
|
@@ -1916,7 +1969,7 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
1916
1969
|
const auditFeedbackIsFull = auditFeedbackChars === 0 || result.output.length <= auditFeedbackChars;
|
|
1917
1970
|
const auditFeedbackLabel = auditFeedbackIsFull
|
|
1918
1971
|
? "full report"
|
|
1919
|
-
: `
|
|
1972
|
+
: `last ${auditFeedbackChars} chars (Required-fixes tail)`;
|
|
1920
1973
|
const auditFeedbackTruncationHint = auditFeedbackIsFull
|
|
1921
1974
|
? ""
|
|
1922
1975
|
: `\n\nReport truncated at the configured limit. /goal status shows the full report; change future feedback with /glla auditfeedbackchars=N (0 = full report).`;
|
|
@@ -2744,6 +2797,40 @@ function cmdStats(args: string, ctx: ExtensionContext): void {
|
|
|
2744
2797
|
ctx.ui.notify(`glla stats — ${rollups.length} project(s)${prematureOnly ? " (premature filter)" : ""}\n${out}`, "info");
|
|
2745
2798
|
}
|
|
2746
2799
|
|
|
2800
|
+
/**
|
|
2801
|
+
* v0.25.4: /glla audits [N|full] — browse the durable per-project audit
|
|
2802
|
+
* log (.pi-glla/audits.jsonl). Default: last 10 verdicts, one line each.
|
|
2803
|
+
* "full" prints the latest report in full.
|
|
2804
|
+
*/
|
|
2805
|
+
function cmdAudits(args: string, ctx: ExtensionContext): void {
|
|
2806
|
+
const full = /\bfull\b/.test(args);
|
|
2807
|
+
const all = /\b(?:all|global|log)\b/.test(args);
|
|
2808
|
+
const nMatch = args.match(/\b(\d+)\b/);
|
|
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
|
+
}
|
|
2816
|
+
const latest = readAuditLog(ctx.cwd).at(-1);
|
|
2817
|
+
ctx.ui.notify(latest ? `Latest audit — ${latest.verdict} (${latest.model}, ${latest.at})\n${latest.report}` : "No audits logged yet.", "info");
|
|
2818
|
+
return;
|
|
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
|
+
}
|
|
2829
|
+
const n = nMatch ? Number(nMatch[1]) : 10;
|
|
2830
|
+
const entries = readAuditLog(ctx.cwd, n);
|
|
2831
|
+
ctx.ui.notify(`glla audits — last ${entries.length} verdict(s) in ${ctx.cwd}\n${formatAuditLog(entries)}`, "info");
|
|
2832
|
+
}
|
|
2833
|
+
|
|
2747
2834
|
async function cmdSettings(args: string, ctx: ExtensionContext): Promise<void> {
|
|
2748
2835
|
// The plugin's ONE config surface — global by default, rarely opened.
|
|
2749
2836
|
// /glla show effective values + where each comes from
|
|
@@ -2762,6 +2849,10 @@ async function cmdSettings(args: string, ctx: ExtensionContext): Promise<void> {
|
|
|
2762
2849
|
cmdStats(trimmed.slice("stats".length).trim(), ctx);
|
|
2763
2850
|
return;
|
|
2764
2851
|
}
|
|
2852
|
+
if (/^audits\b/.test(trimmed)) {
|
|
2853
|
+
cmdAudits(trimmed.slice("audits".length).trim(), ctx);
|
|
2854
|
+
return;
|
|
2855
|
+
}
|
|
2765
2856
|
if (!trimmed) {
|
|
2766
2857
|
if (ctx.hasUI) {
|
|
2767
2858
|
await openSettingsUI(ctx);
|
|
@@ -3059,6 +3150,7 @@ export default function (pi: ExtensionAPI): void {
|
|
|
3059
3150
|
["quotaretryminutes=", "N: minutes before auto-retrying a quota-exhausted auditor (default 60)"],
|
|
3060
3151
|
["stuckmax=", "N: consecutive stuck interventions before a loop stops (default 5)"],
|
|
3061
3152
|
["stats", "per-project ledger rollups: /glla stats [json|premature|project=<path>]"],
|
|
3153
|
+
["audits", "audit-log browser: /glla audits [N|full] — recent verdicts from .pi-glla/audits.jsonl"],
|
|
3062
3154
|
["autoaccept=", "on: drafts activate without the Confirm dialog (unattended rigs)"],
|
|
3063
3155
|
["project", "write a project override: /glla project key=value"],
|
|
3064
3156
|
]),
|
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",
|