pi-goal-list-loop-audit 0.25.4 โ 0.25.6
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 +56 -0
- package/extensions/goal-loop-subagents.ts +120 -13
- package/extensions/loops/goal.ts +89 -24
- package/extensions/quota-retry.ts +10 -0
- package/package.json +1 -1
|
@@ -1090,6 +1090,10 @@ export interface AuditLogEntry {
|
|
|
1090
1090
|
report: string;
|
|
1091
1091
|
impossibleReason?: string;
|
|
1092
1092
|
error?: string;
|
|
1093
|
+
/** v0.25.4 post-audit: how long the audit took, and whether the infra
|
|
1094
|
+
* retry fired. */
|
|
1095
|
+
durationMs?: number;
|
|
1096
|
+
retriedOnce?: boolean;
|
|
1093
1097
|
}
|
|
1094
1098
|
|
|
1095
1099
|
export function auditLogPath(cwd: string): string {
|
|
@@ -1145,3 +1149,55 @@ export function formatAuditLog(entries: AuditLogEntry[]): string {
|
|
|
1145
1149
|
})
|
|
1146
1150
|
.join("\n");
|
|
1147
1151
|
}
|
|
1152
|
+
|
|
1153
|
+
// =================================================================
|
|
1154
|
+
// v0.25.4 (post-audit fix): infra-failure retry-once-with-backoff
|
|
1155
|
+
// =================================================================
|
|
1156
|
+
|
|
1157
|
+
/** Which auditor infra errors are worth an automatic retry? User aborts
|
|
1158
|
+
* and missing-model config are NOT โ retrying can't help them. */
|
|
1159
|
+
export function isRetriableInfraError(error?: string): boolean {
|
|
1160
|
+
if (!error) return false;
|
|
1161
|
+
if (/aborted/i.test(error)) return false;
|
|
1162
|
+
if (/no model/i.test(error)) return false;
|
|
1163
|
+
return true;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
export interface InfraRetryOutcome<T> {
|
|
1167
|
+
result: T;
|
|
1168
|
+
retriedOnce: boolean;
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
/** Run the auditor; on a retriable infra failure, wait `backoffMs` and
|
|
1172
|
+
* retry EXACTLY once before reporting "auditor infrastructure error
|
|
1173
|
+
* (retried once)". The failed pair is never a verdict on the work. */
|
|
1174
|
+
export async function runWithInfraRetry<T extends { error?: string; approved: boolean; disapproved: boolean }>(
|
|
1175
|
+
run: () => Promise<T>,
|
|
1176
|
+
opts: { backoffMs?: number; sleep?: (ms: number) => Promise<void>; onRetry?: (error: string) => void } = {},
|
|
1177
|
+
): Promise<InfraRetryOutcome<T>> {
|
|
1178
|
+
const sleep = opts.sleep ?? ((ms: number) => new Promise<void>((r) => setTimeout(r, ms)));
|
|
1179
|
+
const first = await run();
|
|
1180
|
+
if (first.approved || first.disapproved || !isRetriableInfraError(first.error)) {
|
|
1181
|
+
return { result: first, retriedOnce: false };
|
|
1182
|
+
}
|
|
1183
|
+
opts.onRetry?.(first.error!);
|
|
1184
|
+
await sleep(opts.backoffMs ?? 5000);
|
|
1185
|
+
const second = await run();
|
|
1186
|
+
return { result: second, retriedOnce: true };
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
/** /glla audits default view: the ACTIVE goal's own audit history (the
|
|
1190
|
+
* surface the goal spec asked for), one line per verdict. */
|
|
1191
|
+
export function formatGoalAuditHistory(goal: { id: string; auditHistory?: Array<any> }): string {
|
|
1192
|
+
const history = goal.auditHistory ?? [];
|
|
1193
|
+
if (history.length === 0) return "(no audits on this goal yet)";
|
|
1194
|
+
return history
|
|
1195
|
+
.map((v) => {
|
|
1196
|
+
const glyph = v.approved ? (v.regressionShieldPassed === false ? "๐ก" : "โ") : v.impossible ? "โ" : v.disapproved ? "โ" : "โ ";
|
|
1197
|
+
const day = String(v.at ?? "").slice(5, 16).replace("T", " ");
|
|
1198
|
+
const elapsed = v.durationMs ? ` ยท ${Math.round(v.durationMs / 60000)}m` : "";
|
|
1199
|
+
const firstLine = (String(v.report ?? "").split("\n").find((l: string) => l.trim()) ?? "").trim().slice(0, 80);
|
|
1200
|
+
return `${glyph} ${day} ${v.model ?? "?"}${elapsed} โ ${firstLine}`;
|
|
1201
|
+
})
|
|
1202
|
+
.join("\n");
|
|
1203
|
+
}
|
|
@@ -82,19 +82,81 @@ export const EXPLORE_DEFAULT_TOOLS = "read, bash, grep, find, ls";
|
|
|
82
82
|
interface EmbeddedAgentDefault {
|
|
83
83
|
description: string;
|
|
84
84
|
systemPrompt: string;
|
|
85
|
+
/** "" means "all tools" (upstream omits builtinToolNames). */
|
|
85
86
|
tools: string;
|
|
86
87
|
}
|
|
87
88
|
|
|
88
|
-
|
|
89
|
-
|
|
89
|
+
export const PLAN_DEFAULT_DESCRIPTION = "Software architect agent for designing implementation plans. Use this when you need to plan the implementation strategy for a task. Returns step-by-step plans, identifies critical files, and considers architectural trade-offs.";
|
|
90
|
+
|
|
91
|
+
export const PLAN_DEFAULT_SYSTEM_PROMPT = `# CRITICAL: READ-ONLY MODE - NO FILE MODIFICATIONS
|
|
92
|
+
You are a software architect and planning specialist.
|
|
93
|
+
Your role is EXCLUSIVELY to explore the codebase and design implementation plans.
|
|
94
|
+
You do NOT have access to file editing tools โ attempting to edit files will fail.
|
|
95
|
+
|
|
96
|
+
You are STRICTLY PROHIBITED from:
|
|
97
|
+
- Creating new files
|
|
98
|
+
- Modifying existing files
|
|
99
|
+
- Deleting files
|
|
100
|
+
- Moving or copying files
|
|
101
|
+
- Creating temporary files anywhere, including /tmp
|
|
102
|
+
- Using redirect operators (>, >>, |) or heredocs to write to files
|
|
103
|
+
- Running ANY commands that change system state
|
|
104
|
+
|
|
105
|
+
# Planning Process
|
|
106
|
+
1. Understand requirements
|
|
107
|
+
2. Explore thoroughly (read files, find patterns, understand architecture)
|
|
108
|
+
3. Design solution based on your assigned perspective
|
|
109
|
+
4. Detail the plan with step-by-step implementation strategy
|
|
110
|
+
|
|
111
|
+
# Requirements
|
|
112
|
+
- Consider trade-offs and architectural decisions
|
|
113
|
+
- Identify dependencies and sequencing
|
|
114
|
+
- Anticipate potential challenges
|
|
115
|
+
- Follow existing patterns where appropriate
|
|
116
|
+
|
|
117
|
+
# Tool Usage
|
|
118
|
+
- Use the find tool for file pattern matching (NOT the bash find command)
|
|
119
|
+
- Use the grep tool for content search (NOT bash grep/rg command)
|
|
120
|
+
- Use the read tool for reading files (NOT bash cat/head/tail)
|
|
121
|
+
- Use Bash ONLY for read-only operations
|
|
122
|
+
|
|
123
|
+
# Output Format
|
|
124
|
+
- Use absolute file paths
|
|
125
|
+
- Do not use emojis
|
|
126
|
+
- End your response with:
|
|
127
|
+
|
|
128
|
+
### Critical Files for Implementation
|
|
129
|
+
List 3-5 files most critical for implementing this plan:
|
|
130
|
+
- /absolute/path/to/file.ts - [Brief reason]`;
|
|
131
|
+
|
|
132
|
+
export const GENERAL_PURPOSE_DEFAULT_DESCRIPTION = "General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you.";
|
|
133
|
+
|
|
134
|
+
/** Embedded copies keyed by agent name. Explore needs an entry for the
|
|
135
|
+
* strategy-driven sync (KNOWN_PINNED_DEFAULT_AGENTS); Plan and
|
|
136
|
+
* general-purpose entries exist so users can pin them per-type via
|
|
137
|
+
* subagentModelOverrides (v0.25.6). */
|
|
90
138
|
const EMBEDDED_DEFAULTS: Record<string, EmbeddedAgentDefault> = {
|
|
91
139
|
Explore: {
|
|
92
140
|
description: EXPLORE_DEFAULT_DESCRIPTION,
|
|
93
141
|
systemPrompt: EXPLORE_DEFAULT_SYSTEM_PROMPT,
|
|
94
142
|
tools: EXPLORE_DEFAULT_TOOLS,
|
|
95
143
|
},
|
|
144
|
+
Plan: {
|
|
145
|
+
description: PLAN_DEFAULT_DESCRIPTION,
|
|
146
|
+
systemPrompt: PLAN_DEFAULT_SYSTEM_PROMPT,
|
|
147
|
+
tools: EXPLORE_DEFAULT_TOOLS, // same read-only set upstream
|
|
148
|
+
},
|
|
149
|
+
"general-purpose": {
|
|
150
|
+
description: GENERAL_PURPOSE_DEFAULT_DESCRIPTION,
|
|
151
|
+
systemPrompt: "", // upstream: empty prompt, promptMode append
|
|
152
|
+
tools: "", // upstream: all tools (builtinToolNames omitted)
|
|
153
|
+
},
|
|
96
154
|
};
|
|
97
155
|
|
|
156
|
+
/** v0.25.6: agent types the user can pin per-type via
|
|
157
|
+
* subagentModelOverrides (embedded defaults exist for each). */
|
|
158
|
+
export const OVERRIDABLE_AGENT_TYPES = Object.keys(EMBEDDED_DEFAULTS);
|
|
159
|
+
|
|
98
160
|
/** Default global agent dir (pi-subagents reads $PI_CODING_AGENT_DIR/agents,
|
|
99
161
|
* default ~/.pi/agent/agents). Parameterized in sync for tests. */
|
|
100
162
|
export function defaultAgentDir(): string {
|
|
@@ -107,12 +169,9 @@ export function buildAgentOverrideMd(name: string, model?: string): string {
|
|
|
107
169
|
const def = EMBEDDED_DEFAULTS[name];
|
|
108
170
|
if (!def) throw new Error(`no embedded default config for agent "${name}"`);
|
|
109
171
|
const yamlDesc = "'" + def.description.replace(/'/g, "''") + "'";
|
|
110
|
-
const lines = [
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
`tools: ${def.tools}`,
|
|
114
|
-
"prompt_mode: replace",
|
|
115
|
-
];
|
|
172
|
+
const lines = ["---", `description: ${yamlDesc}`];
|
|
173
|
+
if (def.tools) lines.push(`tools: ${def.tools}`);
|
|
174
|
+
lines.push(def.systemPrompt ? "prompt_mode: replace" : "prompt_mode: append");
|
|
116
175
|
if (model) lines.push(`model: ${model}`);
|
|
117
176
|
lines.push(
|
|
118
177
|
`x-managed-by: ${SUBAGENT_MANAGED_MARKER}`,
|
|
@@ -121,7 +180,7 @@ export function buildAgentOverrideMd(name: string, model?: string): string {
|
|
|
121
180
|
: "x-glla-note: model pin removed (upstream default pins a fixed model) so this agent inherits the parent session model and its quota pool. Managed by glla โ flip /glla subagent strategy to agent-default to restore upstream behavior.",
|
|
122
181
|
"---",
|
|
123
182
|
"",
|
|
124
|
-
def.systemPrompt,
|
|
183
|
+
def.systemPrompt || "(no system-prompt override โ upstream default is an empty append-mode prompt)",
|
|
125
184
|
"",
|
|
126
185
|
);
|
|
127
186
|
return lines.join("\n");
|
|
@@ -135,7 +194,16 @@ export interface SubagentSyncResult {
|
|
|
135
194
|
written: string[];
|
|
136
195
|
removed: string[];
|
|
137
196
|
/** Files left untouched because the user owns them (no marker). */
|
|
138
|
-
skipped: Array<{ name: string
|
|
197
|
+
skipped: Array<{ name: string } & { reason: string }>;
|
|
198
|
+
/** v0.25.6: managed files that were expected (previously written) but
|
|
199
|
+
* found missing or altered, and got re-written โ surface these to the
|
|
200
|
+
* user (external edit, pi update, or dracon-sync churn). */
|
|
201
|
+
repaired: string[];
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** State file tracking what the last sync wrote (repair detection). */
|
|
205
|
+
export function subagentSyncStatePath(agentDir: string): string {
|
|
206
|
+
return path.join(agentDir, "agents", ".glla-subagent-sync.json");
|
|
139
207
|
}
|
|
140
208
|
|
|
141
209
|
/** Sync <agentDir>/agents/<Name>.md with the desired state. Idempotent:
|
|
@@ -145,8 +213,18 @@ export function syncSubagentModelOverrides(opts: {
|
|
|
145
213
|
strategy: SubagentModelStrategy;
|
|
146
214
|
overrides?: Record<string, string>;
|
|
147
215
|
}): SubagentSyncResult {
|
|
148
|
-
const result: SubagentSyncResult = { written: [], removed: [], skipped: [] };
|
|
216
|
+
const result: SubagentSyncResult = { written: [], removed: [], skipped: [], repaired: [] };
|
|
149
217
|
const overrides = opts.overrides ?? {};
|
|
218
|
+
// v0.25.6: load the previous sync state for repair detection โ a file
|
|
219
|
+
// we wrote before that is now MISSING or CONTENT-CHANGED was touched
|
|
220
|
+
// externally; re-writing it is a repair the user should hear about.
|
|
221
|
+
let prevWritten: string[] = [];
|
|
222
|
+
try {
|
|
223
|
+
const prev = JSON.parse(fs.readFileSync(subagentSyncStatePath(opts.agentDir), "utf-8"));
|
|
224
|
+
if (Array.isArray(prev?.written)) prevWritten = prev.written.map(String);
|
|
225
|
+
} catch {
|
|
226
|
+
/* first sync or unreadable state */
|
|
227
|
+
}
|
|
150
228
|
const names = new Set<string>([...KNOWN_PINNED_DEFAULT_AGENTS, ...Object.keys(overrides)]);
|
|
151
229
|
|
|
152
230
|
for (const name of names) {
|
|
@@ -159,11 +237,15 @@ export function syncSubagentModelOverrides(opts: {
|
|
|
159
237
|
continue;
|
|
160
238
|
}
|
|
161
239
|
const file = path.join(opts.agentDir, "agents", `${name}.md`);
|
|
240
|
+
// v0.25.6: the strategy-driven (model-less) write applies ONLY to
|
|
241
|
+
// agents that pin a model upstream (Explore) โ Plan/general-purpose
|
|
242
|
+
// don't pin, so inherit-parent needs no file for them; they get
|
|
243
|
+
// managed files only via an explicit per-type override.
|
|
162
244
|
const desired = overrideModel
|
|
163
245
|
? buildAgentOverrideMd(name, overrideModel)
|
|
164
|
-
: opts.strategy === "inherit-parent"
|
|
246
|
+
: opts.strategy === "inherit-parent" && (KNOWN_PINNED_DEFAULT_AGENTS as readonly string[]).includes(name)
|
|
165
247
|
? buildAgentOverrideMd(name)
|
|
166
|
-
: undefined; // agent-default + no
|
|
248
|
+
: undefined; // agent-default / no pin upstream + no override โ file should be absent
|
|
167
249
|
|
|
168
250
|
const exists = fs.existsSync(file);
|
|
169
251
|
const current = exists ? fs.readFileSync(file, "utf-8") : undefined;
|
|
@@ -186,6 +268,31 @@ export function syncSubagentModelOverrides(opts: {
|
|
|
186
268
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
187
269
|
fs.writeFileSync(file, desired);
|
|
188
270
|
result.written.push(name);
|
|
271
|
+
if (prevWritten.includes(name)) result.repaired.push(name);
|
|
272
|
+
}
|
|
273
|
+
// Persist what we manage now (best-effort).
|
|
274
|
+
try {
|
|
275
|
+
fs.mkdirSync(path.join(opts.agentDir, "agents"), { recursive: true });
|
|
276
|
+
fs.writeFileSync(subagentSyncStatePath(opts.agentDir), JSON.stringify({ written: result.written, at: new Date().toISOString() }));
|
|
277
|
+
} catch {
|
|
278
|
+
/* repair detection is best-effort */
|
|
189
279
|
}
|
|
190
280
|
return result;
|
|
191
281
|
}
|
|
282
|
+
|
|
283
|
+
/** v0.25.6: effective model for an agent type, for the headless settings
|
|
284
|
+
* display. Per-type override wins; inherit-parent falls to the session
|
|
285
|
+
* model; agent-default means upstream's own resolution (Explore's haiku
|
|
286
|
+
* pin for Explore, session model for the others). */
|
|
287
|
+
export function resolveEffectiveSubagentModel(
|
|
288
|
+
name: string,
|
|
289
|
+
settings: { subagentModelStrategy?: string; subagentModelOverrides?: Record<string, string> },
|
|
290
|
+
sessionModel?: string,
|
|
291
|
+
): string {
|
|
292
|
+
const pin = settings.subagentModelOverrides?.[name];
|
|
293
|
+
if (pin) return `${pin} (per-type pin)`;
|
|
294
|
+
if ((settings.subagentModelStrategy ?? "inherit-parent") === "inherit-parent") {
|
|
295
|
+
return sessionModel ? `${sessionModel} (inherits session)` : "(session model)";
|
|
296
|
+
}
|
|
297
|
+
return name === "Explore" ? "anthropic/claude-haiku-4-5 (upstream pin)" : "(agent default)";
|
|
298
|
+
}
|
package/extensions/loops/goal.ts
CHANGED
|
@@ -42,6 +42,8 @@ import {
|
|
|
42
42
|
appendAuditLog,
|
|
43
43
|
computeListDepth,
|
|
44
44
|
formatAuditLog,
|
|
45
|
+
formatGoalAuditHistory,
|
|
46
|
+
runWithInfraRetry,
|
|
45
47
|
readAuditLog,
|
|
46
48
|
stripThinkBlocks,
|
|
47
49
|
type AuditLogEntry,
|
|
@@ -85,6 +87,7 @@ import {
|
|
|
85
87
|
} from "../goal-loop-core.js";
|
|
86
88
|
import {
|
|
87
89
|
isQuotaError,
|
|
90
|
+
isSubagentQuotaResult,
|
|
88
91
|
parseQuotaError,
|
|
89
92
|
scheduleQuotaRetry,
|
|
90
93
|
cancelQuotaRetry,
|
|
@@ -120,6 +123,7 @@ import {
|
|
|
120
123
|
import { buildStatusText, buildWidgetLines, type AuditDisplayProgress } from "../goal-loop-display.js";
|
|
121
124
|
import {
|
|
122
125
|
defaultAgentDir,
|
|
126
|
+
resolveEffectiveSubagentModel,
|
|
123
127
|
syncSubagentModelOverrides,
|
|
124
128
|
type SubagentModelStrategy,
|
|
125
129
|
} from "../goal-loop-subagents.js";
|
|
@@ -1703,24 +1707,38 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
1703
1707
|
// Esc during the audit aborts this tool's signal โ threaded into the
|
|
1704
1708
|
// auditor session, which aborts cleanly and returns "Auditor aborted."
|
|
1705
1709
|
latestAuditProgress = { label: "starting", lastEventAt: Date.now() };
|
|
1706
|
-
const
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1710
|
+
const runAudit = () =>
|
|
1711
|
+
runGoalCompletionAuditor({
|
|
1712
|
+
ctx,
|
|
1713
|
+
goal: state.goal!,
|
|
1714
|
+
completionSummary: p.completionSummary,
|
|
1715
|
+
verificationSummary: p.verificationSummary,
|
|
1716
|
+
model: auditorModel,
|
|
1717
|
+
thinkingLevel: settings.auditorThinkingLevel ?? getSessionThinkingLevel(),
|
|
1718
|
+
signal: signal ?? undefined,
|
|
1719
|
+
onProgress: (progress) => {
|
|
1720
|
+
latestAuditProgress = {
|
|
1721
|
+
currentTool: progress.currentTool,
|
|
1722
|
+
label: progress.label,
|
|
1723
|
+
elapsedMs: progress.elapsedMs,
|
|
1724
|
+
lastEventAt: Date.now(),
|
|
1725
|
+
};
|
|
1726
|
+
refreshUI(ctx);
|
|
1727
|
+
},
|
|
1728
|
+
});
|
|
1729
|
+
// v0.25.4 (post-audit fix): a retriable infra failure (stream error,
|
|
1730
|
+
// auth blip โ NOT user abort, NOT missing model) gets ONE automatic
|
|
1731
|
+
// retry with backoff before we report "auditor infrastructure error
|
|
1732
|
+
// (retried once)". Neither attempt is a verdict on the work.
|
|
1733
|
+
const auditStartMs = Date.now();
|
|
1734
|
+
const { result, retriedOnce } = await runWithInfraRetry(runAudit, {
|
|
1735
|
+
onRetry: (err) => {
|
|
1736
|
+
latestAuditProgress = { label: `infra error (${err.slice(0, 40)}) โ retrying once`, lastEventAt: Date.now() };
|
|
1721
1737
|
refreshUI(ctx);
|
|
1738
|
+
appendLedger(ctx.cwd, "audit_infra_retry", { goalId: state.goal?.id, error: err.slice(0, 200) });
|
|
1722
1739
|
},
|
|
1723
1740
|
});
|
|
1741
|
+
const auditDurationMs = Date.now() - auditStartMs;
|
|
1724
1742
|
latestAuditProgress = null;
|
|
1725
1743
|
// Audit history: record REAL verdicts only โ a non-empty report is the
|
|
1726
1744
|
// evidence the auditor actually inspected something. Empty-report runs
|
|
@@ -1746,7 +1764,8 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
1746
1764
|
error: result.error,
|
|
1747
1765
|
regressionShieldPassed: result.regressionShieldPassed,
|
|
1748
1766
|
regressionShieldMissing: result.regressionShieldMissing,
|
|
1749
|
-
|
|
1767
|
+
durationMs: auditDurationMs,
|
|
1768
|
+
} as any);
|
|
1750
1769
|
// Cap history โ 39 infra errors taught us unbounded growth is real.
|
|
1751
1770
|
if (history.length > 20) history.splice(0, history.length - 20);
|
|
1752
1771
|
// v0.25.4: durable append-only audit log โ survives state-snapshot
|
|
@@ -1771,7 +1790,9 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
1771
1790
|
report: cleanOutput,
|
|
1772
1791
|
impossibleReason: result.impossibleReason,
|
|
1773
1792
|
error: result.error,
|
|
1774
|
-
|
|
1793
|
+
durationMs: auditDurationMs,
|
|
1794
|
+
retriedOnce,
|
|
1795
|
+
} as AuditLogEntry);
|
|
1775
1796
|
}
|
|
1776
1797
|
|
|
1777
1798
|
// Escape hatch: the user aborted the audit (Esc). Offer the explicit
|
|
@@ -1897,14 +1918,14 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
1897
1918
|
updateGoal({
|
|
1898
1919
|
status: "active",
|
|
1899
1920
|
auditHistory: history,
|
|
1900
|
-
pauseReason: `auditor infrastructure: ${result.error}`,
|
|
1921
|
+
pauseReason: `auditor infrastructure${retriedOnce ? " (retried once)" : ""}: ${result.error}`,
|
|
1901
1922
|
pauseSuggestedAction: "Fix the auditor model (/glla model=provider/id) and call complete_goal again โ your work was NOT judged",
|
|
1902
1923
|
}, ctx);
|
|
1903
1924
|
scheduleContinuation(ctx, true);
|
|
1904
1925
|
return {
|
|
1905
1926
|
content: [{
|
|
1906
1927
|
type: "text",
|
|
1907
|
-
text: `The auditor could not run (infrastructure, NOT a verdict): ${result.error}\nYour completion claim was not evaluated. Fix the auditor model with /glla model=provider/id and call complete_goal again โ do not change your deliverable for this.`,
|
|
1928
|
+
text: `The auditor could not run (infrastructure, NOT a verdict${retriedOnce ? "; retried once with backoff, both attempts failed" : ""}): ${result.error}\nYour completion claim was not evaluated. Fix the auditor model with /glla model=provider/id and call complete_goal again โ do not change your deliverable for this.`,
|
|
1908
1929
|
}],
|
|
1909
1930
|
details: {},
|
|
1910
1931
|
};
|
|
@@ -2647,6 +2668,8 @@ async function openSettingsUI(ctx: ExtensionContext): Promise<void> {
|
|
|
2647
2668
|
`Stuck max interventions โ ${show("stuckMaxInterventions", "(5 default)")}`,
|
|
2648
2669
|
`Subagent model strategy โ ${show("subagentModelStrategy", "(inherit-parent)")}`,
|
|
2649
2670
|
`Subagent Explore model pin โ ${loadSettings(ctx.cwd).subagentModelOverrides?.Explore ?? "(follows strategy)"}`,
|
|
2671
|
+
`Subagent Plan model pin โ ${loadSettings(ctx.cwd).subagentModelOverrides?.Plan ?? "(follows strategy)"}`,
|
|
2672
|
+
`Subagent general-purpose model pin โ ${loadSettings(ctx.cwd).subagentModelOverrides?.["general-purpose"] ?? "(follows strategy)"}`,
|
|
2650
2673
|
`Audit feedback characters โ ${show("auditFeedbackChars", "(full report)")}`,
|
|
2651
2674
|
"Done",
|
|
2652
2675
|
],
|
|
@@ -2716,15 +2739,16 @@ async function openSettingsUI(ctx: ExtensionContext): Promise<void> {
|
|
|
2716
2739
|
saveSettings("global", ctx.cwd, { subagentModelStrategy: strategy });
|
|
2717
2740
|
ctx.ui.notify("Subagent model strategy saved โ applies to NEW pi sessions (pi-subagents registers agents at session start).", "info");
|
|
2718
2741
|
}
|
|
2719
|
-
} else if (
|
|
2720
|
-
const
|
|
2742
|
+
} else if (/^Subagent (Explore|Plan|general-purpose) model pin/.test(choice)) {
|
|
2743
|
+
const agentType = choice.match(/^Subagent (Explore|Plan|general-purpose) model pin/)![1]!;
|
|
2744
|
+
const v = await ctx.ui.input(`Model pin for ${agentType} subagents`, "provider/model-id e.g. minimax/MiniMax-M3 โ always wins over strategy; empty = follow strategy");
|
|
2721
2745
|
if (v !== undefined) {
|
|
2722
2746
|
const current = loadSettings(ctx.cwd).subagentModelOverrides ?? {};
|
|
2723
2747
|
const next = { ...current };
|
|
2724
|
-
if (v.trim()) next
|
|
2725
|
-
else delete next
|
|
2748
|
+
if (v.trim()) next[agentType] = v.trim();
|
|
2749
|
+
else delete next[agentType];
|
|
2726
2750
|
saveSettings("global", ctx.cwd, { subagentModelOverrides: Object.keys(next).length > 0 ? next : undefined });
|
|
2727
|
-
ctx.ui.notify(
|
|
2751
|
+
ctx.ui.notify(`${agentType} model pin saved โ applies to NEW pi sessions.`, "info");
|
|
2728
2752
|
}
|
|
2729
2753
|
} else if (choice.startsWith("Audit feedback")) {
|
|
2730
2754
|
const v = await ctx.ui.input("Auditor feedback returned to the executor (characters)", "non-negative integer cap; 0 or empty = full report (default)");
|
|
@@ -2785,12 +2809,28 @@ function cmdStats(args: string, ctx: ExtensionContext): void {
|
|
|
2785
2809
|
*/
|
|
2786
2810
|
function cmdAudits(args: string, ctx: ExtensionContext): void {
|
|
2787
2811
|
const full = /\bfull\b/.test(args);
|
|
2812
|
+
const all = /\b(?:all|global|log)\b/.test(args);
|
|
2788
2813
|
const nMatch = args.match(/\b(\d+)\b/);
|
|
2789
2814
|
if (full) {
|
|
2815
|
+
// Latest report โ active goal's history first, then the log.
|
|
2816
|
+
const fromGoal = state.goal?.auditHistory?.at(-1);
|
|
2817
|
+
if (fromGoal?.report) {
|
|
2818
|
+
ctx.ui.notify(`Latest audit on this goal โ ${fromGoal.model} (${fromGoal.at})\n${fromGoal.report}`, "info");
|
|
2819
|
+
return;
|
|
2820
|
+
}
|
|
2790
2821
|
const latest = readAuditLog(ctx.cwd).at(-1);
|
|
2791
2822
|
ctx.ui.notify(latest ? `Latest audit โ ${latest.verdict} (${latest.model}, ${latest.at})\n${latest.report}` : "No audits logged yet.", "info");
|
|
2792
2823
|
return;
|
|
2793
2824
|
}
|
|
2825
|
+
// Default: the ACTIVE goal's own audit history (with per-audit elapsed);
|
|
2826
|
+
// "all"/"global"/"log" browses the durable cross-goal log.
|
|
2827
|
+
if (!all && state.goal?.auditHistory && state.goal.auditHistory.length > 0) {
|
|
2828
|
+
ctx.ui.notify(
|
|
2829
|
+
`glla audits โ this goal's history (${state.goal.auditHistory.length} verdict(s); /glla audits all for the project log)\n${formatGoalAuditHistory(state.goal)}`,
|
|
2830
|
+
"info",
|
|
2831
|
+
);
|
|
2832
|
+
return;
|
|
2833
|
+
}
|
|
2794
2834
|
const n = nMatch ? Number(nMatch[1]) : 10;
|
|
2795
2835
|
const entries = readAuditLog(ctx.cwd, n);
|
|
2796
2836
|
ctx.ui.notify(`glla audits โ last ${entries.length} verdict(s) in ${ctx.cwd}\n${formatAuditLog(entries)}`, "info");
|
|
@@ -2843,6 +2883,10 @@ async function cmdSettings(args: string, ctx: ExtensionContext): Promise<void> {
|
|
|
2843
2883
|
fmt("aggressiveMode", "aggressiveMode"),
|
|
2844
2884
|
fmt("quotaRetryMinutes", "quotaRetryMinutes"),
|
|
2845
2885
|
fmt("stuckMaxInterventions", "stuckMaxInterventions"),
|
|
2886
|
+
// v0.25.6: effective per-type subagent model resolution.
|
|
2887
|
+
...["Explore", "Plan", "general-purpose"].map(
|
|
2888
|
+
(t) => `subagent ${t}: ${resolveEffectiveSubagentModel(t, loadSettings(ctx.cwd), (ctx.model as any)?.id ? `${(ctx.model as any).provider}/${(ctx.model as any).id}` : undefined)}`,
|
|
2889
|
+
),
|
|
2846
2890
|
`\nglobal: ${globalSettingsPath()}`,
|
|
2847
2891
|
`project: ${projectSettingsPath(ctx.cwd)}`,
|
|
2848
2892
|
`Set with: /glla key=value (global) ยท /glla project key=value (project override)`,
|
|
@@ -3217,6 +3261,18 @@ export default function (pi: ExtensionAPI): void {
|
|
|
3217
3261
|
state.goal.telemetry = t;
|
|
3218
3262
|
}
|
|
3219
3263
|
}
|
|
3264
|
+
// v0.25.6: subagent quota errors (the pi-subagents#175 shape โ
|
|
3265
|
+
// Explore's upstream haiku pin 403s on shared keys). Surface the
|
|
3266
|
+
// repair path immediately; the continuation prompt's WHEN SUBAGENTS
|
|
3267
|
+
// HIT QUOTA ERRORS section carries the full guidance.
|
|
3268
|
+
if (isSubagentQuotaResult(String(event?.toolName ?? ""), Boolean(event?.isError ?? event?.error), event?.output ?? event?.result ?? event?.details ?? "")) {
|
|
3269
|
+
const errText = typeof (event?.output ?? event?.result) === "string" ? (event?.output ?? event?.result) : JSON.stringify(event?.output ?? event?.result ?? event?.details ?? "");
|
|
3270
|
+
appendLedger(registeredCtx?.cwd ?? process.cwd(), "subagent_quota_error", { error: String(errText).slice(0, 200) });
|
|
3271
|
+
registeredCtx?.ui.notify(
|
|
3272
|
+
"Subagent hit a quota error (403/limit). Repair: re-spawn with an explicit model= on your quota pool, or do the work inline โ see the continuation prompt's WHEN SUBAGENTS HIT QUOTA ERRORS. Explore's upstream haiku pin is the usual cause (pi-subagents#175); glla's inherit-parent strategy removes it for NEW sessions.",
|
|
3273
|
+
"warning",
|
|
3274
|
+
);
|
|
3275
|
+
}
|
|
3220
3276
|
if (draftingTarget === null) return;
|
|
3221
3277
|
if (askUserQuestionAnswered(String(event?.toolName ?? ""), event?.details)) {
|
|
3222
3278
|
draftingUserReplies++;
|
|
@@ -3250,6 +3306,15 @@ export default function (pi: ExtensionAPI): void {
|
|
|
3250
3306
|
for (const skip of sync.skipped) {
|
|
3251
3307
|
ctx.ui.notify(`glla subagent override skipped [${skip.name}]: ${skip.reason}`, "warning");
|
|
3252
3308
|
}
|
|
3309
|
+
// v0.25.6: notify-with-repair โ a managed override that went missing
|
|
3310
|
+
// or was altered externally (pi update, manual edit, sync churn) is
|
|
3311
|
+
// re-written AND surfaced, not silently restored.
|
|
3312
|
+
if (sync.repaired.length > 0) {
|
|
3313
|
+
ctx.ui.notify(
|
|
3314
|
+
`glla repaired managed subagent override(s): ${sync.repaired.join(", ")} โ the file(s) were missing or altered externally; re-written per your subagent settings.`,
|
|
3315
|
+
"warning",
|
|
3316
|
+
);
|
|
3317
|
+
}
|
|
3253
3318
|
} catch (err) {
|
|
3254
3319
|
ctx.ui.notify(`glla subagent override sync failed: ${err instanceof Error ? err.message : String(err)}`, "warning");
|
|
3255
3320
|
}
|
|
@@ -88,3 +88,13 @@ export function scheduleQuotaRetry(
|
|
|
88
88
|
"info",
|
|
89
89
|
);
|
|
90
90
|
}
|
|
91
|
+
|
|
92
|
+
/** v0.25.6: detect a SUBAGENT quota failure in a tool_result โ the
|
|
93
|
+
* pi-subagents#175 shape (Explore's upstream haiku pin 403s on shared
|
|
94
|
+
* keys). Tool must be an Agent spawn and the payload a quota error. */
|
|
95
|
+
export function isSubagentQuotaResult(toolName: string, isError: boolean, payload: unknown): boolean {
|
|
96
|
+
if (!isError) return false;
|
|
97
|
+
if (toolName !== "Agent" && toolName !== "agent") return false;
|
|
98
|
+
const text = typeof payload === "string" ? payload : JSON.stringify(payload ?? "");
|
|
99
|
+
return isQuotaError(text);
|
|
100
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goal-list-loop-audit",
|
|
3
|
-
"version": "0.25.
|
|
3
|
+
"version": "0.25.6",
|
|
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",
|