pi-goal-list-loop-audit 0.24.4 → 0.24.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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * pi-goal-list-loop-audit — v0.1.0
2
+ * pi-goal-list-loop-audit — v0.24.5
3
3
  * extensions/goal-loop-core.ts
4
4
  *
5
5
  * Shared types, state machine, JSONL persistence, helpers.
@@ -703,3 +703,44 @@ export function classifySessionCtx(ownerSession: unknown, ownerLive: boolean, se
703
703
  if (!ownerSession || !ownerLive) return "claim";
704
704
  return sessionManager === ownerSession ? "refresh" : "foreign";
705
705
  }
706
+
707
+ // =================================================================
708
+ // v0.24.5: tool-visibility self-heal
709
+ // =================================================================
710
+ //
711
+ // Root cause (INCIDENT-COMPLETION-BLACKHOLE-2026-07-23): external
712
+ // extensions like pi-plugin-list-selector-modlist call pi.setActiveTools
713
+ // with a frozen tool snapshot at session_start. When glla's session_start
714
+ // handler runs BEFORE theirs (load order), our lazily-registered agent
715
+ // tools get registered, briefly auto-activated, then wiped from the
716
+ // model-facing active set on the very next pi.setActiveTools call from
717
+ // modlist. Commands, widget, watchdog keep working (they don't go
718
+ // through the tool registry), but every agent tool — complete_goal,
719
+ // propose_loop_draft, etc. — answers "Tool not found" to the model.
720
+ //
721
+ // Self-heal: any handler that triggers registerAgentTools must also
722
+ // ensure the registered tool names are present in pi.getActiveTools(),
723
+ // re-adding any missing ones via pi.setActiveTools. Once per session,
724
+ // notify the user naming the external allowlist as the likely culprit
725
+ // so they can fix their profile once and silence it.
726
+
727
+ export const GLLA_TOOL_NAMES = [
728
+ "complete_goal",
729
+ "pause_goal",
730
+ "complete_task",
731
+ "update_task_status",
732
+ "propose_goal_draft",
733
+ "propose_loop_draft",
734
+ "propose_loop_refine",
735
+ "list_add",
736
+ "list_activate",
737
+ "list_status",
738
+ "propose_task_list",
739
+ ] as const;
740
+
741
+ export type GllaToolName = (typeof GLLA_TOOL_NAMES)[number];
742
+
743
+ export function missingGllaTools(activeNames: readonly string[]): readonly GllaToolName[] {
744
+ const active = new Set(activeNames);
745
+ return GLLA_TOOL_NAMES.filter((n) => !active.has(n));
746
+ }
@@ -63,6 +63,7 @@ import {
63
63
  shouldAutoResumeOnSessionStart,
64
64
  statusLabel,
65
65
  writeGoalMd,
66
+ missingGllaTools,
66
67
  } from "../goal-loop-core.js";
67
68
  import { runGoalCompletionAuditor } from "../goal-loop-auditor.js";
68
69
  import {
@@ -2729,6 +2730,29 @@ export default function (pi: ExtensionAPI): void {
2729
2730
  // "no active goal" if called).
2730
2731
  let registeredCtx: ExtensionContext | null = null;
2731
2732
 
2733
+ // v0.24.5 tool-visibility self-heal: surface the notify exactly once
2734
+ // per session so the user learns about an external allowlist once and
2735
+ // can fix their profile to silence it.
2736
+ let toolHealNotified = false;
2737
+ function ensureAgentToolsActive(pi: ExtensionAPI, ctx: ExtensionContext): void {
2738
+ try {
2739
+ const active = pi.getActiveTools();
2740
+ const missing = missingGllaTools(active);
2741
+ if (missing.length === 0) return;
2742
+ pi.setActiveTools([...active, ...missing]);
2743
+ if (!toolHealNotified) {
2744
+ toolHealNotified = true;
2745
+ const list = missing.join(", ");
2746
+ ctx.ui.notify(
2747
+ `glla: ${missing.length} agent tool(s) were hidden by an external tool allowlist (e.g. a modlist profile) and have been re-activated (${list}). Add them to your allowlist profile to silence this.`,
2748
+ "warning",
2749
+ );
2750
+ }
2751
+ } catch {
2752
+ // Older pi without getActiveTools/setActiveTools — nothing we can do.
2753
+ }
2754
+ }
2755
+
2732
2756
  pi.on("message_start", async (event: any, _ctx: ExtensionContext) => {
2733
2757
  // v0.14.0 drafting floor: count real user replies while drafting. Our
2734
2758
  // own injected draft prompt arrives as a user message — skip that one.
@@ -2773,6 +2797,7 @@ export default function (pi: ExtensionAPI): void {
2773
2797
  registerAgentTools(pi, ctx);
2774
2798
  registeredCtx = ctx;
2775
2799
  }
2800
+ ensureAgentToolsActive(pi, ctx);
2776
2801
  warnOnCommandCollision(ctx);
2777
2802
  warnIfAuditorProviderRisky(ctx);
2778
2803
  // Restore gate (v0.21.0): a fresh session ("startup"/"new", or a pi too
@@ -2853,6 +2878,7 @@ export default function (pi: ExtensionAPI): void {
2853
2878
  registerAgentTools(pi, ctx);
2854
2879
  registeredCtx = ctx;
2855
2880
  }
2881
+ ensureAgentToolsActive(pi, ctx);
2856
2882
  // Nudge accounting: a supervising turn with zero tool calls is a nudge
2857
2883
  // (no real progress); 3 consecutive → pause. Tool-use turns reset it.
2858
2884
  if (isSupervising()) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-goal-list-loop-audit",
3
- "version": "0.24.4",
3
+ "version": "0.24.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",