pi-plans 0.3.2 → 0.3.3
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/README.md +5 -3
- package/index.ts +92 -6
- package/package.json +1 -1
- package/references/pi-planning-workflow.md +7 -1
- package/references/plan-artifact-template.md +4 -0
- package/references/state-and-config.md +19 -0
- package/src/autocomplete.ts +2 -1
- package/src/code-graph/commands.ts +21 -6
- package/src/compaction.ts +10 -0
- package/src/config-command.ts +2 -0
- package/src/exec.ts +256 -10
- package/src/guard.ts +7 -1
- package/src/resume-command.ts +450 -0
- package/src/resume.ts +205 -0
- package/src/run-context.ts +97 -0
- package/src/run-ownership.ts +310 -0
- package/src/state.ts +24 -1
- package/src/termination-prompt.ts +8 -0
- package/src/workflow-state.ts +1159 -0
- package/tests/ask-choice.test.ts +113 -0
- package/tests/compaction.test.ts +39 -0
- package/tests/exec.test.ts +250 -0
- package/tests/guard.test.ts +46 -0
- package/tests/plans.test.ts +71 -0
- package/tests/refine-resume.test.ts +324 -0
- package/tests/resume-lifecycle.test.ts +385 -0
- package/tests/resume.test.ts +384 -0
- package/tests/run-context.test.ts +119 -0
- package/tests/run-ownership.test.ts +170 -0
- package/tests/state.test.ts +16 -0
- package/tests/workflow-state.test.ts +432 -0
- package/tools/analyze-refs.ts +2 -1
- package/tools/ask-choice.ts +61 -3
- package/tools/code-graph.ts +5 -1
- package/tools/execute-plan.ts +2 -1
- package/tools/plans.ts +119 -0
- package/tools/refine.ts +91 -7
package/tools/plans.ts
CHANGED
|
@@ -3,6 +3,21 @@
|
|
|
3
3
|
* typed tool.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { bindRun, boundRunId, resolveActiveRun } from "../src/run-context.ts";
|
|
7
|
+
import { getExecution, markPrePlanCompactPending } from "../src/exec.ts";
|
|
8
|
+
import { loadVccSettings, scaffoldVccSettings } from "../src/compaction.ts";
|
|
9
|
+
import {
|
|
10
|
+
applyCompleted,
|
|
11
|
+
applyImplementationReviewConfigured,
|
|
12
|
+
applyImplementationRoundFinished,
|
|
13
|
+
applyPlanWritten,
|
|
14
|
+
applyReviewConsolidated,
|
|
15
|
+
createCheckpoint,
|
|
16
|
+
mutateCheckpoint,
|
|
17
|
+
planIdentityOf,
|
|
18
|
+
type WorkflowCheckpoint,
|
|
19
|
+
} from "../src/workflow-state.ts";
|
|
20
|
+
import * as path from "node:path";
|
|
6
21
|
import { StringEnum } from "@earendil-works/pi-ai";
|
|
7
22
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
8
23
|
import { Type } from "typebox";
|
|
@@ -47,6 +62,7 @@ const PlansParams = Type.Object({
|
|
|
47
62
|
"record-decision",
|
|
48
63
|
"record-ref",
|
|
49
64
|
"record-subagent",
|
|
65
|
+
"record-checkpoint",
|
|
50
66
|
] as const,
|
|
51
67
|
{ description: "State command to run" },
|
|
52
68
|
),
|
|
@@ -108,6 +124,29 @@ const PlansParams = Type.Object({
|
|
|
108
124
|
sessionDir: Type.Optional(Type.String()),
|
|
109
125
|
}),
|
|
110
126
|
),
|
|
127
|
+
checkpoint: Type.Optional(
|
|
128
|
+
Type.Object({
|
|
129
|
+
/** Whitelisted semantic transition (I-003). State-machine validated; approval cannot be forged here. */
|
|
130
|
+
transition: StringEnum(
|
|
131
|
+
[
|
|
132
|
+
"plan-written",
|
|
133
|
+
"review-consolidated",
|
|
134
|
+
"implementation-review-configured",
|
|
135
|
+
"implementation-round-finished",
|
|
136
|
+
"completed",
|
|
137
|
+
] as const,
|
|
138
|
+
),
|
|
139
|
+
/** plan-written: absolute or workdir-relative PLAN_vN.md path. */
|
|
140
|
+
planPath: Type.Optional(Type.String()),
|
|
141
|
+
/** review-consolidated: round id + optional disposition artifact (run-dir relative). */
|
|
142
|
+
roundId: Type.Optional(Type.String()),
|
|
143
|
+
dispositionArtifact: Type.Optional(Type.String()),
|
|
144
|
+
/** implementation-review-configured: the serialized termination condition chosen by the user. */
|
|
145
|
+
terminationCondition: Type.Optional(Type.String()),
|
|
146
|
+
/** completed: non-empty evidence that the termination condition is satisfied. */
|
|
147
|
+
evidence: Type.Optional(Type.String()),
|
|
148
|
+
}),
|
|
149
|
+
),
|
|
111
150
|
});
|
|
112
151
|
|
|
113
152
|
// Module-level reference so the start-run action can append a session entry
|
|
@@ -168,6 +207,59 @@ export async function finalCommit(
|
|
|
168
207
|
}
|
|
169
208
|
}
|
|
170
209
|
|
|
210
|
+
/** Whitelisted, state-machine-validated checkpoint transitions (I-003). */
|
|
211
|
+
export function recordCheckpointTransition(
|
|
212
|
+
ctx: { sessionManager: unknown },
|
|
213
|
+
workdir: string,
|
|
214
|
+
runIdArg: string | undefined,
|
|
215
|
+
checkpoint: {
|
|
216
|
+
transition: "plan-written" | "review-consolidated" | "implementation-review-configured" | "implementation-round-finished" | "completed";
|
|
217
|
+
planPath?: string;
|
|
218
|
+
roundId?: string;
|
|
219
|
+
dispositionArtifact?: string;
|
|
220
|
+
terminationCondition?: string;
|
|
221
|
+
evidence?: string;
|
|
222
|
+
},
|
|
223
|
+
): WorkflowCheckpoint {
|
|
224
|
+
const runId =
|
|
225
|
+
runIdArg ??
|
|
226
|
+
boundRunId(ctx.sessionManager, workdir) ??
|
|
227
|
+
resolveActiveRun(ctx.sessionManager, workdir)?.run_id ??
|
|
228
|
+
null;
|
|
229
|
+
if (!runId) throw new StateError("record-checkpoint requires runId (or an active/bound run)");
|
|
230
|
+
switch (checkpoint.transition) {
|
|
231
|
+
case "plan-written": {
|
|
232
|
+
if (!checkpoint.planPath) throw new StateError("plan-written requires planPath");
|
|
233
|
+
const planPath = path.isAbsolute(checkpoint.planPath)
|
|
234
|
+
? checkpoint.planPath
|
|
235
|
+
: path.resolve(workdir, checkpoint.planPath.replace(/^@/, ""));
|
|
236
|
+
const identity = planIdentityOf(planPath, 1);
|
|
237
|
+
return mutateCheckpoint(workdir, runId, (cp) => applyPlanWritten(cp, identity));
|
|
238
|
+
}
|
|
239
|
+
case "review-consolidated": {
|
|
240
|
+
if (!checkpoint.roundId) throw new StateError("review-consolidated requires roundId");
|
|
241
|
+
return mutateCheckpoint(workdir, runId, (cp) =>
|
|
242
|
+
applyReviewConsolidated(cp, checkpoint.roundId!, checkpoint.dispositionArtifact),
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
case "implementation-review-configured": {
|
|
246
|
+
if (!checkpoint.terminationCondition) {
|
|
247
|
+
throw new StateError("implementation-review-configured requires terminationCondition");
|
|
248
|
+
}
|
|
249
|
+
return mutateCheckpoint(workdir, runId, (cp) =>
|
|
250
|
+
applyImplementationReviewConfigured(cp, checkpoint.terminationCondition!),
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
case "implementation-round-finished": {
|
|
254
|
+
return mutateCheckpoint(workdir, runId, (cp) => applyImplementationRoundFinished(cp));
|
|
255
|
+
}
|
|
256
|
+
case "completed": {
|
|
257
|
+
if (!checkpoint.evidence) throw new StateError("completed requires evidence");
|
|
258
|
+
return mutateCheckpoint(workdir, runId, (cp) => applyCompleted(cp, checkpoint.evidence!));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
171
263
|
export function registerPlansTool(pi: ExtensionAPI): void {
|
|
172
264
|
setRunStartAppender((runId, artifactDir) => {
|
|
173
265
|
pi.appendEntry("pi-plans-run-start", { runId, artifactDir });
|
|
@@ -264,6 +356,28 @@ export function registerPlansTool(pi: ExtensionAPI): void {
|
|
|
264
356
|
runStartAppender?.(run.run_id, run.artifact_dir);
|
|
265
357
|
},
|
|
266
358
|
});
|
|
359
|
+
// I-002: attribute this session's work to the run it started.
|
|
360
|
+
bindRun(ctx.sessionManager, workdir, result.run.run_id);
|
|
361
|
+
// I-003: durable cross-session state starts with the run.
|
|
362
|
+
createCheckpoint(workdir, {
|
|
363
|
+
runId: result.run.run_id,
|
|
364
|
+
originWorkdir: workdir,
|
|
365
|
+
workdir,
|
|
366
|
+
});
|
|
367
|
+
// Pre-plan compaction: mark the session so the plans tool_result
|
|
368
|
+
// hook compacts once with the VCC planning path before the first
|
|
369
|
+
// planning question. Opportunistic: never blocks run creation.
|
|
370
|
+
try {
|
|
371
|
+
const prePlanStateRoot = resolveStateRootOrNull(workdir);
|
|
372
|
+
if (prePlanStateRoot && !getExecution()) {
|
|
373
|
+
scaffoldVccSettings(prePlanStateRoot);
|
|
374
|
+
if (loadVccSettings(prePlanStateRoot).prePlanCompact) {
|
|
375
|
+
markPrePlanCompactPending(ctx, result.run.run_id);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
} catch {
|
|
379
|
+
// best-effort: pre-plan compaction is an optimization only
|
|
380
|
+
}
|
|
267
381
|
break;
|
|
268
382
|
}
|
|
269
383
|
case "set-status": {
|
|
@@ -295,6 +409,11 @@ export function registerPlansTool(pi: ExtensionAPI): void {
|
|
|
295
409
|
result = recordSubagent(workdir, params.runId, params.subagent);
|
|
296
410
|
break;
|
|
297
411
|
}
|
|
412
|
+
case "record-checkpoint": {
|
|
413
|
+
if (!params.checkpoint) throw new StateError("record-checkpoint requires checkpoint");
|
|
414
|
+
result = recordCheckpointTransition(ctx, workdir, params.runId, params.checkpoint);
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
298
417
|
}
|
|
299
418
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], details: {} };
|
|
300
419
|
} catch (error) {
|
package/tools/refine.ts
CHANGED
|
@@ -15,6 +15,14 @@ import { Type } from "typebox";
|
|
|
15
15
|
import * as fs from "node:fs";
|
|
16
16
|
import * as path from "node:path";
|
|
17
17
|
import { loadConfig, normalizeWorkdir, readActive, recordSubagent, resolveStateRootOrNull, StateError, type RoleConfig } from "../src/state.ts";
|
|
18
|
+
import { resolveActiveRun } from "../src/run-context.ts";
|
|
19
|
+
import {
|
|
20
|
+
loadCheckpoint,
|
|
21
|
+
readReviewOutput,
|
|
22
|
+
recordLaneOutcome,
|
|
23
|
+
reusableLaneOutputs,
|
|
24
|
+
startReviewRound,
|
|
25
|
+
} from "../src/workflow-state.ts";
|
|
18
26
|
import { buildCriticizerTask, buildImplementationCriticizerTask, buildImplementationReviewerTask, buildReviewerTask, reviewerLanes } from "../src/refine-prompts.ts";
|
|
19
27
|
import { graphBlockForRefiner } from "../src/code-graph/prompts.ts";
|
|
20
28
|
import { runPiSubagent, stripFrontmatter } from "../src/subagent.ts";
|
|
@@ -41,6 +49,12 @@ const RefineParams = Type.Object({
|
|
|
41
49
|
context: Type.Optional(
|
|
42
50
|
Type.String({ description: "Context for the subagents: user goals, repo evidence, constraints, open questions" }),
|
|
43
51
|
),
|
|
52
|
+
resumeRoundId: Type.Optional(
|
|
53
|
+
Type.String({
|
|
54
|
+
description:
|
|
55
|
+
'Round id to resume (I-004). Lanes already complete for this round in the run checkpoint are reused from their persisted outputs; only pending/failed/missing lanes run. Never reuse a round id across plan versions.',
|
|
56
|
+
}),
|
|
57
|
+
),
|
|
44
58
|
workdir: Type.Optional(Type.String({ description: "Target workspace; default current working directory" })),
|
|
45
59
|
});
|
|
46
60
|
|
|
@@ -119,7 +133,7 @@ export function registerRefineTool(pi: ExtensionAPI, baseDir: string): void {
|
|
|
119
133
|
const planText = fs.readFileSync(planPath, "utf8");
|
|
120
134
|
|
|
121
135
|
// Record spawns against the active run when one exists.
|
|
122
|
-
const active =
|
|
136
|
+
const active = resolveActiveRun(ctx.sessionManager, workdir);
|
|
123
137
|
const record = (name: string, model?: string | null) => {
|
|
124
138
|
if (!active) return;
|
|
125
139
|
try {
|
|
@@ -130,6 +144,44 @@ export function registerRefineTool(pi: ExtensionAPI, baseDir: string): void {
|
|
|
130
144
|
};
|
|
131
145
|
|
|
132
146
|
const target = params.target ?? "plan";
|
|
147
|
+
// I-004: durable round bookkeeping. Rounds start (or resume) in the
|
|
148
|
+
// checkpoint BEFORE any lane spawns; successful outputs are persisted
|
|
149
|
+
// BEFORE the tool result returns (C-007).
|
|
150
|
+
const checkpointLoad = active ? loadCheckpoint(workdir, active.run_id) : null;
|
|
151
|
+
const useCheckpoint = checkpointLoad?.status === "ok" ? checkpointLoad.checkpoint : null;
|
|
152
|
+
const roundId =
|
|
153
|
+
params.resumeRoundId ?? `${target}-${params.role}-r${Date.now().toString(36)}`;
|
|
154
|
+
const roundReviewerCount = params.role === "reviewer" ? Math.min(3, Math.max(1, params.reviewers ?? 1)) : 1;
|
|
155
|
+
// F-001 (implementation review): the spec MUST carry lanes —
|
|
156
|
+
// reviewerLanes(count) for reviewer rounds, one lane for criticizer.
|
|
157
|
+
const roundLanes =
|
|
158
|
+
params.role === "reviewer"
|
|
159
|
+
? reviewerLanes(roundReviewerCount).map((lane) => ({ laneId: lane.id, lens: lane.lens ?? undefined }))
|
|
160
|
+
: [{ laneId: "criticizer" }];
|
|
161
|
+
const roundSpec = {
|
|
162
|
+
roundId,
|
|
163
|
+
role: params.role,
|
|
164
|
+
target,
|
|
165
|
+
reviewers: roundReviewerCount,
|
|
166
|
+
planPath,
|
|
167
|
+
focus: params.focus,
|
|
168
|
+
context: params.context,
|
|
169
|
+
lanes: roundLanes,
|
|
170
|
+
};
|
|
171
|
+
if (useCheckpoint) {
|
|
172
|
+
startReviewRound(workdir, active!.run_id, roundSpec);
|
|
173
|
+
}
|
|
174
|
+
const reusable = useCheckpoint
|
|
175
|
+
? Object.fromEntries(reusableLaneOutputs(useCheckpoint, roundId).map((entry) => [entry.laneId, entry.resultFile]))
|
|
176
|
+
: {};
|
|
177
|
+
const persistOutcome = (laneId: string, result: { ok: boolean; output?: string; error?: string }): void => {
|
|
178
|
+
if (!active || !useCheckpoint) return;
|
|
179
|
+
try {
|
|
180
|
+
recordLaneOutcome(workdir, active.run_id, roundId, laneId, result);
|
|
181
|
+
} catch {
|
|
182
|
+
/* the subagents ledger still records the spawn; resume treats the lane as unfinished */
|
|
183
|
+
}
|
|
184
|
+
};
|
|
133
185
|
const pickTask = (role: "reviewer" | "criticizer", lens: string | null): string => {
|
|
134
186
|
if (role === "reviewer") {
|
|
135
187
|
return target === "implementation"
|
|
@@ -163,6 +215,19 @@ export function registerRefineTool(pi: ExtensionAPI, baseDir: string): void {
|
|
|
163
215
|
}
|
|
164
216
|
|
|
165
217
|
if (params.role === "criticizer") {
|
|
218
|
+
const laneId = "criticizer";
|
|
219
|
+
const persisted = reusable[laneId];
|
|
220
|
+
if (persisted) {
|
|
221
|
+
return {
|
|
222
|
+
content: [
|
|
223
|
+
{
|
|
224
|
+
type: "text",
|
|
225
|
+
text: `${readReviewOutput(workdir, active!.run_id, persisted)}\n\n---\nReused the persisted criticizer result for round ${roundId} (no re-run). Ask each criticizer question with ask_choice (one call per question, in the configured language), record every answer, then revise the plan only after every question has an answer.`,
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
details: { mode: "delegated-subagent", role: params.role, planPath, target, roundId, reused: true },
|
|
229
|
+
};
|
|
230
|
+
}
|
|
166
231
|
const name = `${roleConfig.name_prefix}-criticizer-${Date.now().toString(36)}`;
|
|
167
232
|
const execution = setupRefinementExecution(ctx, signal, "criticizer", [{ id: name, label: "criticizer" }], modelLabel);
|
|
168
233
|
try {
|
|
@@ -177,6 +242,7 @@ export function registerRefineTool(pi: ExtensionAPI, baseDir: string): void {
|
|
|
177
242
|
});
|
|
178
243
|
execution.overlay?.complete(name, result);
|
|
179
244
|
record(name, result.ok ? result.model ?? model : null);
|
|
245
|
+
persistOutcome(laneId, result.ok ? { ok: true, output: result.output } : { ok: false, error: result.errorMessage });
|
|
180
246
|
if (!result.ok) {
|
|
181
247
|
throw new Error(
|
|
182
248
|
`criticizer subagent failed: ${result.errorMessage ?? "unknown error"}${result.stderr ? `\nstderr: ${result.stderr.slice(0, 2000)}` : ""}`,
|
|
@@ -186,10 +252,10 @@ export function registerRefineTool(pi: ExtensionAPI, baseDir: string): void {
|
|
|
186
252
|
content: [
|
|
187
253
|
{
|
|
188
254
|
type: "text",
|
|
189
|
-
text: `${result.output}\n\n---\nAsk each criticizer question with ask_choice (one call per question, in the configured language), record every answer, then revise the plan only after every question has an answer.`,
|
|
255
|
+
text: `${result.output}\n\n---\nAsk each criticizer question with ask_choice (one call per question, in the configured language, with a stable questionId per question), record every answer, then revise the plan only after every question has an answer. After the revision, record the boundary: plans record-checkpoint (checkpoint: { transition: "review-consolidated", roundId: "${roundId}" }).`,
|
|
190
256
|
},
|
|
191
257
|
],
|
|
192
|
-
details: { mode: "delegated-subagent", role: params.role, planPath, target, model: result.model ?? model },
|
|
258
|
+
details: { mode: "delegated-subagent", role: params.role, planPath, target, roundId, model: result.model ?? model },
|
|
193
259
|
};
|
|
194
260
|
} finally {
|
|
195
261
|
await execution.close();
|
|
@@ -213,16 +279,19 @@ export function registerRefineTool(pi: ExtensionAPI, baseDir: string): void {
|
|
|
213
279
|
return round;
|
|
214
280
|
};
|
|
215
281
|
|
|
282
|
+
// Lane-level resume (F-007): completed lanes are reused from their
|
|
283
|
+
// persisted outputs; only pending/failed/missing lanes spawn.
|
|
284
|
+
const runnableJobs = jobs.filter((job) => reusable[job.lane.id] === undefined);
|
|
216
285
|
const execution = setupRefinementExecution(
|
|
217
286
|
ctx,
|
|
218
287
|
signal,
|
|
219
288
|
"reviewer",
|
|
220
|
-
|
|
289
|
+
runnableJobs.map((job) => ({ id: job.lane.id, label: job.lane.id })),
|
|
221
290
|
modelLabel,
|
|
222
291
|
);
|
|
223
292
|
try {
|
|
224
293
|
const results = await Promise.all(
|
|
225
|
-
|
|
294
|
+
runnableJobs.map(async (job) => {
|
|
226
295
|
try {
|
|
227
296
|
const result = await runPiSubagent({
|
|
228
297
|
systemPrompt: `${systemPrompt}\n\n${graphPrompt}`,
|
|
@@ -235,6 +304,9 @@ export function registerRefineTool(pi: ExtensionAPI, baseDir: string): void {
|
|
|
235
304
|
});
|
|
236
305
|
execution.overlay?.complete(job.lane.id, result);
|
|
237
306
|
record(job.name, result.ok ? result.model ?? model : null);
|
|
307
|
+
// Persist BEFORE returning (C-007): a crash after this point
|
|
308
|
+
// still leaves the lane reusable.
|
|
309
|
+
persistOutcome(job.lane.id, result.ok ? { ok: true, output: result.output } : { ok: false, error: result.errorMessage });
|
|
238
310
|
if (target === "implementation" && result.ok) {
|
|
239
311
|
try {
|
|
240
312
|
pi.appendEntry("pi-plans-ameliorate", {
|
|
@@ -251,6 +323,7 @@ export function registerRefineTool(pi: ExtensionAPI, baseDir: string): void {
|
|
|
251
323
|
} catch (error) {
|
|
252
324
|
record(job.name, null);
|
|
253
325
|
const message = error instanceof Error ? error.message : String(error);
|
|
326
|
+
persistOutcome(job.lane.id, { ok: false, error: message });
|
|
254
327
|
const result = {
|
|
255
328
|
ok: false,
|
|
256
329
|
output: "",
|
|
@@ -267,6 +340,15 @@ export function registerRefineTool(pi: ExtensionAPI, baseDir: string): void {
|
|
|
267
340
|
|
|
268
341
|
const sections: string[] = [];
|
|
269
342
|
let failures = 0;
|
|
343
|
+
let reusedCount = 0;
|
|
344
|
+
// Reused lanes first (stable order): outputs come from the persisted files.
|
|
345
|
+
for (const job of jobs) {
|
|
346
|
+
const persisted = reusable[job.lane.id];
|
|
347
|
+
if (persisted === undefined) continue;
|
|
348
|
+
reusedCount += 1;
|
|
349
|
+
const title = job.lane.lens ? `${job.name} — ${job.lane.lens}` : job.name;
|
|
350
|
+
sections.push(`### ${title} — REUSED (round ${roundId}, no re-run)\n${readReviewOutput(workdir, active!.run_id, persisted)}`);
|
|
351
|
+
}
|
|
270
352
|
for (const { job, result } of results) {
|
|
271
353
|
const title = job.lane.lens ? `${job.name} — ${job.lane.lens}` : job.name;
|
|
272
354
|
if (!result.ok) {
|
|
@@ -276,7 +358,7 @@ export function registerRefineTool(pi: ExtensionAPI, baseDir: string): void {
|
|
|
276
358
|
}
|
|
277
359
|
sections.push(`### ${title}\n${result.output}`);
|
|
278
360
|
}
|
|
279
|
-
if (failures === results.length) {
|
|
361
|
+
if (failures === results.length && reusedCount === 0) {
|
|
280
362
|
const first = results[0];
|
|
281
363
|
throw new Error(
|
|
282
364
|
`all reviewer subagents failed: ${first?.result.errorMessage ?? "unknown error"}${first?.result.stderr ? `\nstderr: ${first.result.stderr.slice(0, 2000)}` : ""}${model ? `\nIf the model selector "${model}" is unavailable, reset the confirmation (plans set-role --reset-confirmation) and re-ask the model-confirmation question.` : ""}`,
|
|
@@ -292,13 +374,15 @@ export function registerRefineTool(pi: ExtensionAPI, baseDir: string): void {
|
|
|
292
374
|
content: [
|
|
293
375
|
{
|
|
294
376
|
type: "text",
|
|
295
|
-
text: `${text}\n\n---\nConsolidate: merge and dedupe findings into PLAN_vN_reviewer_comments.md${count === 3 ? " (one consolidated file; keep each finding's source reviewer, severity, evidence, and disposition)" : ""}, accept or reject each finding on repo/reference evidence, surface at most five high-priority findings to the user, then immediately ask the next refinement-mode question with ask_choice
|
|
377
|
+
text: `${text}\n\n---\nConsolidate: merge and dedupe findings into PLAN_vN_reviewer_comments.md${count === 3 ? " (one consolidated file; keep each finding's source reviewer, severity, evidence, and disposition)" : ""}, accept or reject each finding on repo/reference evidence, surface at most five high-priority findings to the user, then immediately ask the next refinement-mode question with ask_choice. Then record the boundary: plans record-checkpoint (checkpoint: { transition: "review-consolidated", roundId: "${roundId}", dispositionArtifact: "<comments file, run-dir relative>" }).${target === "implementation" ? ' When the whole round is disposed, also record (checkpoint: { transition: "implementation-round-finished" }); when the termination condition is met, close with (checkpoint: { transition: "completed", evidence: "<why the condition is satisfied>" }).' : ""}`,
|
|
296
378
|
},
|
|
297
379
|
],
|
|
298
380
|
details: {
|
|
299
381
|
mode: "delegated-subagent",
|
|
300
382
|
role: "reviewer",
|
|
301
383
|
planPath,
|
|
384
|
+
roundId,
|
|
385
|
+
reusedLanes: Object.keys(reusable),
|
|
302
386
|
reviewers: count,
|
|
303
387
|
model,
|
|
304
388
|
outputs: results.map(({ job, result }) => ({ name: job.name, lane: job.lane.id, lens: job.lane.lens, ok: result.ok, output: result.output, stderr: result.stderr, turns: result.turns })),
|