pi-goal-list-loop-audit 0.28.32 → 0.28.34
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 +28 -16
- package/extensions/loops/goal.ts +58 -23
- package/extensions/settings-menu.ts +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -201,7 +201,7 @@ No external watchdog plugin needed.
|
|
|
201
201
|
/glla # open the settings UI
|
|
202
202
|
/glla model=provider/id # auditor model override → GLOBAL
|
|
203
203
|
/glla thinking=high # auditor thinking → GLOBAL
|
|
204
|
-
/glla notify='cmd "$1"' # push
|
|
204
|
+
/glla notify='cmd "$1"' # custom push cmd · unset = auto-detect (notify-send/osascript) · off = silent → GLOBAL
|
|
205
205
|
/glla tokenlimit=10000000 # per-goal token budget (default: off) → GLOBAL
|
|
206
206
|
/glla tokenlimit=0 # explicitly no cap (the default)
|
|
207
207
|
/glla wedgealert=30 # hung-command alert minutes (default: 30, 0 = off)
|
|
@@ -233,15 +233,14 @@ activates the moment the agent proposes it, with a notification and a
|
|
|
233
233
|
`draft_autoaccepted` ledger entry (auto-accept is never silent). The seed
|
|
234
234
|
carries the intent. Pair with `autoresume=on` for fully unattended rigs.
|
|
235
235
|
|
|
236
|
-
## Subagents
|
|
236
|
+
## Subagents
|
|
237
237
|
|
|
238
|
-
|
|
239
|
-
|
|
238
|
+
glla's guarantees here come from glla itself (session-handle
|
|
239
|
+
discrimination), not from any specific subagent plugin — any Agent-tool
|
|
240
|
+
provider gets them. Subagent sessions bind extensions too, so glla loads
|
|
241
|
+
there — by design the **main session owns the goal/loop/list; subagents
|
|
242
|
+
are workers** (v0.23.8):
|
|
240
243
|
|
|
241
|
-
- Read-only agents (Explore, Plan) get no glla tools (pi-subagents gates
|
|
242
|
-
them); general-purpose agents see them but state-mutating calls
|
|
243
|
-
(`complete_goal`, `propose_*`, `list_add`, `pause_goal`, …) are refused
|
|
244
|
-
with "report back to the main agent".
|
|
245
244
|
- A subagent session never clobbers the loop's session handle, never runs
|
|
246
245
|
the restore gate, and never drives continuation — so the heartbeat,
|
|
247
246
|
wedge alert, and auto-resume machinery always act on the main session.
|
|
@@ -249,6 +248,11 @@ Subagent sessions bind extensions too, so glla loads there — by design the
|
|
|
249
248
|
is the discriminator.)
|
|
250
249
|
- Subagent tool activity counts as activity for the wedge clock — a long
|
|
251
250
|
subagent run is work, not a hang.
|
|
251
|
+
- With `@tintinweb/pi-subagents` specifically (the one we test against):
|
|
252
|
+
read-only agents (Explore, Plan) get no glla tools; general-purpose
|
|
253
|
+
agents see them but state-mutating calls (`complete_goal`, `propose_*`,
|
|
254
|
+
`list_add`, `pause_goal`, …) are refused with "report back to the main
|
|
255
|
+
agent".
|
|
252
256
|
|
|
253
257
|
## Token guard
|
|
254
258
|
|
|
@@ -287,17 +291,25 @@ contradictory turns. One driver at a time:
|
|
|
287
291
|
fine to keep, never run a ralph loop while a goal/list/loop is active.
|
|
288
292
|
|
|
289
293
|
**Goes well with it**: `@juicesharp/rpiv-ask-user-question` (drafting uses its
|
|
290
|
-
structured forms), `@tintinweb/pi-subagents`
|
|
291
|
-
inside goal work),
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
294
|
+
structured forms), any subagent provider — e.g. `@tintinweb/pi-subagents` —
|
|
295
|
+
(spawn research/review subagents inside goal work), `pi-chrome` (the
|
|
296
|
+
research/search path for goals — logged-in browsing with no extra services;
|
|
297
|
+
standalone search skills like `mmx-cli`/`pi-search-skill` are optional
|
|
298
|
+
conveniences for bulk queries, not requirements).
|
|
299
|
+
|
|
300
|
+
**Overlaps — pick one**: `@tintinweb/pi-tasks` is a second task list next to
|
|
301
|
+
`/list`, and in practice the glla list *is* the task list (queue, statuses,
|
|
302
|
+
per-item audit trail) while the todos end up the weaker copy. We ran both
|
|
303
|
+
and removed pi-tasks. If you truly need session-wide dependency DAGs beyond
|
|
304
|
+
one ordered queue, it exists — but installing both is not the ideal combo.
|
|
296
305
|
|
|
297
306
|
**Two footnotes**: (1) extension-registered providers work in the main session
|
|
298
307
|
but not the auditor's extension-less session — if audits fail auth, set the
|
|
299
|
-
override once with `/glla model=`. (2) `pi-notify-agent` notifies on every
|
|
300
|
-
|
|
308
|
+
override once with `/glla model=`. (2) `pi-notify-agent` notifies on every
|
|
309
|
+
turn; glla pushes fire only where there is something to DO (pauses, verdicts,
|
|
310
|
+
storms, wedge) and work out of the box — with no `notify=` configured glla
|
|
311
|
+
auto-detects `notify-send`/`osascript`; `notify=off` silences, `notify='<cmd>'`
|
|
312
|
+
customizes.
|
|
301
313
|
|
|
302
314
|
## Files
|
|
303
315
|
|
package/extensions/loops/goal.ts
CHANGED
|
@@ -1932,15 +1932,44 @@ function addSingleItem(ctx: ExtensionContext, raw: string): void {
|
|
|
1932
1932
|
}
|
|
1933
1933
|
|
|
1934
1934
|
/**
|
|
1935
|
-
*
|
|
1936
|
-
*
|
|
1937
|
-
*
|
|
1935
|
+
* Push notification, folded IN by default (v0.28.34 — user: "leaving it to
|
|
1936
|
+
* the user to set up sucks, cause then they won't have it"). Resolution:
|
|
1937
|
+
* notifyCmd === "off" → silent (explicit opt-out)
|
|
1938
|
+
* notifyCmd set → that command, message passed as $1
|
|
1939
|
+
* notifyCmd unset → auto-detect ONCE per session: notify-send
|
|
1940
|
+
* (Linux) or osascript (macOS); none → silent.
|
|
1941
|
+
* Pushes fire only where there is something to DO — pauses, auditor
|
|
1942
|
+
* verdicts, storms, wedge, persistence degradation — never per-turn noise.
|
|
1943
|
+
* Fire-and-forget: a broken notifier never blocks the loop.
|
|
1938
1944
|
*/
|
|
1945
|
+
let autoNotifyCmd: string | null | undefined; // undefined = not probed yet
|
|
1946
|
+
|
|
1947
|
+
function probeAutoNotify(ctx: ExtensionContext): void {
|
|
1948
|
+
if (autoNotifyCmd !== undefined || !extensionApi) return;
|
|
1949
|
+
autoNotifyCmd = null; // probing sentinel — drops at most the first push
|
|
1950
|
+
void extensionApi
|
|
1951
|
+
.exec("bash", ["-c", "command -v notify-send || command -v osascript || true"], { cwd: ctx.cwd })
|
|
1952
|
+
.then((r) => {
|
|
1953
|
+
const found = String((r as { stdout?: string }).stdout ?? "").trim();
|
|
1954
|
+
if (found.endsWith("notify-send")) autoNotifyCmd = `notify-send "pi-goal-list-loop-audit" "$1"`;
|
|
1955
|
+
// env-var handoff: the message never touches AppleScript quoting.
|
|
1956
|
+
else if (found.endsWith("osascript")) autoNotifyCmd = `GLLA_MSG="$1" osascript -e 'display notification (system attribute "GLLA_MSG") with title "pi-goal-list-loop-audit"'`;
|
|
1957
|
+
else autoNotifyCmd = null;
|
|
1958
|
+
})
|
|
1959
|
+
.catch(() => {
|
|
1960
|
+
autoNotifyCmd = null;
|
|
1961
|
+
});
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1939
1964
|
function notifyExternal(ctx: ExtensionContext, message: string): void {
|
|
1940
1965
|
try {
|
|
1941
1966
|
const settings = loadSettings(ctx.cwd);
|
|
1942
|
-
|
|
1943
|
-
|
|
1967
|
+
if (settings.notifyCmd === "off" || !extensionApi) return;
|
|
1968
|
+
const cmd = settings.notifyCmd ?? autoNotifyCmd;
|
|
1969
|
+
if (!cmd) {
|
|
1970
|
+
if (settings.notifyCmd === undefined && autoNotifyCmd === undefined) probeAutoNotify(ctx);
|
|
1971
|
+
return;
|
|
1972
|
+
}
|
|
1944
1973
|
void extensionApi.exec("bash", ["-c", cmd, "pi-goal-list-loop-audit", message], { cwd: ctx.cwd }).catch(() => {});
|
|
1945
1974
|
} catch {
|
|
1946
1975
|
// non-fatal by design
|
|
@@ -3892,7 +3921,7 @@ export async function handleSettingChoice(id: string, ctx: ExtensionContext): Pr
|
|
|
3892
3921
|
// current effective subagent models. Treat as no-op.
|
|
3893
3922
|
return;
|
|
3894
3923
|
case "notifyCmd": {
|
|
3895
|
-
const v = await ctx.ui.input("Notify command — the event message is passed as $1", "
|
|
3924
|
+
const v = await ctx.ui.input("Notify command — the event message is passed as $1", "custom command · empty = auto-detect (notify-send/osascript) · 'off' = silent");
|
|
3896
3925
|
if (v !== undefined) saveSettings("global", ctx.cwd, { notifyCmd: v.trim() || undefined });
|
|
3897
3926
|
return;
|
|
3898
3927
|
}
|
|
@@ -4186,7 +4215,7 @@ function cmdLog(args: string, ctx: ExtensionContext): void {
|
|
|
4186
4215
|
}
|
|
4187
4216
|
|
|
4188
4217
|
/**
|
|
4189
|
-
* v0.28.31: /glla
|
|
4218
|
+
* v0.28.31 (renamed v0.28.33): /glla wipe — ONE confirmed command that leaves a project with
|
|
4190
4219
|
* zero live glla state. User directive: "make sure we only have one goal or
|
|
4191
4220
|
* loop or list at a time — many of my older projects have leftovers" (the
|
|
4192
4221
|
* fleet scan found queued lists up to 56 deep, held loops at iter 50, and
|
|
@@ -4195,7 +4224,7 @@ function cmdLog(args: string, ctx: ExtensionContext): void {
|
|
|
4195
4224
|
* the list is cleared, the loop record is wiped after a graceful stop.
|
|
4196
4225
|
* History stays in .pi-glla; only the live state goes.
|
|
4197
4226
|
*/
|
|
4198
|
-
async function
|
|
4227
|
+
async function cmdGllaWipe(ctx: ExtensionContext): Promise<void> {
|
|
4199
4228
|
const g = state.goal;
|
|
4200
4229
|
const live = g && (g.status === "active" || g.status === "paused" || g.status === "auditing");
|
|
4201
4230
|
const n = listQueue().length;
|
|
@@ -4211,36 +4240,36 @@ async function cmdGllaReset(ctx: ExtensionContext): Promise<void> {
|
|
|
4211
4240
|
if (loop) parts.push(`loop ${loop.active ? "stopped" : "cleared"} (iter ${loop.iteration}${loop.bestValue !== null && loop.bestValue !== undefined ? `, best ${loop.bestValue}` : ""})`);
|
|
4212
4241
|
if (ctx.hasUI) {
|
|
4213
4242
|
try {
|
|
4214
|
-
const ok = await ctx.ui.confirm("
|
|
4243
|
+
const ok = await ctx.ui.confirm("Wipe glla state?", `${parts.map((p) => ` ${p}`).join("\n")}\n\nHistory stays in .pi-glla (archive + ledger); the live state is wiped.`);
|
|
4215
4244
|
if (!ok) {
|
|
4216
|
-
ctx.ui.notify("
|
|
4245
|
+
ctx.ui.notify("Wipe cancelled.", "info");
|
|
4217
4246
|
return;
|
|
4218
4247
|
}
|
|
4219
4248
|
} catch {
|
|
4220
|
-
ctx.ui.notify("
|
|
4249
|
+
ctx.ui.notify("Wipe cancelled.", "info");
|
|
4221
4250
|
return;
|
|
4222
4251
|
}
|
|
4223
4252
|
}
|
|
4224
|
-
appendLedger(ctx.cwd, "
|
|
4253
|
+
appendLedger(ctx.cwd, "glla_wipe", { goalId: live ? g!.id : undefined, listCleared: n, loop: loop ? { iteration: loop.iteration, active: loop.active } : undefined });
|
|
4225
4254
|
if (live) {
|
|
4226
|
-
archiveCurrentGoal(ctx, "aborted", "user
|
|
4255
|
+
archiveCurrentGoal(ctx, "aborted", "user wipe (/glla wipe)");
|
|
4227
4256
|
ctx.abort();
|
|
4228
4257
|
} else if (g) {
|
|
4229
4258
|
state = { ...state, goal: null };
|
|
4230
4259
|
}
|
|
4231
4260
|
if (n > 0) {
|
|
4232
4261
|
state = { ...state, list: [] };
|
|
4233
|
-
appendLedger(ctx.cwd, "list_cleared", { via: "
|
|
4262
|
+
appendLedger(ctx.cwd, "list_cleared", { via: "glla_wipe" });
|
|
4234
4263
|
}
|
|
4235
4264
|
if (loop) {
|
|
4236
4265
|
clearLoopTimer();
|
|
4237
4266
|
state.loop = undefined;
|
|
4238
4267
|
await finishLoopGit(ctx, loop);
|
|
4239
|
-
appendLedger(ctx.cwd, "loop_stopped", { reason: "user
|
|
4268
|
+
appendLedger(ctx.cwd, "loop_stopped", { reason: "user wipe (/glla wipe)", iterations: loop.iteration, best: loop.bestValue });
|
|
4240
4269
|
}
|
|
4241
4270
|
persistState(ctx);
|
|
4242
|
-
ctx.ui.notify(`glla
|
|
4243
|
-
notifyExternal(ctx, "glla state
|
|
4271
|
+
ctx.ui.notify(`glla wipe done: ${parts.join(" · ")}. Clean slate.`, "info");
|
|
4272
|
+
notifyExternal(ctx, "glla state wiped by user — clean slate.");
|
|
4244
4273
|
}
|
|
4245
4274
|
|
|
4246
4275
|
/**
|
|
@@ -4297,7 +4326,7 @@ async function cmdGllaResume(ctx: ExtensionContext): Promise<void> {
|
|
|
4297
4326
|
* list item is archived as aborted (its queue is untouched), an active or
|
|
4298
4327
|
* held loop is stopped. Same outcome shape regardless of hidden type —
|
|
4299
4328
|
* the user's caveat ("this sucks if one command doesn't work for others")
|
|
4300
|
-
* is why /list cancel (item + drop queue) and /glla
|
|
4329
|
+
* is why /list cancel (item + drop queue) and /glla wipe (nuke all)
|
|
4301
4330
|
* remain the power verbs instead of being folded in.
|
|
4302
4331
|
*/
|
|
4303
4332
|
async function cmdGllaCancel(ctx: ExtensionContext): Promise<void> {
|
|
@@ -4310,7 +4339,7 @@ async function cmdGllaCancel(ctx: ExtensionContext): Promise<void> {
|
|
|
4310
4339
|
await cmdLoop("stop", ctx);
|
|
4311
4340
|
return;
|
|
4312
4341
|
}
|
|
4313
|
-
ctx.ui.notify("Nothing to cancel — no active/paused goal/list-item, no loop. Queued list items: /list clear; everything: /glla
|
|
4342
|
+
ctx.ui.notify("Nothing to cancel — no active/paused goal/list-item, no loop. Queued list items: /list clear; everything: /glla wipe.", "info");
|
|
4314
4343
|
}
|
|
4315
4344
|
|
|
4316
4345
|
function cmdAudits(args: string, ctx: ExtensionContext): void {
|
|
@@ -4370,9 +4399,15 @@ async function cmdSettings(args: string, ctx: ExtensionContext): Promise<void> {
|
|
|
4370
4399
|
cmdLog(trimmed.slice("log".length).trim(), ctx);
|
|
4371
4400
|
return;
|
|
4372
4401
|
}
|
|
4373
|
-
// v0.28.
|
|
4402
|
+
// v0.28.33: renamed reset → wipe — "reset" sat at edit-distance 2 from
|
|
4403
|
+
// "resume" in the same namespace, and it's the destructive one (user
|
|
4404
|
+
// catch, same day it shipped, before any muscle memory formed).
|
|
4405
|
+
if (/^wipe\b/.test(trimmed)) {
|
|
4406
|
+
await cmdGllaWipe(ctx);
|
|
4407
|
+
return;
|
|
4408
|
+
}
|
|
4374
4409
|
if (/^reset\b/.test(trimmed)) {
|
|
4375
|
-
|
|
4410
|
+
ctx.ui.notify("/glla reset is now /glla wipe (renamed — too close to /glla resume). Nothing was done.", "info");
|
|
4376
4411
|
return;
|
|
4377
4412
|
}
|
|
4378
4413
|
// v0.28.32: /glla resume + /glla cancel — type-blind verbs over the ONE
|
|
@@ -4776,9 +4811,9 @@ export default function (pi: ExtensionAPI): void {
|
|
|
4776
4811
|
["decisionpopup=", "on|off: decision pauses pop the select() picker (default on; the widget card always lists the options, /goal decide reopens the picker)"],
|
|
4777
4812
|
["auditcap=", "N: pause goal after N consecutive auditor disapprovals (default 5, 0 = unlimited)"],
|
|
4778
4813
|
["log", "event-trail tail: /glla log [N] — who created/resumed/paused what, from where (v0.28.28)"],
|
|
4779
|
-
["
|
|
4814
|
+
["wipe", "WIPE live glla state (goal archived, list cleared, loop stopped) — one-shot cleanup for leftover-laden projects"],
|
|
4780
4815
|
["resume", "resume WHATEVER is paused/held (goal, list item, or held loop) — no need to know the type"],
|
|
4781
|
-
["cancel", "cancel the ONE live thing uniformly (goal/list item archived, loop stopped) — queue untouched; /list clear or /glla
|
|
4816
|
+
["cancel", "cancel the ONE live thing uniformly (goal/list item archived, loop stopped) — queue untouched; /list clear or /glla wipe for more"],
|
|
4782
4817
|
["auditfeedbackchars=", "cap on executor-visible disapproval report chars (0 = full report, the default)"],
|
|
4783
4818
|
["aggressivemode=", "on: keep-going defaults — autoResume, cap 10, stuck 10, wedge off, quota auto-retry, cap→TODOs"],
|
|
4784
4819
|
["quotaretryminutes=", "N: minutes before auto-retrying a quota-exhausted auditor (default 60)"],
|
|
@@ -323,9 +323,9 @@ export function buildSettingsRows(
|
|
|
323
323
|
id: "notifyCmd",
|
|
324
324
|
section: "other",
|
|
325
325
|
label: "Notify command",
|
|
326
|
-
valueText: show("notifyCmd", "
|
|
326
|
+
valueText: show("notifyCmd", "auto"),
|
|
327
327
|
sourceText: src("notifyCmd"),
|
|
328
|
-
description: "
|
|
328
|
+
description: "custom command ($1 = message) · unset = auto-detect notify-send/osascript · 'off' = silent",
|
|
329
329
|
},
|
|
330
330
|
{
|
|
331
331
|
id: "tokenLimit",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goal-list-loop-audit",
|
|
3
|
-
"version": "0.28.
|
|
3
|
+
"version": "0.28.34",
|
|
4
4
|
"description": "Goal. Loop. Audit. Done. \u2014 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 \u2014 only the read tools needed to verify your goal.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "dracon",
|