pi-goal-list-loop-audit 0.26.8 → 0.27.0

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
@@ -199,7 +199,7 @@ No external watchdog plugin needed.
199
199
  /glla tokenlimit=10000000 # per-goal token budget (default: off) → GLOBAL
200
200
  /glla tokenlimit=0 # explicitly no cap (the default)
201
201
  /glla wedgealert=30 # hung-command alert minutes (default: 30, 0 = off)
202
- /glla autoresume=on # held goals/loops auto-resume in fresh sessions (unattended rigs)
202
+ /glla autoresume=on # auto-resume goals/loops on session LOAD too (default: hold on load, auto-resume on reload/fork; off: never)
203
203
  /glla auditcap=5 # pause the goal after N consecutive auditor disapprovals (default 5, 0 = unlimited)
204
204
  /glla aggressivemode=on # keep-going defaults: autoResume, cap 10, stuck 10, wedge off, quota auto-retry, cap→TODOs
205
205
  /glla quotaretryminutes=60 # minutes before auto-retrying a quota-exhausted auditor
@@ -660,16 +660,21 @@ export function cloneGoal(goal: Goal): Goal {
660
660
  * setting for unattended restarts). One mechanical predicate; no heuristics.
661
661
  */
662
662
  export function shouldAutoResumeOnSessionStart(reason: string | undefined, autoResume: boolean | undefined): boolean {
663
- // v0.26.8: default flipped to ON — keep pushing forward on every session
664
- // start unless the user explicitly opts out (/glla autoresume=off). The
665
- // "super stuck" brakes (stall escalation, stale-api terminal, pending-
666
- // latch watchdog) still stop the machine loudly; a mere process restart
667
- // is not a reason to hold work. Explicit off preserves the v0.21.0 gate:
668
- // only sessions with history (resume/reload/fork) auto-resume.
669
- if (autoResume === false) {
670
- return reason === "resume" || reason === "reload" || reason === "fork";
671
- }
672
- return true;
663
+ // v0.26.9 tri-state:
664
+ // true → auto-resume on EVERY session start (unattended rigs).
665
+ // false → never auto-resume; always hold for an explicit resume.
666
+ // undefined DEFAULT: a human LOADING a session ("startup"/"new"/
667
+ // "resume", or old pi reporting no reason) must not trigger
668
+ // work show the held popup, they resume explicitly.
669
+ // In-session MACHINERY ("reload"/"fork") auto-resumes so an
670
+ // extension reload or session fork never strands work.
671
+ // Mid-session continuation (agent_end chains, heartbeat refires,
672
+ // post-compaction, list/loop transitions) is not gated here at all — it
673
+ // auto-continues forever unless a super-stuck brake (stall escalation,
674
+ // stale-api terminal, pending-latch watchdog) stops it loudly.
675
+ if (autoResume === true) return true;
676
+ if (autoResume === false) return false;
677
+ return reason === "reload" || reason === "fork";
673
678
  }
674
679
 
675
680
  /**
@@ -2852,66 +2852,93 @@ async function openSettingsUI(ctx: ExtensionContext): Promise<void> {
2852
2852
  const v = p.value === undefined ? fallback : String(p.value);
2853
2853
  return `${v} [${p.source}]`;
2854
2854
  };
2855
+ const settings = loadSettings(ctx.cwd);
2856
+ // v0.27.0: every option on ONE screen, grouped into sections, each row
2857
+ // carrying `label — value [provenance] — what it does` so the menu is
2858
+ // also the documentation. Header rows are no-ops when selected.
2859
+ const rows = [
2860
+ "── Keep-going ──",
2861
+ `Auto-resume on load — ${show("autoResume", "default")} — on: resume on session load too · off: never · default: hold on load, resume on reload/fork`,
2862
+ `Auto-accept drafts — ${show("autoAcceptDrafts", "(off)")} — on: goal/loop drafts activate without the Confirm dialog (unattended rigs)`,
2863
+ `Aggressive mode — ${show("aggressiveMode", "(off)")} — flips DEFAULTS toward keep-going (autoResume, cap 10, stuck 10, wedge off, quota auto-retry, cap→TODOs); explicit per-key settings still win`,
2864
+ "── Auditor ──",
2865
+ `Auditor model — ${show("auditorModel", "(pi session model)")} — provider/model override for the isolated auditor`,
2866
+ `Auditor thinking — ${show("auditorThinkingLevel", "(session, floor high)")} — thinking level for the auditor session`,
2867
+ `Audit cap — ${show("auditCap", "(5)")} — pause the goal after N consecutive disapprovals (0 = unlimited)`,
2868
+ `Audit feedback chars — ${show("auditFeedbackChars", "(full report)")} — cap the executor-visible disapproval report (0 = full report)`,
2869
+ `Quota retry minutes — ${show("quotaRetryMinutes", `(${DEFAULT_QUOTA_RETRY_MINUTES})`)} — auto-retry a quota-exhausted auditor after N minutes`,
2870
+ "── Stall brakes ──",
2871
+ `Wedge alert minutes — ${show("wedgeAlertMinutes", `(${WEDGE_ALERT_DEFAULT_MINUTES})`)} — hung-command alert while the session is busy (0 = off)`,
2872
+ `Stuck max interventions — ${show("stuckMaxInterventions", "(5)")} — consecutive stuck interventions before a loop stops`,
2873
+ `Stall escalation refires — ${show("stallEscalationRefires", "(5)")} — heartbeat refires with no turn before the goal pauses / loop stops (0 = never)`,
2874
+ "── Subagents ──",
2875
+ `Subagent model strategy — ${show("subagentModelStrategy", "(inherit-parent)")} — inherit-parent shares your session model+quota; agent-default pins haiku for Explore`,
2876
+ `Subagent Explore pin — ${settings.subagentModelOverrides?.Explore ?? "(follows strategy)"} — provider/model pin; always wins over strategy`,
2877
+ `Subagent Plan pin — ${settings.subagentModelOverrides?.Plan ?? "(follows strategy)"} — provider/model pin; always wins over strategy`,
2878
+ `Subagent general-purpose pin — ${settings.subagentModelOverrides?.["general-purpose"] ?? "(follows strategy)"} — provider/model pin; always wins over strategy`,
2879
+ "── Other ──",
2880
+ `Notify command — ${show("notifyCmd", "(off)")} — desktop push command; the event message is passed as $1`,
2881
+ `Token limit per goal — ${show("tokenLimit", "(off)")} — per-goal token budget; pause when exceeded (0 = off)`,
2882
+ `Reviewer config… — open the reviewer menu (post-completion follow-up enqueuer: mode, triggers, cascade, caps)`,
2883
+ "Done",
2884
+ ];
2855
2885
  let choice: string | undefined;
2856
2886
  try {
2857
2887
  choice = await ctx.ui.select(
2858
2888
  `pi-goal-list-loop-audit settings — global: ${globalSettingsPath()}`,
2859
- [
2860
- `Auditor model override — ${show("auditorModel", "(pi session model)")}`,
2861
- `Auditor thinking — ${show("auditorThinkingLevel", "(session, floor high)")}`,
2862
- `Notify command — ${show("notifyCmd", "(off)")}`,
2863
- `Token limit per goal — ${show("tokenLimit", "(off)")}`,
2864
- `Wedge alert minutes — ${show("wedgeAlertMinutes", `(${WEDGE_ALERT_DEFAULT_MINUTES}m default)`)}`,
2865
- `Aggressive mode — ${show("aggressiveMode", "(off)")}`,
2866
- `Quota retry minutes — ${show("quotaRetryMinutes", `(${DEFAULT_QUOTA_RETRY_MINUTES}m default)`)}`,
2867
- `Stuck max interventions — ${show("stuckMaxInterventions", "(5 default)")}`,
2868
- `Subagent model strategy — ${show("subagentModelStrategy", "(inherit-parent)")}`,
2869
- `Subagent Explore model pin — ${loadSettings(ctx.cwd).subagentModelOverrides?.Explore ?? "(follows strategy)"}`,
2870
- `Subagent Plan model pin — ${loadSettings(ctx.cwd).subagentModelOverrides?.Plan ?? "(follows strategy)"}`,
2871
- `Subagent general-purpose model pin — ${loadSettings(ctx.cwd).subagentModelOverrides?.["general-purpose"] ?? "(follows strategy)"}`,
2872
- `Audit feedback characters — ${show("auditFeedbackChars", "(full report)")}`,
2873
- "Done",
2874
- ],
2889
+ rows,
2875
2890
  );
2876
2891
  } catch {
2877
2892
  return;
2878
2893
  }
2879
2894
  if (!choice || choice === "Done") return;
2895
+ if (choice.startsWith("──")) continue; // section header — no-op
2880
2896
  try {
2881
- if (choice.startsWith("Auditor model")) {
2897
+ if (choice.startsWith("Auto-resume")) {
2898
+ const v = await ctx.ui.select("Auto-resume goals/loops on session start", [
2899
+ "default — HOLD when a session is loaded (popup shows what waits); auto-resume on reload/fork so machinery never strands work",
2900
+ "on — auto-resume on EVERY session start (unattended rigs)",
2901
+ "off — never auto-resume; always wait for an explicit resume",
2902
+ ]);
2903
+ if (v) saveSettings("global", ctx.cwd, { autoResume: v.startsWith("on") ? true : v.startsWith("off") ? false : undefined });
2904
+ } else if (choice.startsWith("Auto-accept drafts")) {
2905
+ const v = await ctx.ui.select("Auto-accept goal/loop drafts", [
2906
+ "off — the Confirm dialog gates every draft",
2907
+ "on — drafts activate immediately, no Confirm (unattended rigs)",
2908
+ ]);
2909
+ if (v) saveSettings("global", ctx.cwd, { autoAcceptDrafts: v.startsWith("on") ? true : undefined });
2910
+ } else if (choice.startsWith("Aggressive mode")) {
2911
+ const v = await ctx.ui.select("Aggressive mode (flips DEFAULTS toward keep-going — explicit per-key settings still win)", [
2912
+ "off — current behavior: pause at the audit cap, wedge alerts on, manual resume",
2913
+ "on — autoResume, audit cap 10, stuck max 10, wedge alerts off, quota auto-retry, cap disapprovals become a TODO list and the goal KEEPS GOING",
2914
+ ]);
2915
+ if (v) {
2916
+ saveSettings("global", ctx.cwd, { aggressiveMode: v.startsWith("on") });
2917
+ ctx.ui.notify(`Aggressive mode ${v.startsWith("on") ? "ON — goals keep going past the audit cap; objections become TODOs" : "off"}.`, "info");
2918
+ }
2919
+ } else if (choice.startsWith("Auditor model")) {
2882
2920
  const v = await ctx.ui.input("Auditor model override", "provider/model-id — empty keeps the pi session model");
2883
2921
  if (v !== undefined) saveSettings("global", ctx.cwd, { auditorModel: v.trim() || undefined });
2884
2922
  } else if (choice.startsWith("Auditor thinking")) {
2885
2923
  const v = await ctx.ui.select("Auditor thinking level", ["off", "minimal", "low", "medium", "high", "xhigh"]);
2886
2924
  if (v) saveSettings("global", ctx.cwd, { auditorThinkingLevel: v as Settings["auditorThinkingLevel"] });
2887
- } else if (choice.startsWith("Notify command")) {
2888
- const v = await ctx.ui.input("Notify command the event message is passed as $1", "e.g. a desktop-notification or push command; empty = off");
2889
- if (v !== undefined) saveSettings("global", ctx.cwd, { notifyCmd: v.trim() || undefined });
2890
- } else if (choice.startsWith("Token limit")) {
2891
- const v = await ctx.ui.input("Per-goal token budget", "non-negative integer; 0 or empty = off (no cap)");
2925
+ } else if (choice.startsWith("Audit cap")) {
2926
+ const v = await ctx.ui.input("Consecutive auditor disapprovals before the goal pauses", "non-negative integer; 0 = unlimited, empty = default 5");
2892
2927
  if (v !== undefined) {
2893
2928
  const n = Number.parseInt(v.trim(), 10);
2894
- if (Number.isFinite(n) && n >= 0) saveSettings("global", ctx.cwd, { tokenLimit: n });
2895
- else if (!v.trim()) saveSettings("global", ctx.cwd, { tokenLimit: undefined });
2929
+ if (Number.isFinite(n) && n >= 0) saveSettings("global", ctx.cwd, { auditCap: n });
2930
+ else if (!v.trim()) saveSettings("global", ctx.cwd, { auditCap: undefined });
2896
2931
  else ctx.ui.notify(`Not a non-negative integer: ${v}`, "warning");
2897
2932
  }
2898
- } else if (choice.startsWith("Wedge alert")) {
2899
- const v = await ctx.ui.input("Wedge alert threshold (minutes)", "non-negative integer; 0 = off, empty = default 30");
2933
+ } else if (choice.startsWith("Audit feedback")) {
2934
+ const v = await ctx.ui.input("Auditor feedback returned to the executor (characters)", "non-negative integer cap; 0 or empty = full report (default)");
2900
2935
  if (v !== undefined) {
2901
- const n = Number.parseInt(v.trim(), 10);
2902
- if (Number.isFinite(n) && n >= 0) saveSettings("global", ctx.cwd, { wedgeAlertMinutes: n });
2903
- else if (!v.trim()) saveSettings("global", ctx.cwd, { wedgeAlertMinutes: undefined });
2936
+ const raw = v.trim();
2937
+ const n = Number(raw);
2938
+ if (/^\d+$/.test(raw) && Number.isSafeInteger(n)) saveSettings("global", ctx.cwd, { auditFeedbackChars: n });
2939
+ else if (!v.trim()) saveSettings("global", ctx.cwd, { auditFeedbackChars: undefined });
2904
2940
  else ctx.ui.notify(`Not a non-negative integer: ${v}`, "warning");
2905
2941
  }
2906
- } else if (choice.startsWith("Aggressive mode")) {
2907
- const v = await ctx.ui.select("Aggressive mode (flips DEFAULTS toward keep-going — explicit per-key settings still win)", [
2908
- "off — current behavior: pause at the audit cap, wedge alerts on, manual resume",
2909
- "on — autoResume, audit cap 10, stuck max 10, wedge alerts off, quota auto-retry, cap disapprovals become a TODO list and the goal KEEPS GOING",
2910
- ]);
2911
- if (v) {
2912
- saveSettings("global", ctx.cwd, { aggressiveMode: v.startsWith("on") });
2913
- ctx.ui.notify(`Aggressive mode ${v.startsWith("on") ? "ON — goals keep going past the audit cap; objections become TODOs" : "off"}.`, "info");
2914
- }
2915
2942
  } else if (choice.startsWith("Quota retry minutes")) {
2916
2943
  const v = await ctx.ui.input("Minutes before auto-retrying a quota-exhausted auditor", `positive integer; empty = default ${DEFAULT_QUOTA_RETRY_MINUTES}`);
2917
2944
  if (v !== undefined) {
@@ -2920,6 +2947,14 @@ async function openSettingsUI(ctx: ExtensionContext): Promise<void> {
2920
2947
  else if (!v.trim()) saveSettings("global", ctx.cwd, { quotaRetryMinutes: undefined });
2921
2948
  else ctx.ui.notify(`Not a positive integer: ${v}`, "warning");
2922
2949
  }
2950
+ } else if (choice.startsWith("Wedge alert")) {
2951
+ const v = await ctx.ui.input("Wedge alert threshold (minutes)", "non-negative integer; 0 = off, empty = default 30");
2952
+ if (v !== undefined) {
2953
+ const n = Number.parseInt(v.trim(), 10);
2954
+ if (Number.isFinite(n) && n >= 0) saveSettings("global", ctx.cwd, { wedgeAlertMinutes: n });
2955
+ else if (!v.trim()) saveSettings("global", ctx.cwd, { wedgeAlertMinutes: undefined });
2956
+ else ctx.ui.notify(`Not a non-negative integer: ${v}`, "warning");
2957
+ }
2923
2958
  } else if (choice.startsWith("Stuck max interventions")) {
2924
2959
  const v = await ctx.ui.input("Consecutive stuck interventions before a loop stops", "positive integer; empty = default 5 (10 under aggressiveMode)");
2925
2960
  if (v !== undefined) {
@@ -2928,6 +2963,14 @@ async function openSettingsUI(ctx: ExtensionContext): Promise<void> {
2928
2963
  else if (!v.trim()) saveSettings("global", ctx.cwd, { stuckMaxInterventions: undefined });
2929
2964
  else ctx.ui.notify(`Not a positive integer: ${v}`, "warning");
2930
2965
  }
2966
+ } else if (choice.startsWith("Stall escalation")) {
2967
+ const v = await ctx.ui.input("Heartbeat refires without a turn before the goal pauses / loop stops", "non-negative integer; 0 = never escalate, empty = default 5");
2968
+ if (v !== undefined) {
2969
+ const n = Number.parseInt(v.trim(), 10);
2970
+ if (Number.isFinite(n) && n >= 0) saveSettings("global", ctx.cwd, { stallEscalationRefires: n });
2971
+ else if (!v.trim()) saveSettings("global", ctx.cwd, { stallEscalationRefires: undefined });
2972
+ else ctx.ui.notify(`Not a non-negative integer: ${v}`, "warning");
2973
+ }
2931
2974
  } else if (choice.startsWith("Subagent model strategy")) {
2932
2975
  const v = await ctx.ui.select("Subagent model (pi-subagents default agents)", [
2933
2976
  "inherit-parent — subagents share your session model + its quota pool (fixes separate-provider 403s; search agents may run on a pricier model)",
@@ -2938,8 +2981,8 @@ async function openSettingsUI(ctx: ExtensionContext): Promise<void> {
2938
2981
  saveSettings("global", ctx.cwd, { subagentModelStrategy: strategy });
2939
2982
  ctx.ui.notify("Subagent model strategy saved — applies to NEW pi sessions (pi-subagents registers agents at session start).", "info");
2940
2983
  }
2941
- } else if (/^Subagent (Explore|Plan|general-purpose) model pin/.test(choice)) {
2942
- const agentType = choice.match(/^Subagent (Explore|Plan|general-purpose) model pin/)![1]!;
2984
+ } else if (/^Subagent (Explore|Plan|general-purpose) pin/.test(choice)) {
2985
+ const agentType = choice.match(/^Subagent (Explore|Plan|general-purpose) pin/)![1]!;
2943
2986
  const v = await ctx.ui.input(`Model pin for ${agentType} subagents`, "provider/model-id e.g. minimax/MiniMax-M3 — always wins over strategy; empty = follow strategy");
2944
2987
  if (v !== undefined) {
2945
2988
  const current = loadSettings(ctx.cwd).subagentModelOverrides ?? {};
@@ -2949,15 +2992,19 @@ async function openSettingsUI(ctx: ExtensionContext): Promise<void> {
2949
2992
  saveSettings("global", ctx.cwd, { subagentModelOverrides: Object.keys(next).length > 0 ? next : undefined });
2950
2993
  ctx.ui.notify(`${agentType} model pin saved — applies to NEW pi sessions.`, "info");
2951
2994
  }
2952
- } else if (choice.startsWith("Audit feedback")) {
2953
- const v = await ctx.ui.input("Auditor feedback returned to the executor (characters)", "non-negative integer cap; 0 or empty = full report (default)");
2995
+ } else if (choice.startsWith("Notify command")) {
2996
+ const v = await ctx.ui.input("Notify command the event message is passed as $1", "e.g. a desktop-notification or push command; empty = off");
2997
+ if (v !== undefined) saveSettings("global", ctx.cwd, { notifyCmd: v.trim() || undefined });
2998
+ } else if (choice.startsWith("Token limit")) {
2999
+ const v = await ctx.ui.input("Per-goal token budget", "non-negative integer; 0 or empty = off (no cap)");
2954
3000
  if (v !== undefined) {
2955
- const raw = v.trim();
2956
- const n = Number(raw);
2957
- if (/^\d+$/.test(raw) && Number.isSafeInteger(n)) saveSettings("global", ctx.cwd, { auditFeedbackChars: n });
2958
- else if (!v.trim()) saveSettings("global", ctx.cwd, { auditFeedbackChars: undefined });
3001
+ const n = Number.parseInt(v.trim(), 10);
3002
+ if (Number.isFinite(n) && n >= 0) saveSettings("global", ctx.cwd, { tokenLimit: n });
3003
+ else if (!v.trim()) saveSettings("global", ctx.cwd, { tokenLimit: undefined });
2959
3004
  else ctx.ui.notify(`Not a non-negative integer: ${v}`, "warning");
2960
3005
  }
3006
+ } else if (choice.startsWith("Reviewer config")) {
3007
+ await cmdReviewerSettings(ctx);
2961
3008
  }
2962
3009
  } catch {
2963
3010
  return;
@@ -3161,6 +3208,8 @@ async function cmdSettings(args: string, ctx: ExtensionContext): Promise<void> {
3161
3208
  fmt("aggressiveMode", "aggressiveMode"),
3162
3209
  fmt("quotaRetryMinutes", "quotaRetryMinutes"),
3163
3210
  fmt("stuckMaxInterventions", "stuckMaxInterventions"),
3211
+ fmt("stallEscalationRefires", "stallEscalation"),
3212
+ fmt("wedgeAlertMinutes", "wedgeAlert"),
3164
3213
  // v0.25.6: effective per-type subagent model resolution.
3165
3214
  ...["Explore", "Plan", "general-purpose"].map(
3166
3215
  (t) => `subagent ${t}: ${resolveEffectiveSubagentModel(t, loadSettings(ctx.cwd), (ctx.model as any)?.id ? `${(ctx.model as any).provider}/${(ctx.model as any).id}` : undefined)}`,
@@ -3329,7 +3378,7 @@ async function cmdSettings(args: string, ctx: ExtensionContext): Promise<void> {
3329
3378
  saveSettings(scope, ctx.cwd, patch);
3330
3379
  const effective = loadSettings(ctx.cwd);
3331
3380
  ctx.ui.notify(
3332
- `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 === false ? "off" : "on (default)"} auditFeedbackChars=${effective.auditFeedbackChars ?? DEFAULT_AUDIT_FEEDBACK_CHARS}${(effective.auditFeedbackChars ?? DEFAULT_AUDIT_FEEDBACK_CHARS) === 0 ? " (full report)" : ""}\n` +
3381
+ `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" : effective.autoResume === false ? "off" : "default (hold on load)"} auditFeedbackChars=${effective.auditFeedbackChars ?? DEFAULT_AUDIT_FEEDBACK_CHARS}${(effective.auditFeedbackChars ?? DEFAULT_AUDIT_FEEDBACK_CHARS) === 0 ? " (full report)" : ""}\n` +
3333
3382
  `Note: the auditor runs without extensions — it must be a built-in provider, not an extension-registered one.`,
3334
3383
  "info",
3335
3384
  );
@@ -3444,7 +3493,7 @@ export default function (pi: ExtensionAPI): void {
3444
3493
  ["thinking=", "auditor thinking level: /glla thinking=high"],
3445
3494
  ["notify=", "desktop push command: /glla notify='notify-send pi \"$1\"'"],
3446
3495
  ["tokenlimit=", "per-goal token budget (0 = off): /glla tokenlimit=2000000"],
3447
- ["autoresume=", "on (default): auto-resume goals/loops on every session start; off: hold on fresh sessions"],
3496
+ ["autoresume=", "default: hold when a session is loaded, auto-resume on reload/fork; on: always auto-resume; off: never"],
3448
3497
  ["auditcap=", "N: pause goal after N consecutive auditor disapprovals (default 5, 0 = unlimited)"],
3449
3498
  ["auditfeedbackchars=", "cap on executor-visible disapproval report chars (0 = full report, the default)"],
3450
3499
  ["aggressivemode=", "on: keep-going defaults — autoResume, cap 10, stuck 10, wedge off, quota auto-retry, cap→TODOs"],
@@ -3641,12 +3690,14 @@ export default function (pi: ExtensionAPI): void {
3641
3690
  } catch (err) {
3642
3691
  ctx.ui.notify(`glla subagent override sync failed: ${err instanceof Error ? err.message : String(err)}`, "warning");
3643
3692
  }
3644
- // Restore gate (v0.21.0, default flipped v0.26.8): auto-resume on EVERY
3645
- // session start by defaultkeep pushing forward unless super stuck
3646
- // (the stall escalation / stale-api / latch brakes still stop loudly).
3647
- // /glla autoresume=off restores the v0.21.0 gate: fresh sessions
3648
- // ("startup"/"new", or a pi too old to report a reason) HOLD, only
3649
- // sessions with history ("resume"/"reload"/"fork") auto-resume.
3693
+ // Restore gate (v0.26.9 tri-state): a human LOADING a session
3694
+ // ("startup"/"new"/"resume", or no reason) HOLDS the popup shows what
3695
+ // is waiting and nothing starts until they resume explicitly. In-session
3696
+ // machinery ("reload"/"fork") auto-resumes. /glla autoresume=on opts a
3697
+ // project into auto-resume everywhere (unattended rigs); autoresume=off
3698
+ // never auto-resumes. Once running, the chain auto-continues forever
3699
+ // unless a super-stuck brake (stall escalation / stale-api / latch)
3700
+ // stops it loudly.
3650
3701
  const autoResume = shouldAutoResumeOnSessionStart(event?.reason, resolveEffectiveAggressiveSettings(loadSettings(ctx.cwd)).autoResume);
3651
3702
  // v0.25.0 (contract item 6): aggressiveMode announces every auto-event.
3652
3703
  if (
@@ -3668,7 +3719,7 @@ export default function (pi: ExtensionAPI): void {
3668
3719
  state.loop = { ...l, active: false, stopReason: HELD_ON_RESTORE };
3669
3720
  persistState(ctx);
3670
3721
  ctx.ui.notify(
3671
- `Loop held on restore (/glla autoresume=off): ${l.target.slice(0, 60)} — /loop to resume, /glla autoresume=on to auto-resume in this project.`,
3722
+ `Loop held on restore: ${l.target.slice(0, 60)} — /loop to resume, /glla autoresume=on to auto-resume on session load in this project.`,
3672
3723
  "info",
3673
3724
  );
3674
3725
  }
@@ -3687,7 +3738,7 @@ export default function (pi: ExtensionAPI): void {
3687
3738
  const resumeHint = `${resumeCmd} to continue${queued > 0 ? ` (+${queued} waiting in the list)` : ""} · /glla autoresume=on to auto-resume in this project`;
3688
3739
  updateGoal({
3689
3740
  status: "paused",
3690
- pauseReason: "restored in a fresh session — held because /glla autoresume=off",
3741
+ pauseReason: "restored on session load — held for explicit resume",
3691
3742
  pauseSuggestedAction: resumeHint,
3692
3743
  }, ctx);
3693
3744
  ctx.ui.notify(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-goal-list-loop-audit",
3
- "version": "0.26.8",
3
+ "version": "0.27.0",
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",