mirror of
https://github.com/wassname/pi-plan.git
synced 2026-09-26 14:10:23 +08:00
Discard plan-review intent after interrupted or superseding input
This commit is contained in:
+4
-4
@@ -620,14 +620,14 @@ export default function mainSupervisor(pi: ExtensionAPI) {
|
||||
if (state.child && state.parent) pi.appendEntry(RUN, { plan: state.plan, parent: state.parent, session: identity(ctx) });
|
||||
if (clearCheckIn) { clearTimeout(clearCheckIn.deadline); clearCheckIn.deadline = undefined; }
|
||||
});
|
||||
let requestedPlanReview: { generation: number; digest: string } | undefined;
|
||||
let requestedPlanReview: { generation: number; digest: string; signal?: AbortSignal } | undefined;
|
||||
pi.on("agent_settled", async (_e, ctx) => {
|
||||
agentRunActive = false;
|
||||
clearCheckIn?.startDeadline?.();
|
||||
reconcileReports(ctx);
|
||||
remindReports(ctx);
|
||||
const requested = requestedPlanReview; requestedPlanReview = undefined;
|
||||
if (!requested || requested.generation !== generation || state.child || state.mode !== "planning" || !ctx.hasUI) return;
|
||||
if (!requested || requested.signal?.aborted || requested.generation !== generation || state.child || state.mode !== "planning" || !ctx.hasUI) return;
|
||||
const snapshot = readPlan();
|
||||
if (snapshot.text === undefined || digest(snapshot.text) !== requested.digest) return;
|
||||
await ready(ctx, true);
|
||||
@@ -667,7 +667,7 @@ export default function mainSupervisor(pi: ExtensionAPI) {
|
||||
return { systemPrompt: `${event.systemPrompt}\n\n${role}${pending.length ? `\n${pendingReportReviews(pending.map(reportLabel))}` : ""}`, ...(message ? { message } : {}) };
|
||||
});
|
||||
pi.on("input", (event, ctx) => {
|
||||
if (event.source !== "extension") { pauseCheckIn = false; return; }
|
||||
if (event.source !== "extension") { requestedPlanReview = undefined; pauseCheckIn = false; return; }
|
||||
const wake = /^\[Scheduled task ([a-zA-Z0-9_-]+) fired\]\nName: ([^\n]+)\nAction: prompt\n/.exec(event.text);
|
||||
if (!wake || wake[2] !== `goals-${ctx.sessionManager.getSessionId()}` || !ownedCheckInIds(ctx).has(wake[1])) return;
|
||||
const snapshot = readPlan();
|
||||
@@ -709,7 +709,7 @@ export default function mainSupervisor(pi: ExtensionAPI) {
|
||||
parameters: Type.Object({}),
|
||||
async execute(_id, _params, signal, _update, ctx) {
|
||||
if (signal?.aborted || state.child || state.mode !== "planning" || !ctx.hasUI) return result(planReviewResult.unavailable);
|
||||
requestedPlanReview = { generation, digest: digest(planText()) };
|
||||
requestedPlanReview = { generation, digest: digest(planText()), signal };
|
||||
return result(planReviewResult.queued);
|
||||
},
|
||||
});
|
||||
|
||||
+1
-1
@@ -141,7 +141,7 @@ export const planDocument = (objective: string) => `# ${objective.split("\n")[0]
|
||||
export const requestPlanReviewDescription = "Intentionally present the settled goal draft for human acceptance through the existing Ready/Discuss/Edit/Cancel menu. Planning parent only. Not for provisional drafts, redrafting, interview or Log updates; resolve consequential open questions first unless the user explicitly requests a shortcut. Only human Ready authorizes execution.";
|
||||
export const planReviewResult = {
|
||||
unavailable: "Plan review requires a planning parent and an interactive UI. No execution authorized.",
|
||||
queued: "Intentional review queued for the end of this turn. Finish without repeating the full plan; the interface will present it. No execution authorized before human Ready. Further edits, a failed/aborted turn, Discuss/Edit/Cancel or a mode change cancel this request; request again only when settled.",
|
||||
queued: "Intentional review queued for the end of this turn. Finish without repeating the full plan; the interface will present it. No execution authorized before human Ready. Further edits, new user input, a failed/aborted turn, Discuss/Edit/Cancel or a mode change cancel this request; request again only when settled.",
|
||||
};
|
||||
export const discuss = "Type your changes in chat; the draft stays open.";
|
||||
|
||||
|
||||
@@ -337,6 +337,13 @@ it("keeps provisional drafts and interview updates separate from intentional acc
|
||||
f.hooks.get("agent_end")({ messages: [{ role: "assistant", content: [], stopReason }] }, f.ctx);
|
||||
await f.hooks.get("agent_settled")({}, f.ctx);
|
||||
}
|
||||
const interrupted = new AbortController();
|
||||
await f.tools.get("RequestPlanReview").execute("interrupted", {}, interrupted.signal, undefined, f.ctx);
|
||||
interrupted.abort(); // can arrive after the tool returned, without a new assistant error
|
||||
await f.hooks.get("agent_settled")({}, f.ctx);
|
||||
await f.tools.get("RequestPlanReview").execute("superseded", {}, undefined, undefined, f.ctx);
|
||||
f.hooks.get("input")({ source: "interactive", text: "Wait, another question" }, f.ctx);
|
||||
await f.hooks.get("agent_settled")({}, f.ctx);
|
||||
await f.tools.get("RequestPlanReview").execute("discussed", {}, undefined, undefined, f.ctx);
|
||||
await f.command("discuss");
|
||||
await f.hooks.get("agent_settled")({}, f.ctx);
|
||||
|
||||
Reference in New Issue
Block a user