pi-goal-list-loop-audit 0.38.13 → 0.38.15
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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.38.15 — pause anti-confabulation (2026-09-04)
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
`pause_goal` refuses pauses whose blocker claims a GLLA tool is missing (dispatch proves the batch landed) and tells the model to call it; quoting pi's `Tool X not found` is accepted as genuine outage. Runbook + new-tab forensics in `audit/PAUSE-ANTI-CONFABULATION-2026-09-04.md`.
|
|
7
|
+
|
|
8
|
+
## 0.38.14 — human end-of-objective briefing (2026-09-04)
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
Every `✓ done` chat notify is now a human briefing: outcome first in its own words, only informing labels after it (`none` / `not recorded` / empty filler dropped; `none — <content>` keeps the content). Word-boundary cuts, auditor/no-audit trailer, archive keeps the full six-label record. Runbook in `audit/HUMAN-BRIEF-2026-09-04.md`.
|
|
12
|
+
|
|
3
13
|
## 0.38.13 — good completion summary (2026-09-04)
|
|
4
14
|
|
|
5
15
|
### Changed
|
package/docs/INDEX.md
CHANGED
|
@@ -18,7 +18,7 @@ For shipped docs, the relevant entry points are:
|
|
|
18
18
|
failback; v0.35.9 hardened cross-version npm tarball checks; v0.35.10
|
|
19
19
|
handles multi-entry npm dry-run reports; v0.35.11 accepts both npm report
|
|
20
20
|
shapes; v0.35.12 supports npm 12's keyed pack reports; v0.35.13 fixes stale-API recovery loops.
|
|
21
|
-
v0.35.14–v0.38.
|
|
21
|
+
v0.35.14–v0.38.15 continue through the supervisor freeze (`/glla pause`),
|
|
22
22
|
load hold, auditor picker parity, Windows launch fix, zombie-watchdog
|
|
23
23
|
subagent carve-out, due-wait backstop, the `/glla agents` visibility panel,
|
|
24
24
|
durable state-root selection, blank-until-resume auditor context, frozen
|
|
@@ -116,6 +116,49 @@ export function compactCompletionSummary(text: string | undefined, maxValueLengt
|
|
|
116
116
|
return parts.join(" · ");
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
/** A recap value informs the human only when it is not an empty / none /
|
|
120
|
+
* not-recorded placeholder. `None — <real content>` (a common agent habit)
|
|
121
|
+
* keeps just the content. Returns null for filler. */
|
|
122
|
+
export function briefValueContent(value: string): string | null {
|
|
123
|
+
const clean = value.replace(/\s+/g, " ").trim();
|
|
124
|
+
if (!clean) return null;
|
|
125
|
+
// System placeholder vocabulary: the `— explanation` is still a placeholder.
|
|
126
|
+
if (/^not recorded\b/i.test(clean)) return null;
|
|
127
|
+
// Agent habit: `none — <real content>` hides information behind filler.
|
|
128
|
+
const prefixed = /^none\s*[—–:\-]\s*(.+)$/i.exec(clean);
|
|
129
|
+
const body = (prefixed?.[1] ?? clean).trim();
|
|
130
|
+
if (/^(none|n\/a|nil|nothing)(\s+for\s+this\s+\w+)?\.?$/i.test(body)) return null;
|
|
131
|
+
return body;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface HumanCompletionBrief {
|
|
135
|
+
outcome: string;
|
|
136
|
+
details: string[];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** The human briefing: outcome first in its own words, then only the
|
|
140
|
+
* labels that carry real content (filler like `Unresolved: none` or
|
|
141
|
+
* `Changed: not recorded` is dropped, never shown). The durable archive
|
|
142
|
+
* keeps the full six-label record; this is the end-of-objective voice
|
|
143
|
+
* that informs the user at a glance. */
|
|
144
|
+
export function humanCompletionBrief(
|
|
145
|
+
text: string | undefined,
|
|
146
|
+
outcomeBudget = 140,
|
|
147
|
+
valueBudget = 120,
|
|
148
|
+
): HumanCompletionBrief {
|
|
149
|
+
const lines = completionSummaryLines(text, Math.max(outcomeBudget, valueBudget));
|
|
150
|
+
const rawOutcome = (lines[0] ?? "").replace(/^Outcome:\s*/, "");
|
|
151
|
+
const outcome = clipSummaryValue(briefValueContent(rawOutcome) ?? "done", outcomeBudget);
|
|
152
|
+
const details: string[] = [];
|
|
153
|
+
for (const line of lines.slice(1)) {
|
|
154
|
+
const separator = line.indexOf(":");
|
|
155
|
+
if (separator < 0) continue;
|
|
156
|
+
const content = briefValueContent(line.slice(separator + 1));
|
|
157
|
+
if (content) details.push(`${line.slice(0, separator)}: ${clipSummaryValue(content, valueBudget)}`);
|
|
158
|
+
}
|
|
159
|
+
return { outcome, details };
|
|
160
|
+
}
|
|
161
|
+
|
|
119
162
|
/** Multi-line projection: one `Label: value` line per label with generous
|
|
120
163
|
* word-bounded values. This is the user-facing `✓ done` block — six short
|
|
121
164
|
* facts that stay scannable in chat. The single-line projection remains
|
|
@@ -236,6 +279,15 @@ export function compactTerminalCompletionSummary(
|
|
|
236
279
|
return compactCompletionSummary(resolveCompletionSummary(facts, candidate).summary, maxValueLength);
|
|
237
280
|
}
|
|
238
281
|
|
|
282
|
+
/** Brief twin of compactTerminalCompletionSummary for the `✓ done` chat
|
|
283
|
+
* notifies: same resolved facts, outcome + informing labels only. */
|
|
284
|
+
export function terminalHumanBrief(
|
|
285
|
+
facts: CompletionSummaryFacts,
|
|
286
|
+
candidate = facts.goal.completionSummary,
|
|
287
|
+
): HumanCompletionBrief {
|
|
288
|
+
return humanCompletionBrief(resolveCompletionSummary(facts, candidate).summary);
|
|
289
|
+
}
|
|
290
|
+
|
|
239
291
|
/** Multi-line twin of compactTerminalCompletionSummary for the `✓ done`
|
|
240
292
|
* chat notifies: same resolved facts, one `Label: value` line each. */
|
|
241
293
|
export function terminalCompletionSummaryLines(
|
|
@@ -3056,6 +3056,28 @@ export function missingGllaTools(activeNames: readonly string[]): readonly GllaT
|
|
|
3056
3056
|
return GLLA_TOOL_NAMES.filter((n) => !active.has(n));
|
|
3057
3057
|
}
|
|
3058
3058
|
|
|
3059
|
+
/** pi's own wording when a model calls a tool pi cannot dispatch. A pause
|
|
3060
|
+
* reason quoting this is evidence of a genuine tool outage — the pause
|
|
3061
|
+
* must be accepted, never refused as confabulation. */
|
|
3062
|
+
export const PI_TOOL_NOT_FOUND_QUOTE = /tool\s+\S+\s+not found/i;
|
|
3063
|
+
|
|
3064
|
+
/** A pause reason that claims a GLLA tool does not exist in this session
|
|
3065
|
+
* (`no complete_goal tool`, `complete_goal is missing`, …). Ordinary
|
|
3066
|
+
* mentions of a tool (results, instructions, summaries) never match —
|
|
3067
|
+
* absence language is required. */
|
|
3068
|
+
export function claimedMissingGllaTool(reason: string): GllaToolName | null {
|
|
3069
|
+
const text = (reason ?? "").replace(/\s+/g, " ");
|
|
3070
|
+
if (!text) return null;
|
|
3071
|
+
for (const name of GLLA_TOOL_NAMES) {
|
|
3072
|
+
const absence = new RegExp(
|
|
3073
|
+
`\\bno\\s+${name}\\s+tool\\b|\\b${name}\\b[^.]{0,120}?\\b(missing|not found|not available|unavailable|doesn'?t exist|isn'?t available|is not available|no longer (?:available|exists|present))\\b`,
|
|
3074
|
+
"i",
|
|
3075
|
+
);
|
|
3076
|
+
if (absence.test(text)) return name;
|
|
3077
|
+
}
|
|
3078
|
+
return null;
|
|
3079
|
+
}
|
|
3080
|
+
|
|
3059
3081
|
// -----------------------------------------------------------------
|
|
3060
3082
|
// v0.25.0 — eager-continuation contract helpers
|
|
3061
3083
|
// -----------------------------------------------------------------
|
|
@@ -240,7 +240,7 @@ import {
|
|
|
240
240
|
pushCapped as pushRepetitionCapped,
|
|
241
241
|
} from "../goal-loop-repetition.js";
|
|
242
242
|
import { buildStatusText, buildWidgetLines, type AuditDisplayProgress } from "../goal-loop-display.js";
|
|
243
|
-
import { compactCompletionSummary, compactTerminalCompletionSummary, isGenericCompletionSummary, missingCompletionSummaryLabels,
|
|
243
|
+
import { compactCompletionSummary, compactTerminalCompletionSummary, isGenericCompletionSummary, missingCompletionSummaryLabels, terminalHumanBrief } from "../completion-summary.js";
|
|
244
244
|
import {
|
|
245
245
|
defaultAgentDir,
|
|
246
246
|
resolveEffectiveSubagentModel,
|
|
@@ -1416,7 +1416,7 @@ async function retryStoredCompletionAudit(origin: CompletionAuditOrigin = "provi
|
|
|
1416
1416
|
archivePath: path.relative(liveCtx.cwd, archivedGoalPath(liveCtx.cwd, state.goal.id)) || archivedGoalPath(liveCtx.cwd, state.goal.id),
|
|
1417
1417
|
}, state.goal.completionSummary);
|
|
1418
1418
|
// Computed pre-archive: archiveCurrentGoal clears state.goal.
|
|
1419
|
-
const
|
|
1419
|
+
const brief = terminalHumanBrief({
|
|
1420
1420
|
goal: state.goal,
|
|
1421
1421
|
status: "complete",
|
|
1422
1422
|
stopReason: terminalReason,
|
|
@@ -1438,10 +1438,10 @@ async function retryStoredCompletionAudit(origin: CompletionAuditOrigin = "provi
|
|
|
1438
1438
|
appendLedger(liveCtx.cwd, "goal_archive_failed_after_approval", { goalId, attemptId: claim.attemptId, origin });
|
|
1439
1439
|
return;
|
|
1440
1440
|
}
|
|
1441
|
-
// v0.38.
|
|
1442
|
-
//
|
|
1443
|
-
//
|
|
1444
|
-
liveCtx.ui.notify(`✓ done — auditor ${result.model} approved${approvalVia}
|
|
1441
|
+
// v0.38.14: the chat notify is the human briefing — outcome first,
|
|
1442
|
+
// filler labels dropped — while the external notify keeps the compact
|
|
1443
|
+
// single line (pager/sound safe).
|
|
1444
|
+
liveCtx.ui.notify(`✓ done — ${brief.outcome}\n${[...brief.details, `— auditor ${result.model} approved${approvalVia}.`].join("\n")}`, "info");
|
|
1445
1445
|
notifyExternal(liveCtx, `Goal complete (auditor approved, ${origin}): ${recap}`);
|
|
1446
1446
|
return;
|
|
1447
1447
|
}
|
|
@@ -114,6 +114,8 @@ import {
|
|
|
114
114
|
readQueueFromDisk,
|
|
115
115
|
deleteQueueItemFileResult,
|
|
116
116
|
missingGllaTools,
|
|
117
|
+
claimedMissingGllaTool,
|
|
118
|
+
PI_TOOL_NOT_FOUND_QUOTE,
|
|
117
119
|
runPersistStep,
|
|
118
120
|
isPersistenceDegraded,
|
|
119
121
|
lastPersistenceFailure,
|
|
@@ -250,7 +252,7 @@ import {
|
|
|
250
252
|
pushCapped as pushRepetitionCapped,
|
|
251
253
|
} from "../goal-loop-repetition.js";
|
|
252
254
|
import { buildStatusText, buildWidgetLines, type AuditDisplayProgress } from "../goal-loop-display.js";
|
|
253
|
-
import { compactCompletionSummary, compactTerminalCompletionSummary, resolveCompletionSummary,
|
|
255
|
+
import { compactCompletionSummary, compactTerminalCompletionSummary, resolveCompletionSummary, terminalHumanBrief } from "../completion-summary.js";
|
|
254
256
|
import {
|
|
255
257
|
defaultAgentDir,
|
|
256
258
|
resolveEffectiveSubagentModel,
|
|
@@ -1216,15 +1218,16 @@ function registerAgentTools(pi: any): void {
|
|
|
1216
1218
|
details: {},
|
|
1217
1219
|
};
|
|
1218
1220
|
}
|
|
1219
|
-
const
|
|
1221
|
+
const brief = terminalHumanBrief({
|
|
1220
1222
|
goal: terminalGoal,
|
|
1221
1223
|
status: "complete",
|
|
1222
1224
|
stopReason: terminalReason,
|
|
1223
1225
|
archivePath: path.relative(ctx.cwd, archivedGoalPath(ctx.cwd, terminalGoal.id)) || archivedGoalPath(ctx.cwd, terminalGoal.id),
|
|
1224
1226
|
});
|
|
1225
|
-
|
|
1227
|
+
const briefBlock = [...brief.details, `— completed without audit (your choice).`].join("\n");
|
|
1228
|
+
ctx.ui.notify(`✓ done — ${brief.outcome}\n${briefBlock}`, "info");
|
|
1226
1229
|
notifyExternal(ctx, `Goal complete without audit (user choice): ${recap}`);
|
|
1227
|
-
return { content: [{ type: "text", text: `Goal marked complete without audit (user choice).\n\n${
|
|
1230
|
+
return { content: [{ type: "text", text: `Goal marked complete without audit (user choice).\n\n${briefBlock}` }], details: {} };
|
|
1228
1231
|
}
|
|
1229
1232
|
scheduleContinuation(ctx, true);
|
|
1230
1233
|
return {
|
|
@@ -1250,7 +1253,7 @@ function registerAgentTools(pi: any): void {
|
|
|
1250
1253
|
archivePath: path.relative(ctx.cwd, archivedGoalPath(ctx.cwd, state.goal.id)) || archivedGoalPath(ctx.cwd, state.goal.id),
|
|
1251
1254
|
}, state.goal.completionSummary);
|
|
1252
1255
|
// Computed pre-archive: archiveCurrentGoal clears state.goal.
|
|
1253
|
-
const
|
|
1256
|
+
const brief = terminalHumanBrief({
|
|
1254
1257
|
goal: state.goal,
|
|
1255
1258
|
status: "complete",
|
|
1256
1259
|
stopReason: terminalReason,
|
|
@@ -1271,7 +1274,7 @@ function registerAgentTools(pi: any): void {
|
|
|
1271
1274
|
appendLedger(ctx.cwd, "goal_archive_failed_after_approval", { goalId: state.goal?.id, origin: "manual-verify", model: result.model });
|
|
1272
1275
|
return { content: [{ type: "text", text: "The auditor approved, but the terminal archive could not be persisted. The goal is paused; fix persistence, resume, and retry complete_goal." }], details: {} };
|
|
1273
1276
|
}
|
|
1274
|
-
ctx.ui.notify(`✓ done — auditor ${result.model} approved
|
|
1277
|
+
ctx.ui.notify(`✓ done — ${brief.outcome}\n${[...brief.details, `— auditor ${result.model} approved.`].join("\n")}`, "info");
|
|
1275
1278
|
notifyExternal(ctx, `Goal complete (auditor approved): ${recap}`);
|
|
1276
1279
|
return { content: [{ type: "text", text: `Goal approved by auditor ${result.model}.` }], details: {} };
|
|
1277
1280
|
}
|
|
@@ -1926,6 +1929,20 @@ function registerAgentTools(pi: any): void {
|
|
|
1926
1929
|
if (state.goal.status !== "active") {
|
|
1927
1930
|
return { content: [{ type: "text", text: `Goal is already ${state.goal.status}; pause request ignored.` }], details: {} };
|
|
1928
1931
|
}
|
|
1932
|
+
// v0.38.15: anti-confabulation — a pause whose blocker is "this
|
|
1933
|
+
// session has no <glla tool>" is refused when the tool path provably
|
|
1934
|
+
// works: this very pause_goal call dispatched through the same
|
|
1935
|
+
// registration batch (complete_goal registers first in that batch),
|
|
1936
|
+
// so the tool IS callable — the model is misreading its tool list
|
|
1937
|
+
// (field: new-tab 2026-09-04, 5×-compacted session, zero tool errors
|
|
1938
|
+
// in transcript or ledger). The model is told to call it now; a
|
|
1939
|
+
// quoted pi `Tool X not found` error is genuine-outage evidence and
|
|
1940
|
+
// the pause is accepted.
|
|
1941
|
+
const missingClaim = claimedMissingGllaTool(p.reason ?? "");
|
|
1942
|
+
if (missingClaim && !PI_TOOL_NOT_FOUND_QUOTE.test(p.reason ?? "")) {
|
|
1943
|
+
appendLedger(ctx.cwd, "pause_refused_tool_present", { goalId: state.goal.id, tool: missingClaim });
|
|
1944
|
+
return { content: [{ type: "text", text: `Not paused: \`${missingClaim}\` is registered in this session — this pause_goal call just dispatched through the same registration batch, so the tool path works. If \`${missingClaim}\` is missing from your visible tool list, that is a client-side gap: call \`${missingClaim}\` now${missingClaim === "complete_goal" ? " with your six-label recap and verification summary" : ""}. If pi itself answers with a \`Tool ${missingClaim} not found\` error, call pause_goal again quoting that exact error and the pause will be accepted.` }], details: {} };
|
|
1945
|
+
}
|
|
1929
1946
|
const pauseCopy = providerErrorPresentation(p.reason, "recovery");
|
|
1930
1947
|
const safePauseReason = pauseCopy.sensitive ? pauseCopy.display : p.reason;
|
|
1931
1948
|
const safePauseAction = p.suggestedAction ? sanitizeProviderDisplayText(p.suggestedAction) : p.suggestedAction;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goal-list-loop-audit",
|
|
3
|
-
"version": "0.38.
|
|
3
|
+
"version": "0.38.15",
|
|
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. A detached extension-less auditor process re-verifies every completion with raw evidence without holding the main pi turn; confirmed drafts, decision pauses and consent gates keep you in charge.",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"author": "dracon",
|