pi-goal-list-loop-audit 0.24.7 → 0.24.8

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/README.md CHANGED
@@ -201,6 +201,7 @@ No external watchdog plugin needed.
201
201
  /glla wedgealert=30 # hung-command alert minutes (default: 30, 0 = off)
202
202
  /glla autoresume=on # held goals/loops auto-resume in fresh sessions (unattended rigs)
203
203
  /glla auditcap=5 # pause the goal after N consecutive auditor disapprovals (default 3, 0 = unlimited)
204
+ /glla auditfeedbackchars=16000 # auditor report returned to the executor (default 800, 0 = full report)
204
205
  /glla autoaccept=on # drafts ACTIVATE without the Confirm dialog (unattended rigs)
205
206
  /glla project tokenlimit=500 # rare per-project override
206
207
  ```
@@ -211,6 +212,13 @@ auditor can't auth it — you're told once (info level) with the fix:
211
212
  `/glla model=provider/id`, set once, rarely touched again. The plugin never
212
213
  picks a model itself. Thinking follows the session too (floor `high`).
213
214
 
215
+ On disapproval, the executor receives up to `auditFeedbackChars` characters
216
+ from the auditor report (default 800, preserving the previous behavior).
217
+ Increase it for multi-item `<evidence>` blocks and raw verification output,
218
+ or set `/glla auditfeedbackchars=0` to return the full report. The complete
219
+ report continues to be stored in audit history and is available through
220
+ `/goal status`.
221
+
214
222
  `autoaccept=on` skips BOTH the Confirm dialog and the drafting interview
215
223
  floor — every `propose_*` draft (goal, list batch, loop, task list)
216
224
  activates the moment the agent proposes it, with a notification and a
@@ -320,4 +328,4 @@ pi install .
320
328
 
321
329
  ## License
322
330
 
323
- MIT
331
+ MIT
@@ -287,6 +287,17 @@ export function mergeSettings<T extends Record<string, unknown>>(base: T, ...lay
287
287
  return out as T;
288
288
  }
289
289
 
290
+ /** Backward-compatible default for executor-visible auditor feedback. */
291
+ export const DEFAULT_AUDIT_FEEDBACK_CHARS = 800;
292
+
293
+ /**
294
+ * Bound the auditor report returned to the executor after disapproval.
295
+ * A limit of 0 explicitly means "show the full report".
296
+ */
297
+ export function auditFeedbackExcerpt(output: string, maxChars: number): string {
298
+ return maxChars === 0 ? output : output.slice(0, maxChars);
299
+ }
300
+
290
301
  export interface ListItem {
291
302
  id: string;
292
303
  objective: string;
@@ -30,6 +30,8 @@ import {
30
30
  archivedGoalPath,
31
31
  buildTaskList,
32
32
  buildTaskSummary,
33
+ auditFeedbackExcerpt,
34
+ DEFAULT_AUDIT_FEEDBACK_CHARS,
33
35
  DEFAULT_TOKEN_LIMIT,
34
36
  mergeSettings,
35
37
  parseListImport,
@@ -1666,7 +1668,19 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
1666
1668
  // goal the auditor can NEVER approve used to re-continue forever.
1667
1669
  // auditCap consecutive disapprovals → pause + notify, bounded and
1668
1670
  // surfaced like every other stop in this stack.
1669
- const auditCap = loadSettings(ctx.cwd).auditCap ?? 3;
1671
+ const auditCap = settings.auditCap ?? 3;
1672
+ const configuredFeedbackChars = settings.auditFeedbackChars;
1673
+ const auditFeedbackChars = Number.isInteger(configuredFeedbackChars) && configuredFeedbackChars! >= 0
1674
+ ? configuredFeedbackChars!
1675
+ : DEFAULT_AUDIT_FEEDBACK_CHARS;
1676
+ const auditFeedback = auditFeedbackExcerpt(result.output, auditFeedbackChars);
1677
+ const auditFeedbackIsFull = auditFeedbackChars === 0 || result.output.length <= auditFeedbackChars;
1678
+ const auditFeedbackLabel = auditFeedbackIsFull
1679
+ ? "full report"
1680
+ : `first ${auditFeedbackChars} chars`;
1681
+ const auditFeedbackTruncationHint = auditFeedbackIsFull
1682
+ ? ""
1683
+ : `\n\nReport truncated at the configured limit. /goal status shows the full report; change future feedback with /glla auditfeedbackchars=N (0 = full report).`;
1670
1684
  const trailingDisapprovals = countTrailingDisapprovals(history);
1671
1685
  if (auditCap > 0 && trailingDisapprovals >= auditCap) {
1672
1686
  updateGoal({
@@ -1681,7 +1695,7 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
1681
1695
  return {
1682
1696
  content: [{
1683
1697
  type: "text",
1684
- text: `The auditor has now disapproved ${trailingDisapprovals} times in a row (cap ${auditCap}). The goal is PAUSED — continuing to re-attempt without addressing the pattern wastes tokens. Latest report (first 800 chars):\n${result.output.slice(0, 800)}\n\nDo not call complete_goal again. Summarize the repeated objections for the user and ask how to proceed (/goal status shows all reports; /goal resume resumes).`,
1698
+ text: `The auditor has now disapproved ${trailingDisapprovals} times in a row (cap ${auditCap}). The goal is PAUSED — continuing to re-attempt without addressing the pattern wastes tokens. Latest report (${auditFeedbackLabel}):\n${auditFeedback}\n\nDo not call complete_goal again. Summarize the repeated objections for the user and ask how to proceed (/goal status shows all reports; /goal resume resumes).`,
1685
1699
  }],
1686
1700
  details: {},
1687
1701
  };
@@ -1696,7 +1710,7 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
1696
1710
  return {
1697
1711
  content: [{
1698
1712
  type: "text",
1699
- text: `Auditor disapproved. Report (first 800 chars):\n${result.output.slice(0, 800)}${noContractHint}`,
1713
+ text: `Auditor disapproved. Report (${auditFeedbackLabel}):\n${auditFeedback}${auditFeedbackTruncationHint}${noContractHint}`,
1700
1714
  }],
1701
1715
  details: {},
1702
1716
  };
@@ -2267,6 +2281,9 @@ interface Settings {
2267
2281
  autoResume?: boolean;
2268
2282
  /** v0.24.2: pause the goal after N consecutive auditor disapprovals (0 = unlimited). Default 3. */
2269
2283
  auditCap?: number;
2284
+ /** Maximum auditor-report characters returned to the executor after a
2285
+ * disapproval (0 = full report). Default 800. */
2286
+ auditFeedbackChars?: number;
2270
2287
  /** on → propose_* drafts activate WITHOUT the Confirm dialog and the
2271
2288
  * interview floor is skipped — the seed carries the intent (unattended
2272
2289
  * rigs). Default off: nothing activates before the user confirms. */
@@ -2293,6 +2310,7 @@ const DEFAULT_SETTINGS: Settings = {
2293
2310
  // v0.24.6: subagents inherit the session model by default — one quota
2294
2311
  // pool, no surprise 403s from a pinned default agent's provider.
2295
2312
  subagentModelStrategy: "inherit-parent",
2313
+ auditFeedbackChars: DEFAULT_AUDIT_FEEDBACK_CHARS,
2296
2314
  };
2297
2315
 
2298
2316
  // Two-tier config (v0.7.0): GLOBAL is the normal home — you set things once
@@ -2330,7 +2348,7 @@ function settingsProvenance(cwd: string): Record<keyof Settings, { value: unknow
2330
2348
  const glob = readSettingsFile(globalSettingsPath());
2331
2349
  const effective = loadSettings(cwd);
2332
2350
  const out: Record<string, { value: unknown; source: "project" | "global" | "default" }> = {};
2333
- const keys: Array<keyof Settings> = ["auditorModel", "auditorThinkingLevel", "notifyCmd", "tokenLimit", "wedgeAlertMinutes", "autoResume", "autoAcceptDrafts", "auditCap"];
2351
+ const keys: Array<keyof Settings> = ["auditorModel", "auditorThinkingLevel", "notifyCmd", "tokenLimit", "wedgeAlertMinutes", "autoResume", "autoAcceptDrafts", "auditCap", "auditFeedbackChars", "subagentModelStrategy", "subagentModelOverrides"];
2334
2352
  for (const k of keys) {
2335
2353
  if ((proj as Record<string, unknown>)[k] !== undefined) out[k] = { value: (proj as any)[k], source: "project" };
2336
2354
  else if ((glob as Record<string, unknown>)[k] !== undefined) out[k] = { value: (glob as any)[k], source: "global" };
@@ -2432,6 +2450,7 @@ async function openSettingsUI(ctx: ExtensionContext): Promise<void> {
2432
2450
  `Wedge alert minutes — ${show("wedgeAlertMinutes", `(${WEDGE_ALERT_DEFAULT_MINUTES}m default)`)}`,
2433
2451
  `Subagent model strategy — ${show("subagentModelStrategy", "(inherit-parent)")}`,
2434
2452
  `Subagent Explore model pin — ${loadSettings(ctx.cwd).subagentModelOverrides?.Explore ?? "(follows strategy)"}`,
2453
+ `Audit feedback characters — ${show("auditFeedbackChars", `(${DEFAULT_AUDIT_FEEDBACK_CHARS} default)`)}`,
2435
2454
  "Done",
2436
2455
  ],
2437
2456
  );
@@ -2485,6 +2504,15 @@ async function openSettingsUI(ctx: ExtensionContext): Promise<void> {
2485
2504
  saveSettings("global", ctx.cwd, { subagentModelOverrides: Object.keys(next).length > 0 ? next : undefined });
2486
2505
  ctx.ui.notify("Explore model pin saved — applies to NEW pi sessions.", "info");
2487
2506
  }
2507
+ } else if (choice.startsWith("Audit feedback")) {
2508
+ const v = await ctx.ui.input("Auditor feedback returned to the executor (characters)", `non-negative integer; 0 = full report, empty = default ${DEFAULT_AUDIT_FEEDBACK_CHARS}`);
2509
+ if (v !== undefined) {
2510
+ const raw = v.trim();
2511
+ const n = Number(raw);
2512
+ if (/^\d+$/.test(raw) && Number.isSafeInteger(n)) saveSettings("global", ctx.cwd, { auditFeedbackChars: n });
2513
+ else if (!v.trim()) saveSettings("global", ctx.cwd, { auditFeedbackChars: undefined });
2514
+ else ctx.ui.notify(`Not a non-negative integer: ${v}`, "warning");
2515
+ }
2488
2516
  }
2489
2517
  } catch {
2490
2518
  return;
@@ -2500,6 +2528,7 @@ async function cmdSettings(args: string, ctx: ExtensionContext): Promise<void> {
2500
2528
  // /glla notify='cmd $1' write to GLOBAL config
2501
2529
  // /glla tokenlimit=2000000 write to GLOBAL config
2502
2530
  // /glla wedgealert=30 hung-command alert minutes (0=off, unset=30)
2531
+ // /glla auditfeedbackchars=800 executor-visible disapproval report chars (0=full)
2503
2532
  // /glla project model=... write to PROJECT override (rare)
2504
2533
  // /glla model=unset remove key (from global; project model=unset for project)
2505
2534
  const trimmed = args.trim();
@@ -2524,6 +2553,7 @@ async function cmdSettings(args: string, ctx: ExtensionContext): Promise<void> {
2524
2553
  fmt("autoResume", "autoResume"),
2525
2554
  fmt("autoAcceptDrafts", "autoAccept"),
2526
2555
  fmt("auditCap", "auditCap"),
2556
+ fmt("auditFeedbackChars", "auditFeedbackChars"),
2527
2557
  `\nglobal: ${globalSettingsPath()}`,
2528
2558
  `project: ${projectSettingsPath(ctx.cwd)}`,
2529
2559
  `Set with: /glla key=value (global) · /glla project key=value (project override)`,
@@ -2609,6 +2639,19 @@ async function cmdSettings(args: string, ctx: ExtensionContext): Promise<void> {
2609
2639
  ctx.ui.notify(`auditcap must be a non-negative integer (0 = unlimited), got: ${value}`, "warning");
2610
2640
  }
2611
2641
  }
2642
+ } else if (key === "auditfeedbackchars") {
2643
+ if (["unset", "default"].includes(value)) {
2644
+ patch.auditFeedbackChars = undefined;
2645
+ changed = true;
2646
+ } else {
2647
+ const n = Number(value);
2648
+ if (/^\d+$/.test(value) && Number.isSafeInteger(n)) {
2649
+ patch.auditFeedbackChars = n;
2650
+ changed = true;
2651
+ } else {
2652
+ ctx.ui.notify(`auditfeedbackchars must be a non-negative integer (0 = full report), got: ${value}`, "warning");
2653
+ }
2654
+ }
2612
2655
  } else if (key === "thinking" || key === "auditorthinkinglevel") {
2613
2656
  if (["off", "minimal", "low", "medium", "high", "xhigh"].includes(value)) {
2614
2657
  patch.auditorThinkingLevel = value as Settings["auditorThinkingLevel"];
@@ -2619,13 +2662,13 @@ async function cmdSettings(args: string, ctx: ExtensionContext): Promise<void> {
2619
2662
  }
2620
2663
  }
2621
2664
  if (!changed) {
2622
- ctx.ui.notify("Nothing changed. Use key=value (model, thinking, notify, tokenlimit, autoresume), optionally prefixed with 'project'.", "info");
2665
+ ctx.ui.notify("Nothing changed. Use key=value (model, thinking, notify, tokenlimit, autoresume, auditcap, auditfeedbackchars), optionally prefixed with 'project'.", "info");
2623
2666
  return;
2624
2667
  }
2625
2668
  saveSettings(scope, ctx.cwd, patch);
2626
2669
  const effective = loadSettings(ctx.cwd);
2627
2670
  ctx.ui.notify(
2628
- `Saved to ${scope} config. Effective now: model=${effective.auditorModel ?? "(session model)"} thinking=${effective.auditorThinkingLevel ?? "(session)"} notify=${effective.notifyCmd ?? "(off)"} tokenLimit=${effective.tokenLimit ?? 0}${(effective.tokenLimit ?? 0) > 0 ? "" : " (off)"} autoResume=${effective.autoResume === true ? "on" : "off"}\n` +
2671
+ `Saved to ${scope} config. Effective now: model=${effective.auditorModel ?? "(session model)"} thinking=${effective.auditorThinkingLevel ?? "(session)"} notify=${effective.notifyCmd ?? "(off)"} tokenLimit=${effective.tokenLimit ?? 0}${(effective.tokenLimit ?? 0) > 0 ? "" : " (off)"} autoResume=${effective.autoResume === true ? "on" : "off"} auditFeedbackChars=${effective.auditFeedbackChars ?? DEFAULT_AUDIT_FEEDBACK_CHARS}${(effective.auditFeedbackChars ?? DEFAULT_AUDIT_FEEDBACK_CHARS) === 0 ? " (full report)" : ""}\n` +
2629
2672
  `Note: the auditor runs without extensions — it must be a built-in provider, not an extension-registered one.`,
2630
2673
  "info",
2631
2674
  );
@@ -2741,6 +2784,7 @@ export default function (pi: ExtensionAPI): void {
2741
2784
  ["tokenlimit=", "per-goal token budget (0 = off): /glla tokenlimit=2000000"],
2742
2785
  ["autoresume=", "on: auto-resume held goals/loops in fresh sessions"],
2743
2786
  ["auditcap=", "N: pause goal after N consecutive auditor disapprovals (default 3, 0 = unlimited)"],
2787
+ ["auditfeedbackchars=", `executor-visible disapproval report characters (default ${DEFAULT_AUDIT_FEEDBACK_CHARS}, 0 = full report)`],
2744
2788
  ["autoaccept=", "on: drafts activate without the Confirm dialog (unattended rigs)"],
2745
2789
  ["project", "write a project override: /glla project key=value"],
2746
2790
  ]),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-goal-list-loop-audit",
3
- "version": "0.24.7",
3
+ "version": "0.24.8",
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",