pi-goal-list-loop-audit 0.22.1 → 0.22.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.
|
@@ -208,7 +208,15 @@ export async function runGoalCompletionAuditor(args: {
|
|
|
208
208
|
cwd: ctx.cwd,
|
|
209
209
|
model,
|
|
210
210
|
thinkingLevel,
|
|
211
|
-
|
|
211
|
+
// Pass the PARENT's ModelRuntime (v0.22.2). createAgentSession has no
|
|
212
|
+
// "modelRegistry" option — passing the facade was silently ignored and
|
|
213
|
+
// a FRESH runtime was built from auth.json/models.json, which has no
|
|
214
|
+
// extension-registered providers. Streaming a model from such a
|
|
215
|
+
// provider (e.g. one with a custom streamSimple wrapper) then failed
|
|
216
|
+
// inside the stream and the auditor produced zero output. The facade
|
|
217
|
+
// keeps the runtime in a TS-private field; reach it defensively and
|
|
218
|
+
// fall back to the default (fresh runtime) if pi ever reshapes it.
|
|
219
|
+
modelRuntime: (ctx.modelRegistry as any)?.runtime,
|
|
212
220
|
resourceLoader: makeAuditorResourceLoader(),
|
|
213
221
|
sessionManager: SessionManager.inMemory(ctx.cwd),
|
|
214
222
|
// Compaction ENABLED (v0.4.0, closes pi-goal-x flaw #3: context exhaustion
|
|
@@ -256,6 +264,13 @@ export async function runGoalCompletionAuditor(args: {
|
|
|
256
264
|
if (event.type === "message_end") {
|
|
257
265
|
const message = event.message as any;
|
|
258
266
|
if (message?.role !== "assistant") return;
|
|
267
|
+
// Stream failures surface as an assistant message with stopReason
|
|
268
|
+
// "error" + errorMessage — NOT as an "error" event (v0.22.2: this
|
|
269
|
+
// is why "unknown provider"/driver failures looked like a silent
|
|
270
|
+
// empty report).
|
|
271
|
+
if (message.stopReason === "error" && typeof message.errorMessage === "string" && message.errorMessage.trim()) {
|
|
272
|
+
streamError = message.errorMessage.slice(0, 300);
|
|
273
|
+
}
|
|
259
274
|
for (const part of message.content ?? []) {
|
|
260
275
|
if (part.type === "text" && typeof part.text === "string") outputParts.push(part.text);
|
|
261
276
|
}
|
|
@@ -37,6 +37,18 @@ export function truncate(s: string, max: number): string {
|
|
|
37
37
|
return s.length <= max ? s : s.slice(0, Math.max(0, max - 1)) + "…";
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Width-aware truncation budget (v0.22.2). The hardcoded caps are FLOORS for
|
|
42
|
+
* narrow terminals; when the terminal is wider, lines may use the available
|
|
43
|
+
* width instead of being cut at a fixed ~60 chars (pi-tasks truncates at
|
|
44
|
+
* tui.terminal.columns — match that behavior). `prefixCols` is the visible
|
|
45
|
+
* width of the static prefix on the line (branch glyph + pi's 1-col gutter).
|
|
46
|
+
*/
|
|
47
|
+
function budgetFor(width: number | undefined, prefixCols: number, floor: number): number {
|
|
48
|
+
if (!width || width <= 0) return floor;
|
|
49
|
+
return Math.max(floor, width - 1 - prefixCols);
|
|
50
|
+
}
|
|
51
|
+
|
|
40
52
|
function sinceIso(iso: string): number {
|
|
41
53
|
const t = Date.parse(iso);
|
|
42
54
|
return Number.isFinite(t) ? Date.now() - t : 0;
|
|
@@ -122,17 +134,17 @@ function countTotal(g: Goal): number {
|
|
|
122
134
|
* Widget lines for ctx.ui.setWidget("pi-glla", lines).
|
|
123
135
|
* Returns undefined when nothing is worth showing.
|
|
124
136
|
*/
|
|
125
|
-
export function buildWidgetLines(state: State, audit?: AuditDisplayProgress | null, now = Date.now(), theme?: DisplayTheme): string[] | undefined {
|
|
126
|
-
if (state.loop?.active) return loopLines(state.loop, now, theme);
|
|
137
|
+
export function buildWidgetLines(state: State, audit?: AuditDisplayProgress | null, now = Date.now(), theme?: DisplayTheme, width?: number): string[] | undefined {
|
|
138
|
+
if (state.loop?.active) return loopLines(state.loop, now, theme, width);
|
|
127
139
|
const g = state.goal;
|
|
128
140
|
if (!g) return undefined;
|
|
129
141
|
if (g.status === "complete" || g.status === "aborted") return undefined;
|
|
130
|
-
return goalLines(g, state, audit, now, theme);
|
|
142
|
+
return goalLines(g, state, audit, now, theme, width);
|
|
131
143
|
}
|
|
132
144
|
|
|
133
145
|
// Branch lines sit flush-left (pi-tasks convention): pi's widget renderer
|
|
134
146
|
// adds its own one-space gutter, so any indent here doubles up.
|
|
135
|
-
function goalLines(g: Goal, state: State, audit: AuditDisplayProgress | null | undefined, now: number, theme?: DisplayTheme): string[] {
|
|
147
|
+
function goalLines(g: Goal, state: State, audit: AuditDisplayProgress | null | undefined, now: number, theme?: DisplayTheme, width?: number): string[] {
|
|
136
148
|
// Head glyph is ● (not ◆): U+25C6 renders as a color-emoji diamond in some
|
|
137
149
|
// terminal fonts and ignores ANSI color; ● takes the paint everywhere.
|
|
138
150
|
const icon =
|
|
@@ -141,7 +153,7 @@ function goalLines(g: Goal, state: State, audit: AuditDisplayProgress | null | u
|
|
|
141
153
|
: g.status === "auditing"
|
|
142
154
|
? paint(theme, "accent", "⟡")
|
|
143
155
|
: paint(theme, "success", "●");
|
|
144
|
-
const head = `${icon} ${truncate(g.objective.replace(/\s+/g, " "), 64)}`;
|
|
156
|
+
const head = `${icon} ${truncate(g.objective.replace(/\s+/g, " "), budgetFor(width, 3, 64))}`;
|
|
145
157
|
const statusWord = g.status === "active" ? paint(theme, "success", "active") : g.status;
|
|
146
158
|
// Token segment only when a budget is set (v0.22.0): the guard is opt-in,
|
|
147
159
|
// and "0/0 tok" carried no information when off.
|
|
@@ -155,29 +167,29 @@ function goalLines(g: Goal, state: State, audit: AuditDisplayProgress | null | u
|
|
|
155
167
|
return lines;
|
|
156
168
|
}
|
|
157
169
|
if (g.status === "paused" && g.pauseReason) {
|
|
158
|
-
lines.push(`├─ ${paint(theme, pauseIsError(g) ? "error" : "warning", truncate(g.pauseReason, 60))}`);
|
|
159
|
-
if (g.pauseSuggestedAction) lines.push(`└─ ${paint(theme, "dim", truncate(g.pauseSuggestedAction, 60))}`);
|
|
170
|
+
lines.push(`├─ ${paint(theme, pauseIsError(g) ? "error" : "warning", truncate(g.pauseReason, budgetFor(width, 3, 60)))}`);
|
|
171
|
+
if (g.pauseSuggestedAction) lines.push(`└─ ${paint(theme, "dim", truncate(g.pauseSuggestedAction, budgetFor(width, 3, 60)))}`);
|
|
160
172
|
return lines;
|
|
161
173
|
}
|
|
162
174
|
const next = nextPending(g);
|
|
163
|
-
if (next) lines.push(`├─ next: ${truncate(next, 56)}`);
|
|
175
|
+
if (next) lines.push(`├─ next: ${truncate(next, budgetFor(width, 9, 56))}`);
|
|
164
176
|
const queue = state.list?.length ?? 0;
|
|
165
177
|
lines.push(`└─ ${paint(theme, "dim", `${queue > 0 ? `list ${queue} · ` : ""}/goal status · /glla`)}`);
|
|
166
178
|
return lines;
|
|
167
179
|
}
|
|
168
180
|
|
|
169
|
-
function loopLines(l: LoopState, now: number, theme?: DisplayTheme): string[] {
|
|
181
|
+
function loopLines(l: LoopState, now: number, theme?: DisplayTheme, width?: number): string[] {
|
|
170
182
|
const arrow = paint(theme, "accent", l.direction === "min" ? "↓" : "↑");
|
|
171
183
|
const best = paint(theme, "success", `${l.bestValue ?? "n/a"}`);
|
|
172
184
|
const stallText = `stall ${l.stallCount}/${l.plateauWindow}`;
|
|
173
185
|
const stall = l.stallCount >= l.plateauWindow - 1 ? paint(theme, "warning", stallText) : stallText;
|
|
174
186
|
const lines = [
|
|
175
|
-
`${paint(theme, "accent", "●")} ${truncate(l.target, 64)}`,
|
|
187
|
+
`${paint(theme, "accent", "●")} ${truncate(l.target, budgetFor(width, 3, 64))}`,
|
|
176
188
|
`├─ loop ${arrow} iter ${l.iteration}/${l.maxIterations} · ${fmtElapsed(now - Date.parse(l.startedAt))}`,
|
|
177
189
|
`├─ best ${best} · last ${l.lastValue ?? "n/a"} · ${stall}`,
|
|
178
|
-
`└─ ${paint(theme, "dim", truncate(l.measureCmd, 56))}`,
|
|
190
|
+
`└─ ${paint(theme, "dim", truncate(l.measureCmd, budgetFor(width, 3, 56)))}`,
|
|
179
191
|
];
|
|
180
|
-
if (l.branchName) lines.push(`⎇ ${paint(theme, "muted", truncate(l.branchName, 50))}`);
|
|
192
|
+
if (l.branchName) lines.push(`⎇ ${paint(theme, "muted", truncate(l.branchName, budgetFor(width, 3, 50)))}`);
|
|
181
193
|
return lines;
|
|
182
194
|
}
|
|
183
195
|
|
package/extensions/loops/goal.ts
CHANGED
|
@@ -144,8 +144,11 @@ function refreshUI(ctx: ExtensionContext): void {
|
|
|
144
144
|
if (!ctx.hasUI) return;
|
|
145
145
|
try {
|
|
146
146
|
const theme = ctx.ui.theme as unknown as import("../goal-loop-display.js").DisplayTheme | undefined;
|
|
147
|
+
// Terminal width for truncation budgets: on wide terminals the widget
|
|
148
|
+
// uses the room instead of cutting at fixed ~60-char floors.
|
|
149
|
+
const width = process.stdout.columns || 80;
|
|
147
150
|
ctx.ui.setStatus("pi-glla", buildStatusText(state, latestAuditProgress, Date.now(), theme));
|
|
148
|
-
ctx.ui.setWidget("pi-glla", buildWidgetLines(state, latestAuditProgress, Date.now(), theme));
|
|
151
|
+
ctx.ui.setWidget("pi-glla", buildWidgetLines(state, latestAuditProgress, Date.now(), theme, width));
|
|
149
152
|
} catch {
|
|
150
153
|
// stale ctx — next event refreshes
|
|
151
154
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goal-list-loop-audit",
|
|
3
|
-
"version": "0.22.
|
|
3
|
+
"version": "0.22.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",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@earendil-works/pi-agent-core": "^0.74.0",
|
|
57
57
|
"@earendil-works/pi-ai": "^0.74.0",
|
|
58
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
58
|
+
"@earendil-works/pi-coding-agent": "^0.81.1",
|
|
59
59
|
"@earendil-works/pi-tui": "^0.74.0",
|
|
60
60
|
"typebox": "^1.0.58",
|
|
61
61
|
"typescript": "^5.9.3"
|