pi-goal-list-loop-audit 0.28.27 → 0.28.28
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.
|
@@ -185,6 +185,10 @@ export interface Goal {
|
|
|
185
185
|
* the retry resolves. Only consumed while paused with an "auditor quota:"
|
|
186
186
|
* reason, so a stale value is unreachable by construction. */
|
|
187
187
|
pendingCompletion?: { completionSummary?: string; verificationSummary?: string; at: string };
|
|
188
|
+
/** v0.28.28: provenance — who created this goal ("user", "list-cascade",
|
|
189
|
+
* "draft-confirmed", "draft-autoaccepted"). Ledgered on goal_created so
|
|
190
|
+
* "where did this come from" is answerable after the fact. */
|
|
191
|
+
createdVia?: string;
|
|
188
192
|
/** v0.25.0 (contract item 22): auditor objections extracted as TODOs when
|
|
189
193
|
* aggressiveMode keeps the goal active past the disapproval cap. Rendered
|
|
190
194
|
* into every continuation prompt until the next audit clears them. */
|
package/extensions/loops/goal.ts
CHANGED
|
@@ -895,18 +895,19 @@ function notifyPersistenceState(ctx: ExtensionContext): void {
|
|
|
895
895
|
}
|
|
896
896
|
}
|
|
897
897
|
|
|
898
|
-
function setGoal(goal: Goal, ctx: ExtensionContext): void {
|
|
898
|
+
function setGoal(goal: Goal, ctx: ExtensionContext, via = "user"): void {
|
|
899
899
|
// v0.28.14: never silently orphan a live goal — a paused/active goal
|
|
900
900
|
// being replaced is archived honestly first (the old behavior left it in
|
|
901
901
|
// goals/ but untracked: "older goals lying around leading to confusion").
|
|
902
902
|
if (state.goal && state.goal.id !== goal.id && (state.goal.status === "active" || state.goal.status === "paused")) {
|
|
903
903
|
archiveCurrentGoal(ctx, "aborted", `replaced by goal ${goal.id}`);
|
|
904
904
|
}
|
|
905
|
+
goal.createdVia = via; // v0.28.28: provenance — answerable from the ledger + /glla log
|
|
905
906
|
state = { ...state, goal }; // preserve list AND loop (v0.28.14: the bare reconstruction used to nuke a held/active loop whenever a goal was set)
|
|
906
907
|
const file = writeGoalMd(ctx.cwd, goal);
|
|
907
908
|
state.goal!.activePath = path.relative(ctx.cwd, file) || file;
|
|
908
909
|
persistState(ctx);
|
|
909
|
-
appendLedger(ctx.cwd, "goal_created", { goalId: goal.id, objective: goal.objective, policy: goal.policy });
|
|
910
|
+
appendLedger(ctx.cwd, "goal_created", { goalId: goal.id, objective: goal.objective, policy: goal.policy, via });
|
|
910
911
|
}
|
|
911
912
|
|
|
912
913
|
function updateGoal(patch: Partial<Goal>, ctx: ExtensionContext): void {
|
|
@@ -1142,7 +1143,7 @@ function fireReviewer(
|
|
|
1142
1143
|
manual: opts.manual,
|
|
1143
1144
|
ledgerEntries,
|
|
1144
1145
|
sources,
|
|
1145
|
-
enqueueListItems: (objectives) => enqueueItems(ctx, objectives, "reviewer"),
|
|
1146
|
+
enqueueListItems: (objectives) => enqueueItems(ctx, objectives, "reviewer", { autoActivate: loadSettings(ctx.cwd).autoResume === true }),
|
|
1146
1147
|
proposeGoal: (objective, reason) => {
|
|
1147
1148
|
try {
|
|
1148
1149
|
extensionApi?.sendUserMessage(
|
|
@@ -1212,7 +1213,7 @@ function activateNextListItem(ctx: ExtensionContext, n = 1): boolean {
|
|
|
1212
1213
|
state = { ...state, list: rest };
|
|
1213
1214
|
const goal = createGoal(next.objective, ctx, "list");
|
|
1214
1215
|
if (next.verificationContract) goal.verificationContract = next.verificationContract;
|
|
1215
|
-
setGoal(goal, ctx);
|
|
1216
|
+
setGoal(goal, ctx, "list-cascade");
|
|
1216
1217
|
iterationCounter = 0;
|
|
1217
1218
|
consecutiveErrorIterations = 0;
|
|
1218
1219
|
consecutiveAbortIterations = 0;
|
|
@@ -1631,7 +1632,7 @@ async function cmdTweak(args: string, ctx: ExtensionContext): Promise<void> {
|
|
|
1631
1632
|
* contract extraction) → appended to the queue → persisted → first item
|
|
1632
1633
|
* activated when nothing is running. Returns the count enqueued.
|
|
1633
1634
|
*/
|
|
1634
|
-
function enqueueItems(ctx: ExtensionContext, texts: string[], source: string): number {
|
|
1635
|
+
function enqueueItems(ctx: ExtensionContext, texts: string[], source: string, opts?: { autoActivate?: boolean }): number {
|
|
1635
1636
|
const items = texts.map((text) => {
|
|
1636
1637
|
const extracted = extractVerificationContract(text);
|
|
1637
1638
|
return { id: newGoalId(), objective: extracted.objective, verificationContract: extracted.verificationContract || undefined, addedAt: nowIso() };
|
|
@@ -1640,7 +1641,16 @@ function enqueueItems(ctx: ExtensionContext, texts: string[], source: string): n
|
|
|
1640
1641
|
persistState(ctx);
|
|
1641
1642
|
appendLedger(ctx.cwd, "list_imported", { source, count: items.length });
|
|
1642
1643
|
if (!state.goal || state.goal.status === "complete" || state.goal.status === "aborted") {
|
|
1643
|
-
|
|
1644
|
+
// v0.28.28: unsolicited sources (the reviewer) do NOT auto-start the
|
|
1645
|
+
// head unless autoResume is on — "I cancelled a goal and the next one
|
|
1646
|
+
// started itself" was the field complaint. User-driven imports keep
|
|
1647
|
+
// the immediate-start behavior (opts default true).
|
|
1648
|
+
if (opts?.autoActivate === false) {
|
|
1649
|
+
ctx.ui.notify(`Queued ${items.length} item(s) from ${source} — /list next when ready (auto-start is opt-in: /glla autoresume=on).`, "info");
|
|
1650
|
+
appendLedger(ctx.cwd, "list_autoactivation_held", { source, count: items.length });
|
|
1651
|
+
} else {
|
|
1652
|
+
activateNextListItem(ctx);
|
|
1653
|
+
}
|
|
1644
1654
|
}
|
|
1645
1655
|
return items.length;
|
|
1646
1656
|
}
|
|
@@ -3167,13 +3177,34 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
3167
3177
|
persistState(liveCtx);
|
|
3168
3178
|
appendLedger(liveCtx.cwd, "list_added", { id: item.id, objective: item.objective, drafted: true });
|
|
3169
3179
|
if (!state.goal || state.goal.status === "complete" || state.goal.status === "aborted") {
|
|
3180
|
+
// v0.28.28: an AUTO-ACCEPTED draft does not auto-start unless
|
|
3181
|
+
// autoResume is on — accepting a draft is not consent to start.
|
|
3182
|
+
if (autoAccept && loadSettings(liveCtx.cwd).autoResume !== true) {
|
|
3183
|
+
liveCtx.ui.notify(`Auto-accepted and QUEUED (autoResume off — not auto-started): ${extracted.objective.slice(0, 80)} — /list next when ready.`, "info");
|
|
3184
|
+
appendLedger(liveCtx.cwd, "list_autoactivation_held", { source: "draft-autoaccepted", count: 1 });
|
|
3185
|
+
return { content: [{ type: "text", text: "Draft accepted and added to the list, but NOT started (the user's autoResume setting is off — auto-accepted drafts queue, they don't auto-start). Do NOT begin work. Tell the user: /list next starts it." }], details: {} };
|
|
3186
|
+
}
|
|
3170
3187
|
activateNextListItem(liveCtx);
|
|
3171
3188
|
return { content: [{ type: "text", text: "Confirmed and activated (list was empty). Begin work now." }], details: {} };
|
|
3172
3189
|
}
|
|
3173
3190
|
return { content: [{ type: "text", text: `Confirmed and added to the list (${listQueue().length} waiting). It activates when the current goal completes.` }], details: {} };
|
|
3174
3191
|
}
|
|
3175
3192
|
const goal = createGoal(full, liveCtx);
|
|
3176
|
-
setGoal(goal, liveCtx);
|
|
3193
|
+
setGoal(goal, liveCtx, autoAccept ? "draft-autoaccepted" : "draft-confirmed");
|
|
3194
|
+
// v0.28.28: auto-accepted goal drafts are created HELD when autoResume
|
|
3195
|
+
// is off — auto-accept delegates the Confirm click, not the decision
|
|
3196
|
+
// to start. Explicit user-confirmed drafts still start immediately.
|
|
3197
|
+
if (autoAccept && loadSettings(liveCtx.cwd).autoResume !== true) {
|
|
3198
|
+
updateGoal({
|
|
3199
|
+
status: "paused",
|
|
3200
|
+
pauseKind: "blocked",
|
|
3201
|
+
pauseReason: "auto-accepted draft — held for the user's go-ahead (autoResume off)",
|
|
3202
|
+
pauseSuggestedAction: "/goal resume to start · /goal cancel to drop · /glla autoresume=on starts auto-accepted drafts automatically",
|
|
3203
|
+
}, liveCtx);
|
|
3204
|
+
appendLedger(liveCtx.cwd, "draft_held", { goalId: goal.id, reason: "autoaccept-autoresume-off" });
|
|
3205
|
+
liveCtx.ui.notify(`Draft auto-accepted and HELD (autoResume off): ${goal.objective.slice(0, 80)} — /goal resume to start, /goal cancel to drop.`, "info");
|
|
3206
|
+
return { content: [{ type: "text", text: "Goal accepted but HELD (the user's autoResume setting is off — auto-accepted drafts do not auto-start). Do NOT begin work. Tell the user: /goal resume starts it, /goal cancel drops it." }], details: {} };
|
|
3207
|
+
}
|
|
3177
3208
|
iterationCounter = 0;
|
|
3178
3209
|
consecutiveErrorIterations = 0;
|
|
3179
3210
|
consecutiveAbortIterations = 0;
|
|
@@ -4085,6 +4116,42 @@ function cmdStats(args: string, ctx: ExtensionContext): void {
|
|
|
4085
4116
|
* log (.pi-glla/audits.jsonl). Default: last 10 verdicts, one line each.
|
|
4086
4117
|
* "full" prints the latest report in full.
|
|
4087
4118
|
*/
|
|
4119
|
+
/**
|
|
4120
|
+
* v0.28.28: /glla log [N] — human-readable tail of the event ledger (the
|
|
4121
|
+
* forensic trail: who created/resumed/paused goals, from where). Skips the
|
|
4122
|
+
* high-frequency noise entries (state snapshots, re-arm internals) unless
|
|
4123
|
+
* "all" is passed. N defaults to 15.
|
|
4124
|
+
*/
|
|
4125
|
+
const LOG_NOISE = new Set(["state", "send_rearm_start", "heartbeat_suppressed_tick"]);
|
|
4126
|
+
function cmdLog(args: string, ctx: ExtensionContext): void {
|
|
4127
|
+
const all = /\ball\b/.test(args);
|
|
4128
|
+
const nMatch = args.match(/\b(\d+)\b/);
|
|
4129
|
+
const n = Math.min(Math.max(parseInt(nMatch?.[1] ?? "15", 10) || 15, 1), 100);
|
|
4130
|
+
let entries: Array<{ type: string; at?: string; value?: any }> = [];
|
|
4131
|
+
try {
|
|
4132
|
+
entries = parseLedgerEntries(fs.readFileSync(ledgerPath(ctx.cwd), "utf-8"));
|
|
4133
|
+
} catch {
|
|
4134
|
+
ctx.ui.notify("No ledger yet — .pi-glla/active.jsonl doesn't exist.", "info");
|
|
4135
|
+
return;
|
|
4136
|
+
}
|
|
4137
|
+
const visible = all ? entries : entries.filter((e) => !LOG_NOISE.has(e.type));
|
|
4138
|
+
const tail = visible.slice(-n);
|
|
4139
|
+
if (tail.length === 0) {
|
|
4140
|
+
ctx.ui.notify("Ledger is empty (no non-noise events yet).", "info");
|
|
4141
|
+
return;
|
|
4142
|
+
}
|
|
4143
|
+
const lines = tail.map((e) => {
|
|
4144
|
+
const t = (e.at ?? "").slice(11, 19);
|
|
4145
|
+
const v = e.value ?? {};
|
|
4146
|
+
const detail = Object.entries(v)
|
|
4147
|
+
.filter(([k]) => k !== "goalId" && k !== "report")
|
|
4148
|
+
.map(([k, val]) => `${k}=${typeof val === "string" ? val.slice(0, 60) : JSON.stringify(val)?.slice(0, 60)}`)
|
|
4149
|
+
.join(" ");
|
|
4150
|
+
return `${t} ${e.type}${detail ? ` ${detail}` : ""}`;
|
|
4151
|
+
});
|
|
4152
|
+
ctx.ui.notify(`Ledger tail (last ${tail.length}${all ? "" : " non-noise"} events — /glla log <N> for more, /glla log all to include noise):\n${lines.join("\n")}`, "info");
|
|
4153
|
+
}
|
|
4154
|
+
|
|
4088
4155
|
function cmdAudits(args: string, ctx: ExtensionContext): void {
|
|
4089
4156
|
const full = /\bfull\b/.test(args);
|
|
4090
4157
|
const all = /\b(?:all|global|log)\b/.test(args);
|
|
@@ -4136,6 +4203,12 @@ async function cmdSettings(args: string, ctx: ExtensionContext): Promise<void> {
|
|
|
4136
4203
|
cmdAudits(trimmed.slice("audits".length).trim(), ctx);
|
|
4137
4204
|
return;
|
|
4138
4205
|
}
|
|
4206
|
+
// v0.28.28: /glla log [N] — the raw event trail, human-readable. "Log it
|
|
4207
|
+
// so we can look back and see where we are doing things wrong."
|
|
4208
|
+
if (/^log\b/.test(trimmed)) {
|
|
4209
|
+
cmdLog(trimmed.slice("log".length).trim(), ctx);
|
|
4210
|
+
return;
|
|
4211
|
+
}
|
|
4139
4212
|
if (/^reviewer\b/.test(trimmed)) {
|
|
4140
4213
|
await cmdReviewerSettings(ctx);
|
|
4141
4214
|
return;
|
|
@@ -4526,6 +4599,7 @@ export default function (pi: ExtensionAPI): void {
|
|
|
4526
4599
|
["autoresume=", "default: hold when a session is loaded, auto-resume on reload/fork; on: always auto-resume; off: never"],
|
|
4527
4600
|
["decisionpopup=", "on|off: decision pauses pop the select() picker (default on; the widget card always lists the options, /goal decide reopens the picker)"],
|
|
4528
4601
|
["auditcap=", "N: pause goal after N consecutive auditor disapprovals (default 5, 0 = unlimited)"],
|
|
4602
|
+
["log", "event-trail tail: /glla log [N] — who created/resumed/paused what, from where (v0.28.28)"],
|
|
4529
4603
|
["auditfeedbackchars=", "cap on executor-visible disapproval report chars (0 = full report, the default)"],
|
|
4530
4604
|
["aggressivemode=", "on: keep-going defaults — autoResume, cap 10, stuck 10, wedge off, quota auto-retry, cap→TODOs"],
|
|
4531
4605
|
["quotaretryminutes=", "N: minutes before auto-retrying a quota-exhausted auditor (default 60)"],
|
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.28",
|
|
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",
|
package/schemas/goal.schema.json
CHANGED
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"interruptedReason": { "type": "string" },
|
|
55
55
|
"auditInfraStreak": { "type": "number" },
|
|
56
56
|
"pendingCompletion": { "type": "object" },
|
|
57
|
+
"createdVia": { "type": "string" },
|
|
57
58
|
"activePath": { "type": "string" },
|
|
58
59
|
"archivedPath": { "type": "string" },
|
|
59
60
|
"usage": {
|