pi-goal-list-loop-audit 0.28.27 → 0.28.29
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/extensions/goal-loop-core.ts +4 -0
- package/extensions/loops/goal.ts +135 -31
- package/package.json +1 -1
- package/schemas/goal.schema.json +1 -0
|
@@ -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
|
@@ -482,35 +482,65 @@ const COMPACTION_GRACE_MS = 3 * 60_000;
|
|
|
482
482
|
// brake (1m, 2m, 4m, 8m, 16m cap). A successful turn resets both.
|
|
483
483
|
const ERROR_RETRY_LADDER_MS = [5_000, 15_000, 45_000, 90_000, 180_000];
|
|
484
484
|
let errorBrakeStreak = 0;
|
|
485
|
-
const
|
|
486
|
-
|
|
485
|
+
const SEND_REARM_LEDGER_MILESTONES_MS = [2 * 60_000, 5 * 60_000, 10 * 60_000];
|
|
486
|
+
// v0.28.29: escalation is TIME-based and ACTIVITY-gated. A busy session is
|
|
487
|
+
// NORMAL — the user conversing, or one long subagent turn — and the old
|
|
488
|
+
// flat-50ms × 6000-count rule misread 5 minutes of busy as "wedged" and
|
|
489
|
+
// paused the goal (the polis field report). Escalate only after 15 minutes
|
|
490
|
+
// of failed sends AND no session activity in the last 5 minutes (a wedged
|
|
491
|
+
// queue shows no events at all; a busy one streams constantly).
|
|
492
|
+
const SEND_REARM_ESCALATE_AFTER_MS = 15 * 60_000;
|
|
493
|
+
const SEND_REARM_ESCALATE_SILENT_MS = 5 * 60_000;
|
|
494
|
+
let continuationRearmSince = 0;
|
|
495
|
+
let loopRearmSince = 0;
|
|
496
|
+
let continuationRearmMilestone = 0;
|
|
497
|
+
let loopRearmMilestone = 0;
|
|
498
|
+
|
|
499
|
+
/** v0.28.29: busy-retry cadence backs off — 50ms for the first beats
|
|
500
|
+
* (instant pickup right after a turn ends), then 250ms, 1s, 5s, 15s, 30s
|
|
501
|
+
* cap. agent_end reschedules independently, so the slow tail costs nothing
|
|
502
|
+
* in the common case; it only caps the ledger/CPU spam of a long busy stretch. */
|
|
503
|
+
function sendRearmDelayMs(streak: number): number {
|
|
504
|
+
if (streak <= 4) return 50;
|
|
505
|
+
if (streak <= 8) return 250;
|
|
506
|
+
if (streak <= 12) return 1_000;
|
|
507
|
+
if (streak === 13) return 5_000;
|
|
508
|
+
if (streak === 14) return 15_000;
|
|
509
|
+
return 30_000;
|
|
510
|
+
}
|
|
487
511
|
|
|
488
512
|
function accountSendRearm(ctx: ExtensionContext, kind: "continuation" | "loop"): void {
|
|
489
513
|
const streak = kind === "continuation" ? ++continuationRearmStreak : ++loopRearmStreak;
|
|
490
514
|
if (streak === 1) {
|
|
515
|
+
if (kind === "continuation") { continuationRearmSince = Date.now(); continuationRearmMilestone = 0; } else { loopRearmSince = Date.now(); loopRearmMilestone = 0; }
|
|
491
516
|
appendLedger(ctx.cwd, "send_rearm_start", { kind });
|
|
492
517
|
return;
|
|
493
518
|
}
|
|
494
|
-
|
|
495
|
-
|
|
519
|
+
const since = kind === "continuation" ? continuationRearmSince : loopRearmSince;
|
|
520
|
+
const elapsed = Date.now() - since;
|
|
521
|
+
const milestone = kind === "continuation" ? continuationRearmMilestone : loopRearmMilestone;
|
|
522
|
+
if (milestone < SEND_REARM_LEDGER_MILESTONES_MS.length && elapsed >= SEND_REARM_LEDGER_MILESTONES_MS[milestone]!) {
|
|
523
|
+
if (kind === "continuation") continuationRearmMilestone++; else loopRearmMilestone++;
|
|
524
|
+
appendLedger(ctx.cwd, "send_rearm_storm", { kind, streak, minutes: Math.round(elapsed / 60000) });
|
|
496
525
|
}
|
|
497
|
-
if (
|
|
498
|
-
if (kind === "continuation") continuationRearmStreak = 0; else loopRearmStreak = 0;
|
|
526
|
+
if (elapsed >= SEND_REARM_ESCALATE_AFTER_MS && Date.now() - lastActivityAt >= SEND_REARM_ESCALATE_SILENT_MS) {
|
|
527
|
+
if (kind === "continuation") { continuationRearmStreak = 0; continuationRearmSince = 0; } else { loopRearmStreak = 0; loopRearmSince = 0; }
|
|
499
528
|
escalateSendRearmStorm(ctx, kind);
|
|
500
529
|
}
|
|
501
530
|
}
|
|
502
531
|
|
|
503
532
|
function escalateSendRearmStorm(ctx: ExtensionContext, kind: "continuation" | "loop"): void {
|
|
504
|
-
// Same loud-terminal shape as escalateStallNow (v0.24.7):
|
|
505
|
-
//
|
|
506
|
-
//
|
|
507
|
-
const mins = Math.round(
|
|
508
|
-
|
|
533
|
+
// Same loud-terminal shape as escalateStallNow (v0.24.7). v0.28.29: this
|
|
534
|
+
// only fires on a REAL wedge now (15m of failed sends + 5m of zero
|
|
535
|
+
// session activity) — busy-but-alive sessions never reach it.
|
|
536
|
+
const mins = Math.round(SEND_REARM_ESCALATE_AFTER_MS / 60000);
|
|
537
|
+
const silent = Math.round(SEND_REARM_ESCALATE_SILENT_MS / 60000);
|
|
538
|
+
appendLedger(ctx.cwd, "send_rearm_escalated", { kind, afterMinutes: mins, silentMinutes: silent });
|
|
509
539
|
if (kind === "loop" && isLoopActive()) {
|
|
510
540
|
clearLoopTimer();
|
|
511
|
-
state.loop = { ...state.loop!, active: false, stopReason: `send-retry storm: ${mins}m of
|
|
541
|
+
state.loop = { ...state.loop!, active: false, stopReason: `send-retry storm: ${mins}m of re-arms with no session activity for ${silent}m — the session is wedged. Restart pi, then /loop start again.` };
|
|
512
542
|
persistState(ctx);
|
|
513
|
-
ctx.ui.notify(`Loop stopped: send-retry storm (${mins}m). Restart pi and /loop start.`, "warning");
|
|
543
|
+
ctx.ui.notify(`Loop stopped: send-retry storm (${mins}m, session silent ${silent}m). Restart pi and /loop start.`, "warning");
|
|
514
544
|
notifyExternal(ctx, "Loop stopped: send-retry storm.");
|
|
515
545
|
return;
|
|
516
546
|
}
|
|
@@ -518,10 +548,10 @@ function escalateSendRearmStorm(ctx: ExtensionContext, kind: "continuation" | "l
|
|
|
518
548
|
updateGoal({
|
|
519
549
|
status: "paused",
|
|
520
550
|
pauseKind: "error",
|
|
521
|
-
pauseReason: `send-retry storm: ${mins}m of
|
|
522
|
-
pauseSuggestedAction: "The session
|
|
551
|
+
pauseReason: `send-retry storm: ${mins}m of re-arms with no session activity for ${silent}m — the session never went idle for the continuation`,
|
|
552
|
+
pauseSuggestedAction: "The session produced no events while the send retried (wedged queue). Restart pi, then /goal resume.",
|
|
523
553
|
}, ctx);
|
|
524
|
-
ctx.ui.notify(`Goal paused: send-retry storm (${mins}m). Restart pi, then /goal resume.`, "warning");
|
|
554
|
+
ctx.ui.notify(`Goal paused: send-retry storm (${mins}m, session silent ${silent}m). Restart pi, then /goal resume.`, "warning");
|
|
525
555
|
notifyExternal(ctx, "Goal paused: send-retry storm.");
|
|
526
556
|
}
|
|
527
557
|
}
|
|
@@ -736,10 +766,10 @@ function sendContinuation(goalId: string): void {
|
|
|
736
766
|
return;
|
|
737
767
|
}
|
|
738
768
|
if (!ctx.isIdle() || ctx.hasPendingMessages()) {
|
|
739
|
-
// v0.28.5 (E3): count + ledger + escalate the re-arm storm.
|
|
740
769
|
accountSendRearm(ctx, "continuation");
|
|
741
770
|
continuationScheduledFor = goalId;
|
|
742
|
-
|
|
771
|
+
// v0.28.29: backing-off cadence (was flat 50ms — 6,000 spins in 5m).
|
|
772
|
+
continuationTimer = setTimeout(() => sendContinuation(goalId), sendRearmDelayMs(continuationRearmStreak));
|
|
743
773
|
continuationTimer.unref?.();
|
|
744
774
|
return;
|
|
745
775
|
}
|
|
@@ -750,7 +780,7 @@ function sendContinuation(goalId: string): void {
|
|
|
750
780
|
content: continuationPrompt(state.goal!),
|
|
751
781
|
display: false,
|
|
752
782
|
}, { triggerTurn: true, deliverAs: "followUp" });
|
|
753
|
-
continuationRearmStreak = 0; // v0.28.5 (E3): a landed send clears the storm
|
|
783
|
+
continuationRearmStreak = 0; continuationRearmSince = 0; // v0.28.5 (E3): a landed send clears the storm
|
|
754
784
|
appendLedger(ctx.cwd, "goal_continuation_sent", { goalId });
|
|
755
785
|
} catch (err) {
|
|
756
786
|
appendLedger(ctx.cwd, "goal_continuation_send_failed", { goalId, error: err instanceof Error ? err.message : String(err) });
|
|
@@ -895,18 +925,19 @@ function notifyPersistenceState(ctx: ExtensionContext): void {
|
|
|
895
925
|
}
|
|
896
926
|
}
|
|
897
927
|
|
|
898
|
-
function setGoal(goal: Goal, ctx: ExtensionContext): void {
|
|
928
|
+
function setGoal(goal: Goal, ctx: ExtensionContext, via = "user"): void {
|
|
899
929
|
// v0.28.14: never silently orphan a live goal — a paused/active goal
|
|
900
930
|
// being replaced is archived honestly first (the old behavior left it in
|
|
901
931
|
// goals/ but untracked: "older goals lying around leading to confusion").
|
|
902
932
|
if (state.goal && state.goal.id !== goal.id && (state.goal.status === "active" || state.goal.status === "paused")) {
|
|
903
933
|
archiveCurrentGoal(ctx, "aborted", `replaced by goal ${goal.id}`);
|
|
904
934
|
}
|
|
935
|
+
goal.createdVia = via; // v0.28.28: provenance — answerable from the ledger + /glla log
|
|
905
936
|
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
937
|
const file = writeGoalMd(ctx.cwd, goal);
|
|
907
938
|
state.goal!.activePath = path.relative(ctx.cwd, file) || file;
|
|
908
939
|
persistState(ctx);
|
|
909
|
-
appendLedger(ctx.cwd, "goal_created", { goalId: goal.id, objective: goal.objective, policy: goal.policy });
|
|
940
|
+
appendLedger(ctx.cwd, "goal_created", { goalId: goal.id, objective: goal.objective, policy: goal.policy, via });
|
|
910
941
|
}
|
|
911
942
|
|
|
912
943
|
function updateGoal(patch: Partial<Goal>, ctx: ExtensionContext): void {
|
|
@@ -1142,7 +1173,7 @@ function fireReviewer(
|
|
|
1142
1173
|
manual: opts.manual,
|
|
1143
1174
|
ledgerEntries,
|
|
1144
1175
|
sources,
|
|
1145
|
-
enqueueListItems: (objectives) => enqueueItems(ctx, objectives, "reviewer"),
|
|
1176
|
+
enqueueListItems: (objectives) => enqueueItems(ctx, objectives, "reviewer", { autoActivate: loadSettings(ctx.cwd).autoResume === true }),
|
|
1146
1177
|
proposeGoal: (objective, reason) => {
|
|
1147
1178
|
try {
|
|
1148
1179
|
extensionApi?.sendUserMessage(
|
|
@@ -1212,7 +1243,7 @@ function activateNextListItem(ctx: ExtensionContext, n = 1): boolean {
|
|
|
1212
1243
|
state = { ...state, list: rest };
|
|
1213
1244
|
const goal = createGoal(next.objective, ctx, "list");
|
|
1214
1245
|
if (next.verificationContract) goal.verificationContract = next.verificationContract;
|
|
1215
|
-
setGoal(goal, ctx);
|
|
1246
|
+
setGoal(goal, ctx, "list-cascade");
|
|
1216
1247
|
iterationCounter = 0;
|
|
1217
1248
|
consecutiveErrorIterations = 0;
|
|
1218
1249
|
consecutiveAbortIterations = 0;
|
|
@@ -1631,7 +1662,7 @@ async function cmdTweak(args: string, ctx: ExtensionContext): Promise<void> {
|
|
|
1631
1662
|
* contract extraction) → appended to the queue → persisted → first item
|
|
1632
1663
|
* activated when nothing is running. Returns the count enqueued.
|
|
1633
1664
|
*/
|
|
1634
|
-
function enqueueItems(ctx: ExtensionContext, texts: string[], source: string): number {
|
|
1665
|
+
function enqueueItems(ctx: ExtensionContext, texts: string[], source: string, opts?: { autoActivate?: boolean }): number {
|
|
1635
1666
|
const items = texts.map((text) => {
|
|
1636
1667
|
const extracted = extractVerificationContract(text);
|
|
1637
1668
|
return { id: newGoalId(), objective: extracted.objective, verificationContract: extracted.verificationContract || undefined, addedAt: nowIso() };
|
|
@@ -1640,7 +1671,16 @@ function enqueueItems(ctx: ExtensionContext, texts: string[], source: string): n
|
|
|
1640
1671
|
persistState(ctx);
|
|
1641
1672
|
appendLedger(ctx.cwd, "list_imported", { source, count: items.length });
|
|
1642
1673
|
if (!state.goal || state.goal.status === "complete" || state.goal.status === "aborted") {
|
|
1643
|
-
|
|
1674
|
+
// v0.28.28: unsolicited sources (the reviewer) do NOT auto-start the
|
|
1675
|
+
// head unless autoResume is on — "I cancelled a goal and the next one
|
|
1676
|
+
// started itself" was the field complaint. User-driven imports keep
|
|
1677
|
+
// the immediate-start behavior (opts default true).
|
|
1678
|
+
if (opts?.autoActivate === false) {
|
|
1679
|
+
ctx.ui.notify(`Queued ${items.length} item(s) from ${source} — /list next when ready (auto-start is opt-in: /glla autoresume=on).`, "info");
|
|
1680
|
+
appendLedger(ctx.cwd, "list_autoactivation_held", { source, count: items.length });
|
|
1681
|
+
} else {
|
|
1682
|
+
activateNextListItem(ctx);
|
|
1683
|
+
}
|
|
1644
1684
|
}
|
|
1645
1685
|
return items.length;
|
|
1646
1686
|
}
|
|
@@ -2001,8 +2041,8 @@ function sendLoopTurn(): void {
|
|
|
2001
2041
|
if (!isLoopActive() || !extensionApi) return;
|
|
2002
2042
|
const ctx = freshCtx();
|
|
2003
2043
|
if (!ctx || !ctx.isIdle() || ctx.hasPendingMessages()) {
|
|
2004
|
-
if (ctx) accountSendRearm(ctx, "loop");
|
|
2005
|
-
loopTimer = setTimeout(() => sendLoopTurn(),
|
|
2044
|
+
if (ctx) accountSendRearm(ctx, "loop");
|
|
2045
|
+
loopTimer = setTimeout(() => sendLoopTurn(), sendRearmDelayMs(loopRearmStreak)); // v0.28.29: backing-off cadence
|
|
2006
2046
|
loopTimer.unref?.();
|
|
2007
2047
|
return;
|
|
2008
2048
|
}
|
|
@@ -2049,7 +2089,7 @@ function sendLoopTurn(): void {
|
|
|
2049
2089
|
}, { triggerTurn: true, deliverAs: "followUp" });
|
|
2050
2090
|
// v0.26.1: the send path is ledgered — the hegemon zombie spun 619
|
|
2051
2091
|
// refires with zero visibility into whether sends were landing.
|
|
2052
|
-
loopRearmStreak = 0; // v0.28.5 (E3): a landed turn clears the storm
|
|
2092
|
+
loopRearmStreak = 0; loopRearmSince = 0; // v0.28.5 (E3): a landed turn clears the storm
|
|
2053
2093
|
appendLedger(ctx.cwd, "loop_turn_sent", { iteration: loop.iteration });
|
|
2054
2094
|
} catch (err) {
|
|
2055
2095
|
// stale API — next agent_end reschedules (but if none comes, the
|
|
@@ -3167,13 +3207,34 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
3167
3207
|
persistState(liveCtx);
|
|
3168
3208
|
appendLedger(liveCtx.cwd, "list_added", { id: item.id, objective: item.objective, drafted: true });
|
|
3169
3209
|
if (!state.goal || state.goal.status === "complete" || state.goal.status === "aborted") {
|
|
3210
|
+
// v0.28.28: an AUTO-ACCEPTED draft does not auto-start unless
|
|
3211
|
+
// autoResume is on — accepting a draft is not consent to start.
|
|
3212
|
+
if (autoAccept && loadSettings(liveCtx.cwd).autoResume !== true) {
|
|
3213
|
+
liveCtx.ui.notify(`Auto-accepted and QUEUED (autoResume off — not auto-started): ${extracted.objective.slice(0, 80)} — /list next when ready.`, "info");
|
|
3214
|
+
appendLedger(liveCtx.cwd, "list_autoactivation_held", { source: "draft-autoaccepted", count: 1 });
|
|
3215
|
+
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: {} };
|
|
3216
|
+
}
|
|
3170
3217
|
activateNextListItem(liveCtx);
|
|
3171
3218
|
return { content: [{ type: "text", text: "Confirmed and activated (list was empty). Begin work now." }], details: {} };
|
|
3172
3219
|
}
|
|
3173
3220
|
return { content: [{ type: "text", text: `Confirmed and added to the list (${listQueue().length} waiting). It activates when the current goal completes.` }], details: {} };
|
|
3174
3221
|
}
|
|
3175
3222
|
const goal = createGoal(full, liveCtx);
|
|
3176
|
-
setGoal(goal, liveCtx);
|
|
3223
|
+
setGoal(goal, liveCtx, autoAccept ? "draft-autoaccepted" : "draft-confirmed");
|
|
3224
|
+
// v0.28.28: auto-accepted goal drafts are created HELD when autoResume
|
|
3225
|
+
// is off — auto-accept delegates the Confirm click, not the decision
|
|
3226
|
+
// to start. Explicit user-confirmed drafts still start immediately.
|
|
3227
|
+
if (autoAccept && loadSettings(liveCtx.cwd).autoResume !== true) {
|
|
3228
|
+
updateGoal({
|
|
3229
|
+
status: "paused",
|
|
3230
|
+
pauseKind: "blocked",
|
|
3231
|
+
pauseReason: "auto-accepted draft — held for the user's go-ahead (autoResume off)",
|
|
3232
|
+
pauseSuggestedAction: "/goal resume to start · /goal cancel to drop · /glla autoresume=on starts auto-accepted drafts automatically",
|
|
3233
|
+
}, liveCtx);
|
|
3234
|
+
appendLedger(liveCtx.cwd, "draft_held", { goalId: goal.id, reason: "autoaccept-autoresume-off" });
|
|
3235
|
+
liveCtx.ui.notify(`Draft auto-accepted and HELD (autoResume off): ${goal.objective.slice(0, 80)} — /goal resume to start, /goal cancel to drop.`, "info");
|
|
3236
|
+
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: {} };
|
|
3237
|
+
}
|
|
3177
3238
|
iterationCounter = 0;
|
|
3178
3239
|
consecutiveErrorIterations = 0;
|
|
3179
3240
|
consecutiveAbortIterations = 0;
|
|
@@ -4085,6 +4146,42 @@ function cmdStats(args: string, ctx: ExtensionContext): void {
|
|
|
4085
4146
|
* log (.pi-glla/audits.jsonl). Default: last 10 verdicts, one line each.
|
|
4086
4147
|
* "full" prints the latest report in full.
|
|
4087
4148
|
*/
|
|
4149
|
+
/**
|
|
4150
|
+
* v0.28.28: /glla log [N] — human-readable tail of the event ledger (the
|
|
4151
|
+
* forensic trail: who created/resumed/paused goals, from where). Skips the
|
|
4152
|
+
* high-frequency noise entries (state snapshots, re-arm internals) unless
|
|
4153
|
+
* "all" is passed. N defaults to 15.
|
|
4154
|
+
*/
|
|
4155
|
+
const LOG_NOISE = new Set(["state", "send_rearm_start", "heartbeat_suppressed_tick"]);
|
|
4156
|
+
function cmdLog(args: string, ctx: ExtensionContext): void {
|
|
4157
|
+
const all = /\ball\b/.test(args);
|
|
4158
|
+
const nMatch = args.match(/\b(\d+)\b/);
|
|
4159
|
+
const n = Math.min(Math.max(parseInt(nMatch?.[1] ?? "15", 10) || 15, 1), 100);
|
|
4160
|
+
let entries: Array<{ type: string; at?: string; value?: any }> = [];
|
|
4161
|
+
try {
|
|
4162
|
+
entries = parseLedgerEntries(fs.readFileSync(ledgerPath(ctx.cwd), "utf-8"));
|
|
4163
|
+
} catch {
|
|
4164
|
+
ctx.ui.notify("No ledger yet — .pi-glla/active.jsonl doesn't exist.", "info");
|
|
4165
|
+
return;
|
|
4166
|
+
}
|
|
4167
|
+
const visible = all ? entries : entries.filter((e) => !LOG_NOISE.has(e.type));
|
|
4168
|
+
const tail = visible.slice(-n);
|
|
4169
|
+
if (tail.length === 0) {
|
|
4170
|
+
ctx.ui.notify("Ledger is empty (no non-noise events yet).", "info");
|
|
4171
|
+
return;
|
|
4172
|
+
}
|
|
4173
|
+
const lines = tail.map((e) => {
|
|
4174
|
+
const t = (e.at ?? "").slice(11, 19);
|
|
4175
|
+
const v = e.value ?? {};
|
|
4176
|
+
const detail = Object.entries(v)
|
|
4177
|
+
.filter(([k]) => k !== "goalId" && k !== "report")
|
|
4178
|
+
.map(([k, val]) => `${k}=${typeof val === "string" ? val.slice(0, 60) : JSON.stringify(val)?.slice(0, 60)}`)
|
|
4179
|
+
.join(" ");
|
|
4180
|
+
return `${t} ${e.type}${detail ? ` ${detail}` : ""}`;
|
|
4181
|
+
});
|
|
4182
|
+
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");
|
|
4183
|
+
}
|
|
4184
|
+
|
|
4088
4185
|
function cmdAudits(args: string, ctx: ExtensionContext): void {
|
|
4089
4186
|
const full = /\bfull\b/.test(args);
|
|
4090
4187
|
const all = /\b(?:all|global|log)\b/.test(args);
|
|
@@ -4136,6 +4233,12 @@ async function cmdSettings(args: string, ctx: ExtensionContext): Promise<void> {
|
|
|
4136
4233
|
cmdAudits(trimmed.slice("audits".length).trim(), ctx);
|
|
4137
4234
|
return;
|
|
4138
4235
|
}
|
|
4236
|
+
// v0.28.28: /glla log [N] — the raw event trail, human-readable. "Log it
|
|
4237
|
+
// so we can look back and see where we are doing things wrong."
|
|
4238
|
+
if (/^log\b/.test(trimmed)) {
|
|
4239
|
+
cmdLog(trimmed.slice("log".length).trim(), ctx);
|
|
4240
|
+
return;
|
|
4241
|
+
}
|
|
4139
4242
|
if (/^reviewer\b/.test(trimmed)) {
|
|
4140
4243
|
await cmdReviewerSettings(ctx);
|
|
4141
4244
|
return;
|
|
@@ -4526,6 +4629,7 @@ export default function (pi: ExtensionAPI): void {
|
|
|
4526
4629
|
["autoresume=", "default: hold when a session is loaded, auto-resume on reload/fork; on: always auto-resume; off: never"],
|
|
4527
4630
|
["decisionpopup=", "on|off: decision pauses pop the select() picker (default on; the widget card always lists the options, /goal decide reopens the picker)"],
|
|
4528
4631
|
["auditcap=", "N: pause goal after N consecutive auditor disapprovals (default 5, 0 = unlimited)"],
|
|
4632
|
+
["log", "event-trail tail: /glla log [N] — who created/resumed/paused what, from where (v0.28.28)"],
|
|
4529
4633
|
["auditfeedbackchars=", "cap on executor-visible disapproval report chars (0 = full report, the default)"],
|
|
4530
4634
|
["aggressivemode=", "on: keep-going defaults — autoResume, cap 10, stuck 10, wedge off, quota auto-retry, cap→TODOs"],
|
|
4531
4635
|
["quotaretryminutes=", "N: minutes before auto-retrying a quota-exhausted auditor (default 60)"],
|
|
@@ -4628,8 +4732,8 @@ export default function (pi: ExtensionAPI): void {
|
|
|
4628
4732
|
// v0.28.24: a compaction is LEGITIMATE busy time — reset the send-rearm
|
|
4629
4733
|
// storm streaks (π-web nearly escalated a "send-retry storm" pause during
|
|
4630
4734
|
// a 3.5-minute compact) and open the post-compaction stall grace.
|
|
4631
|
-
continuationRearmStreak = 0;
|
|
4632
|
-
loopRearmStreak = 0;
|
|
4735
|
+
continuationRearmStreak = 0; continuationRearmSince = 0;
|
|
4736
|
+
loopRearmStreak = 0; loopRearmSince = 0;
|
|
4633
4737
|
compactionGraceUntil = Date.now() + COMPACTION_GRACE_MS;
|
|
4634
4738
|
const settle = setTimeout(() => {
|
|
4635
4739
|
const c = freshCtx();
|
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.29",
|
|
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": {
|