pi-goal-list-loop-audit 0.34.6 → 0.34.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 +33 -6
- package/extensions/loops/goal.ts +41 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -312,6 +312,35 @@ stuck backoff caps at 5 minutes then pauses, measure commands get a 10m
|
|
|
312
312
|
hard timeout, and the auditor aborts after 10m with zero session activity
|
|
313
313
|
(infrastructure error, never a verdict).
|
|
314
314
|
|
|
315
|
+
## Recommended companions
|
|
316
|
+
|
|
317
|
+
glla is the goal plane — it drives, verifies, and notifies. It does not
|
|
318
|
+
try to be the whole rig. Four plugins round it out (all optional; glla
|
|
319
|
+
works without any of them):
|
|
320
|
+
|
|
321
|
+
- **`@tintinweb/pi-subagents`** — the `Agent` tool: parallel Explore /
|
|
322
|
+
Plan / general-purpose subagents. glla's prompts teach fan-out with ROI
|
|
323
|
+
(parallelize real work, never ceremony spawning) and brief discipline,
|
|
324
|
+
and big audit collects genuinely assume this exists. glla's subagent
|
|
325
|
+
guarantees (the main session owns the goal; workers can't clobber it)
|
|
326
|
+
are plugin-agnostic, but this is the provider we test against.
|
|
327
|
+
- **`@juicesharp/rpiv-advisor`** — a second opinion the executor model
|
|
328
|
+
can request mid-flight: the whole conversation branch is forwarded to a
|
|
329
|
+
stronger reviewer model, which answers with a plan, a correction, or a
|
|
330
|
+
stop signal. Drive the session with a cheap/fast model and buy strong
|
|
331
|
+
judgment per call. Role clarity: the advisor is *advisory*, never
|
|
332
|
+
verification — glla's isolated auditor remains the only completion
|
|
333
|
+
gate.
|
|
334
|
+
- **`@juicesharp/rpiv-ask-user-question`** — structured questions with
|
|
335
|
+
multi-select and markdown previews. glla's drafting interviews and
|
|
336
|
+
DECIDE findings render through it when installed (plain prompts
|
|
337
|
+
otherwise).
|
|
338
|
+
- **`@pi-unipi/notify`** — push beyond the desktop: Telegram, Gotify,
|
|
339
|
+
ntfy, with per-event routing. glla's built-in pushes cover the local
|
|
340
|
+
desktop case and fire only where there is something to DO; add this
|
|
341
|
+
for away-from-desk alerts — route it to critical events only, or every
|
|
342
|
+
glla pause/verdict pings twice.
|
|
343
|
+
|
|
315
344
|
## Compatibility (what goes well, what conflicts)
|
|
316
345
|
|
|
317
346
|
**The Two-Driver Rule**: any plugin that drives agent turns on `agent_end`
|
|
@@ -325,12 +354,10 @@ contradictory turns. One driver at a time:
|
|
|
325
354
|
- **Installed-but-don't-run-simultaneously**: `@tmustier/pi-ralph-wiggum` —
|
|
326
355
|
fine to keep, never run a ralph loop while a goal/list/loop is active.
|
|
327
356
|
|
|
328
|
-
**Goes well with it**:
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
standalone search skills like `mmx-cli`/`pi-search-skill` are optional
|
|
333
|
-
conveniences for bulk queries, not requirements).
|
|
357
|
+
**Goes well with it**: see **Recommended companions** above. `pi-chrome` too
|
|
358
|
+
(the research/search path for goals — logged-in browsing with no extra
|
|
359
|
+
services; standalone search skills like `mmx-cli`/`pi-search-skill` are
|
|
360
|
+
optional conveniences for bulk queries, not requirements).
|
|
334
361
|
|
|
335
362
|
**Overlaps — pick one**: `@tintinweb/pi-tasks` is a second task list next to
|
|
336
363
|
`/list`, and in practice the glla list *is* the task list (queue, statuses,
|
package/extensions/loops/goal.ts
CHANGED
|
@@ -411,6 +411,26 @@ function probeExtensionApiStale(): boolean {
|
|
|
411
411
|
return extensionApiStale;
|
|
412
412
|
}
|
|
413
413
|
|
|
414
|
+
/** v0.34.7: orchestrator-path sendUserMessage that can NEVER crash the
|
|
415
|
+
* process. Darklord 2026-08-01: fanOutListAuditFindings ran on a stale
|
|
416
|
+
* handle (a /reload landed mid-collect), assertActive threw, the floating
|
|
417
|
+
* promise from sync archiveCurrentGoal turned it into an uncaughtException
|
|
418
|
+
* and pi EXITED mid-audit. Probe first, catch anyway, ledger the skip. */
|
|
419
|
+
function safeSteerUser(ctx: ExtensionContext, text: string): boolean {
|
|
420
|
+
if (probeExtensionApiStale()) {
|
|
421
|
+
appendLedger(ctx.cwd, "steer_skipped_stale", { chars: text.length });
|
|
422
|
+
return false;
|
|
423
|
+
}
|
|
424
|
+
try {
|
|
425
|
+
extensionApi?.sendUserMessage(text, { deliverAs: ctx.isIdle() ? "followUp" : "steer" });
|
|
426
|
+
return true;
|
|
427
|
+
} catch (err) {
|
|
428
|
+
if (isStaleApiError(err)) extensionApiStale = true;
|
|
429
|
+
appendLedger(ctx.cwd, "steer_skipped_stale", { chars: text.length, threw: true });
|
|
430
|
+
return false;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
414
434
|
/** v0.28.1 (S3): command-entry staleness probe + honest warning. Returns
|
|
415
435
|
* true when the handle is stale — callers must skip send-dependent paths
|
|
416
436
|
* and must NOT claim work started (S3's "created — starting now" lie). */
|
|
@@ -1407,11 +1427,9 @@ async function fanOutListAuditFindings(ctx: ExtensionContext): Promise<void> {
|
|
|
1407
1427
|
// queued or the fan-out was declined.
|
|
1408
1428
|
if (decisions.length > 0) {
|
|
1409
1429
|
const decList = decisions.slice(0, 8).map((d, i) => `${i + 1}. ${d.slice(0, 500)}`).join("\n");
|
|
1410
|
-
|
|
1411
|
-
`[DECIDE FINDINGS — user decisions needed] The audit surfaced ${decisions.length} DECIDE finding(s) — direction calls only the user can make (a decision is not a task, so they were NOT queued):\n${decList}\nRaise them to the user NOW with ask_user_question — one question per finding, options from the finding's own two sides plus "Defer" (prose numbered list if ask_user_question is unavailable; Esc = Defer). Then record every answer in ${AUDIT_FINDINGS_REL}: replace the "- [?]" line with "- [x] DECIDED: <what was chosen> (<date>)" (or "- [x] DEFERRED") so it stops re-surfacing, and queue any chosen work with list_add — do NOT start the work inline
|
|
1412
|
-
|
|
1413
|
-
);
|
|
1414
|
-
appendLedger(ctx.cwd, "list_audit_decisions_raised", { decisions: decisions.length });
|
|
1430
|
+
if (safeSteerUser(ctx,
|
|
1431
|
+
`[DECIDE FINDINGS — user decisions needed] The audit surfaced ${decisions.length} DECIDE finding(s) — direction calls only the user can make (a decision is not a task, so they were NOT queued):\n${decList}\nRaise them to the user NOW with ask_user_question — one question per finding, options from the finding's own two sides plus "Defer" (prose numbered list if ask_user_question is unavailable; Esc = Defer). Then record every answer in ${AUDIT_FINDINGS_REL}: replace the "- [?]" line with "- [x] DECIDED: <what was chosen> (<date>)" (or "- [x] DEFERRED") so it stops re-surfacing, and queue any chosen work with list_add — do NOT start the work inline.`))
|
|
1432
|
+
appendLedger(ctx.cwd, "list_audit_decisions_raised", { decisions: decisions.length });
|
|
1415
1433
|
}
|
|
1416
1434
|
const decideNote =
|
|
1417
1435
|
decisions.length > 0
|
|
@@ -1481,7 +1499,12 @@ function archiveCurrentGoal(ctx: ExtensionContext, status: Status, stopReason?:
|
|
|
1481
1499
|
// was empty, enqueueItems activates the first fix itself, so the
|
|
1482
1500
|
// list-complete / reviewer noise below must NOT fire for this item.
|
|
1483
1501
|
const isListAuditCollect = goal.objective.includes(LIST_AUDIT_COLLECT_MARKER);
|
|
1484
|
-
|
|
1502
|
+
// v0.34.7: the float gets a catch — ANY rejection here used to become
|
|
1503
|
+
// an uncaughtException and kill pi (darklord 2026-08-01).
|
|
1504
|
+
if (isListAuditCollect)
|
|
1505
|
+
void fanOutListAuditFindings(ctx).catch((err) => {
|
|
1506
|
+
appendLedger(ctx.cwd, "list_audit_fanout_error", { error: String(err).slice(0, 200) });
|
|
1507
|
+
});
|
|
1485
1508
|
const advanced = activateNextListItem(ctx);
|
|
1486
1509
|
// v0.26.0: the queue just EMPTIED on a completion → list-complete.
|
|
1487
1510
|
if (!advanced && !isListAuditCollect) {
|
|
@@ -1699,10 +1722,8 @@ function fireReviewer(
|
|
|
1699
1722
|
enqueueListItems: (objectives) => enqueueItems(ctx, objectives, "reviewer", { autoActivate: loadGlobalSettings().autoResume === true }),
|
|
1700
1723
|
proposeGoal: (objective, reason) => {
|
|
1701
1724
|
try {
|
|
1702
|
-
|
|
1703
|
-
`[REVIEWER FOLLOW-UP — ${reason}. Propose this as a /goal via propose_goal_draft (the user Confirms or rejects): ${objective}]
|
|
1704
|
-
{ deliverAs: ctx.isIdle() ? "followUp" : "steer" },
|
|
1705
|
-
);
|
|
1725
|
+
safeSteerUser(ctx,
|
|
1726
|
+
`[REVIEWER FOLLOW-UP — ${reason}. Propose this as a /goal via propose_goal_draft (the user Confirms or rejects): ${objective}]`);
|
|
1706
1727
|
return true;
|
|
1707
1728
|
} catch (err) {
|
|
1708
1729
|
// v0.28.8 (E4): the phantom-reviewer hole — a swallowed throw used
|
|
@@ -1833,7 +1854,7 @@ async function startDrafting(ctx: ExtensionContext, target: "goal" | "list" | "l
|
|
|
1833
1854
|
}
|
|
1834
1855
|
}
|
|
1835
1856
|
try {
|
|
1836
|
-
|
|
1857
|
+
safeSteerUser(ctx, tmpl);
|
|
1837
1858
|
draftingUserReplies = 0;
|
|
1838
1859
|
draftingBlockedProposals = 0;
|
|
1839
1860
|
draftingSeedInFlight = true; // our injected prompt also arrives as a user message — don't count it
|
|
@@ -2025,6 +2046,7 @@ async function cmdResume(ctx: ExtensionContext): Promise<void> {
|
|
|
2025
2046
|
return;
|
|
2026
2047
|
}
|
|
2027
2048
|
appendLedger(ctx.cwd, "resume_rekick", { goalId: state.goal.id, policy: state.goal.policy, via: "/goal resume" });
|
|
2049
|
+
if (state.goal.interruptedAt) updateGoal({ interruptedAt: undefined, interruptedReason: undefined }, ctx); // v0.34.7: same marker law here
|
|
2028
2050
|
ctx.ui.notify(
|
|
2029
2051
|
`The ${state.goal.policy === "list" ? "list item" : "goal"} is ACTIVE but idle — re-firing its continuation: ${state.goal.objective.replace(/\s+/g, " ").slice(0, 70)}`,
|
|
2030
2052
|
"info",
|
|
@@ -2134,13 +2156,13 @@ async function showDecisionPrompt(ctx: ExtensionContext): Promise<boolean> {
|
|
|
2134
2156
|
else if (group === "loop" && verb === "stop") await cmdLoop("stop", ctx);
|
|
2135
2157
|
else if (group === "loop" && verb === "resume") await cmdLoop("resume", ctx);
|
|
2136
2158
|
else {
|
|
2137
|
-
|
|
2159
|
+
safeSteerUser(ctx, `Decision for the paused goal "${g.objective}": ${label} — continue on this path.`);
|
|
2138
2160
|
await cmdResume(ctx);
|
|
2139
2161
|
}
|
|
2140
2162
|
return true;
|
|
2141
2163
|
}
|
|
2142
2164
|
// Content choice — deliver to the agent, then resume.
|
|
2143
|
-
|
|
2165
|
+
safeSteerUser(ctx, `Decision for the paused goal "${g.objective}": ${label} — continue on this path.`);
|
|
2144
2166
|
await cmdResume(ctx);
|
|
2145
2167
|
return true;
|
|
2146
2168
|
} finally {
|
|
@@ -5352,6 +5374,12 @@ async function cmdGllaResume(ctx: ExtensionContext): Promise<void> {
|
|
|
5352
5374
|
// resume"). Re-kick the continuation instead of shrugging.
|
|
5353
5375
|
if (g && g.status === "active") {
|
|
5354
5376
|
appendLedger(ctx.cwd, "resume_rekick", { goalId: g.id, policy: g.policy });
|
|
5377
|
+
// v0.34.7: the re-kick fulfills the stale-handle marker's promise too
|
|
5378
|
+
// (junk-runner/polis/neonbreak 2026-08-01: actively working with the
|
|
5379
|
+
// ⚠ interrupted banner still screaming — the v0.34.2 clear only lived
|
|
5380
|
+
// in the paused-resume path; the staleness entry-guard above already
|
|
5381
|
+
// filtered out a stale session reaching this branch).
|
|
5382
|
+
if (g.interruptedAt) updateGoal({ interruptedAt: undefined, interruptedReason: undefined }, ctx);
|
|
5355
5383
|
ctx.ui.notify(
|
|
5356
5384
|
`The ${g.policy === "list" ? "list item" : "goal"} is ACTIVE but idle — re-firing its continuation: ${g.objective.replace(/\s+/g, " ").slice(0, 70)}`,
|
|
5357
5385
|
"info",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goal-list-loop-audit",
|
|
3
|
-
"version": "0.34.
|
|
3
|
+
"version": "0.34.8",
|
|
4
4
|
"description": "Mission control for autonomous pi: interview-drafted goals, an audited task queue, and forever-loops (metric, spec, project-audit) that run for hours. An isolated extension-less auditor re-verifies every completion with raw evidence; confirmed drafts, decision pauses and consent gates keep you in charge.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "dracon",
|