pi-goal-list-loop-audit 0.27.1 → 0.27.2
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.
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// pi-goal-list-loop-audit — v0.27.2
|
|
2
|
+
// extensions/length-continue.ts
|
|
3
|
+
//
|
|
4
|
+
// Folded-in auto-continue for output-token truncation (was the standalone
|
|
5
|
+
// pi-length-continue 0.1.0, now deprecated). When ONE assistant response
|
|
6
|
+
// exceeds the model's provider-side per-response output cap, pi ends the
|
|
7
|
+
// turn with stopReason "length" and idles — a dead stop on unattended rigs.
|
|
8
|
+
// The tracker decides when to re-trigger; goal.ts's agent_end handler wires
|
|
9
|
+
// it BEFORE all turn bookkeeping: a truncated turn is not a completed turn
|
|
10
|
+
// (no telemetry), not a stall (no no-tool nudge), and must not run the
|
|
11
|
+
// loop measure or the normal goal continuation on half a response.
|
|
12
|
+
//
|
|
13
|
+
// Guards (same as the standalone):
|
|
14
|
+
// - consecutive cap: after MAX back-to-back truncations, give up (once)
|
|
15
|
+
// instead of burning quota in a truncation ping-pong. Any normally
|
|
16
|
+
// finished turn resets the counter.
|
|
17
|
+
// - the caller skips when messages are already pending (a queued message
|
|
18
|
+
// triggers a turn anyway) and routes stale-handle errors to
|
|
19
|
+
// goStaleTerminal (pi#7154).
|
|
20
|
+
|
|
21
|
+
export const LENGTH_CONTINUE_MAX = 3;
|
|
22
|
+
|
|
23
|
+
export const LENGTH_CONTINUE_TEXT = [
|
|
24
|
+
"Your previous response was cut off at the model's per-response output token limit.",
|
|
25
|
+
"Continue EXACTLY where you stopped — finish the current artifact, then keep going.",
|
|
26
|
+
"Keep each individual response shorter from here: split large file writes into multiple smaller write/edit calls across turns instead of one giant response.",
|
|
27
|
+
].join(" ");
|
|
28
|
+
|
|
29
|
+
export interface LengthContinueTick {
|
|
30
|
+
/** Send the continue message this round. */
|
|
31
|
+
fire: boolean;
|
|
32
|
+
/** The cap was just exceeded — notify the give-up exactly once. */
|
|
33
|
+
giveUpNow: boolean;
|
|
34
|
+
/** Current consecutive truncation streak (after this tick). */
|
|
35
|
+
consecutive: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function makeLengthContinueTracker(max: number = LENGTH_CONTINUE_MAX) {
|
|
39
|
+
let consecutive = 0;
|
|
40
|
+
let gaveUp = false;
|
|
41
|
+
return {
|
|
42
|
+
tick(stopped: boolean): LengthContinueTick {
|
|
43
|
+
if (!stopped) {
|
|
44
|
+
consecutive = 0;
|
|
45
|
+
gaveUp = false;
|
|
46
|
+
return { fire: false, giveUpNow: false, consecutive: 0 };
|
|
47
|
+
}
|
|
48
|
+
consecutive++;
|
|
49
|
+
if (consecutive > max) {
|
|
50
|
+
const giveUpNow = !gaveUp;
|
|
51
|
+
gaveUp = true;
|
|
52
|
+
return { fire: false, giveUpNow, consecutive };
|
|
53
|
+
}
|
|
54
|
+
return { fire: true, giveUpNow: false, consecutive };
|
|
55
|
+
},
|
|
56
|
+
get consecutive(): number {
|
|
57
|
+
return consecutive;
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Session-level singleton — one tracker per extension runtime. The factory
|
|
63
|
+
// calls resetLengthContinue() so an extension reload starts clean.
|
|
64
|
+
let tracker = makeLengthContinueTracker();
|
|
65
|
+
|
|
66
|
+
export function tickLengthContinue(stopped: boolean): LengthContinueTick {
|
|
67
|
+
return tracker.tick(stopped);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function resetLengthContinue(): void {
|
|
71
|
+
tracker = makeLengthContinueTracker();
|
|
72
|
+
}
|
package/extensions/loops/goal.ts
CHANGED
|
@@ -86,6 +86,12 @@ import {
|
|
|
86
86
|
writeGoalMd,
|
|
87
87
|
missingGllaTools,
|
|
88
88
|
} from "../goal-loop-core.js";
|
|
89
|
+
import {
|
|
90
|
+
LENGTH_CONTINUE_MAX,
|
|
91
|
+
LENGTH_CONTINUE_TEXT,
|
|
92
|
+
resetLengthContinue,
|
|
93
|
+
tickLengthContinue,
|
|
94
|
+
} from "../length-continue.js";
|
|
89
95
|
import {
|
|
90
96
|
isQuotaError,
|
|
91
97
|
isSubagentQuotaResult,
|
|
@@ -527,6 +533,25 @@ function sendContinuation(goalId: string): void {
|
|
|
527
533
|
}
|
|
528
534
|
}
|
|
529
535
|
|
|
536
|
+
// v0.27.2: send the truncation-continue nudge. Same guards as
|
|
537
|
+
// sendContinuation (stale api = terminal), independent of goal state —
|
|
538
|
+
// plain sessions truncate too.
|
|
539
|
+
function sendLengthContinue(ctx: ExtensionContext, consecutive: number): void {
|
|
540
|
+
if (!extensionApi || extensionApiStale) return;
|
|
541
|
+
try {
|
|
542
|
+
extensionApi.sendMessage({
|
|
543
|
+
customType: GOAL_EVENT_ENTRY,
|
|
544
|
+
content: LENGTH_CONTINUE_TEXT,
|
|
545
|
+
display: true,
|
|
546
|
+
}, { triggerTurn: true, deliverAs: "followUp" });
|
|
547
|
+
appendLedger(ctx.cwd, "length_continue_sent", { consecutive });
|
|
548
|
+
ctx.ui.notify(`Response hit the output-token cap — auto-continuing (${consecutive}/${LENGTH_CONTINUE_MAX})`, "warning");
|
|
549
|
+
} catch (err) {
|
|
550
|
+
appendLedger(ctx.cwd, "length_continue_send_failed", { consecutive, error: err instanceof Error ? err.message : String(err) });
|
|
551
|
+
if (isStaleApiError(err)) goStaleTerminal(ctx, "sendLengthContinue");
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
530
555
|
function continuationPrompt(goal: Goal): string {
|
|
531
556
|
// Read the .md file as the template, then substitute {{tokens}}.
|
|
532
557
|
// For v0.1.0 we inline-substitute so we don't need fs at runtime.
|
|
@@ -3463,6 +3488,7 @@ function warnOnCommandCollision(ctx: ExtensionContext): void {
|
|
|
3463
3488
|
export default function (pi: ExtensionAPI): void {
|
|
3464
3489
|
extensionApi = pi;
|
|
3465
3490
|
extensionApiStale = false; // a fresh factory run means a fresh runtime (reload path)
|
|
3491
|
+
resetLengthContinue(); // v0.27.2: fresh runtime, fresh truncation streak
|
|
3466
3492
|
startHeartbeat();
|
|
3467
3493
|
startUITicker();
|
|
3468
3494
|
// Four top-level commands, that's all (v0.8.0 consolidation):
|
|
@@ -3778,6 +3804,23 @@ export default function (pi: ExtensionAPI): void {
|
|
|
3778
3804
|
// continuation loop.
|
|
3779
3805
|
if (isForeignCtx(ctx)) return;
|
|
3780
3806
|
noteActivity(true);
|
|
3807
|
+
// v0.27.2: folded-in length-continue (standalone pi-length-continue is
|
|
3808
|
+
// deprecated). A response cut by the per-response output cap is NOT a
|
|
3809
|
+
// completed turn (no telemetry), NOT a stall (no no-tool nudge), and
|
|
3810
|
+
// must not run the loop measure or the normal goal continuation on half
|
|
3811
|
+
// a response — re-trigger immediately with split-smaller guidance and
|
|
3812
|
+
// skip ALL turn bookkeeping; the NEXT agent_end processes the run.
|
|
3813
|
+
// Works with no goal active (plain sessions truncate too).
|
|
3814
|
+
const lastA = [...(event.messages as any[])].reverse().find((m) => m.role === "assistant");
|
|
3815
|
+
const lc = tickLengthContinue(lastA?.stopReason === "length");
|
|
3816
|
+
if (lc.giveUpNow) {
|
|
3817
|
+
ctx.ui.notify(`glla: response hit the output-token cap ${LENGTH_CONTINUE_MAX}× in a row — stepping aside. Ask the model to split the work into smaller pieces.`, "warning");
|
|
3818
|
+
notifyExternal(ctx, "Response truncated 3× in a row — giving up auto-continue.");
|
|
3819
|
+
}
|
|
3820
|
+
if (lastA?.stopReason === "length") {
|
|
3821
|
+
if (lc.fire && !ctx.hasPendingMessages()) sendLengthContinue(ctx, lc.consecutive);
|
|
3822
|
+
return;
|
|
3823
|
+
}
|
|
3781
3824
|
// v0.25.2: per-goal turn telemetry (/glla stats).
|
|
3782
3825
|
if (state.goal && state.goal.status === "active") {
|
|
3783
3826
|
const t = state.goal.telemetry ?? { turns: 0, fileWrites: 0, bashCalls: 0 };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goal-list-loop-audit",
|
|
3
|
-
"version": "0.27.
|
|
3
|
+
"version": "0.27.2",
|
|
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",
|