Separate provisional planning from intentional acceptance

This commit is contained in:
wassname2
2026-09-21 11:46:24 +08:00
parent 54268feb26
commit adc50dc322
6 changed files with 70 additions and 79 deletions
+19 -12
View File
@@ -38,12 +38,14 @@ import {
planDocument,
planning,
planningSeed,
planReviewResult,
planUnavailable,
readyApproved,
removeGoalSchedule,
reportGoalEventDescription,
reportReviewContent,
reportReviewDescription,
requestPlanReviewDescription,
resumeNotice,
scheduleCheckIn,
schedulerMessages,
@@ -372,6 +374,7 @@ export default function mainSupervisor(pi: ExtensionAPI) {
}
const stamp = generation;
if (menu || edit) {
if (menu) pi.sendMessage({ customType: "goal-plan-proposal", content: text, display: true }, { triggerTurn: false });
const choice = edit ? "Edit" : await ctx.ui.select(`Review ${state.plan}`, ["Ready", "Discuss", "Edit", "Cancel"]);
if (stamp !== generation || digest(planText()) !== digest(text)) { ctx.ui.notify("Plan changed during review. Review it again.", "warning"); return; }
if (choice === "Discuss") { ctx.ui.notify(discuss, "info"); return; }
@@ -610,28 +613,22 @@ export default function mainSupervisor(pi: ExtensionAPI) {
const text = last?.role === "assistant" ? last.errorMessage || last.content.filter(part => part.type === "text").map(part => part.text).join("\n") || last.stopReason : nativeMessages.noAssistant;
reportStop(text, last?.role === "assistant" && last.stopReason === "aborted" ? "aborted" : last?.role === "assistant" && (last.stopReason === "error" || last.errorMessage) ? "blocker" : "unclassified", true, true);
finalReviewTurnDigest = undefined; refresh(ctx); if (!planWatcher && state.mode === "supervising") watchPlan(ctx); });
let proposedDraft = "";
let proposing = false;
pi.on("agent_start", (_event, ctx) => {
agentRunActive = true;
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;
pi.on("agent_settled", async (_e, ctx) => {
agentRunActive = false;
clearCheckIn?.startDeadline?.();
reconcileReports(ctx);
remindReports(ctx);
if (state.child || state.mode !== "planning" || !ctx.hasUI || proposing) return;
const text = planText();
const version = `${state.plan}:${digest(text)}`;
if (!goals(text).length || version === proposedDraft) return;
proposedDraft = version;
proposing = true;
try {
pi.sendMessage({ customType: "goal-plan-proposal", content: text, display: true }, { triggerTurn: false });
await ready(ctx, true);
} finally { proposing = false; }
const requested = requestedPlanReview; requestedPlanReview = undefined;
if (!requested || 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);
});
// No context hook. Historical message arrays, native checkpoints and model selection are untouched.
pi.on("before_agent_start", (event, ctx) => {
@@ -705,6 +702,16 @@ export default function mainSupervisor(pi: ExtensionAPI) {
if (nativeWorkerControl && (state.child || state.mode === "solo")) return { block: true, reason: goalToolBlocked(state.child ? "worker" : state.mode) };
});
pi.registerTool({
name: "RequestPlanReview", label: "Present settled goal plan", description: requestPlanReviewDescription,
parameters: Type.Object({}),
async execute(_id, _params, _signal, _update, ctx) {
if (state.child || state.mode !== "planning" || !ctx.hasUI) return result(planReviewResult.unavailable);
requestedPlanReview = { generation, digest: digest(planText()) };
return result(planReviewResult.queued);
},
});
pi.registerCommand("goals", {
description: "Goal plan actions: new, edit, discuss, review, ready, status, stop, resume, solo, attach, model, quit (exit/clear)",
getArgumentCompletions: (prefix) => ["new", "attach", "edit", "discuss", "review", "ready", "status", "stop", "resume", "solo", "model", "help", "exit", "clear", "quit"].filter((verb) => verb.startsWith(prefix)).map((verb) => ({ value: verb, label: verb })),
+16 -10
View File
@@ -19,17 +19,18 @@ export const workerViewText = {
export const workerViewUnavailable = (reason: string) => `Worker view unavailable: ${reason}. Current activity unknown; use the owned saved session and native controls.`;
export const planDrafting = `\
You are in plan mode. Help the user express what they want this project to achieve in a short judgeable plan. Seek to understand their underlying goals, infer ordinary details, and use their applicable AGENTS.md instructions, relevant skills, and project context to interpret the request correctly. Do not silently substitute your own goals or expand the agreed scope. Unless the user explicitly asks for speed or no questions, follow this order and do not draft early: explore, identify protected decisions, grill, then write.
You are in plan mode. Help the user express what they want this project to achieve in a short judgeable plan. Seek to understand their underlying goals, infer ordinary details, and use their applicable AGENTS.md instructions, relevant skills, and project context to interpret the request correctly. Do not silently substitute your own goals or expand the agreed scope. Default to an explicit first provisional draft, then explore, grill consequential gaps, and intentionally present the settled draft for acceptance. Respect the user's requested order and shortcuts such as "skip questions" or "just propose a plan". Redraft at any time; do not force ritual questions when none matter.
1. Explore first. Read every user-supplied link and resource that available tools can access, the existing plan, applicable instructions and relevant project files. Grep or search to resolve facts and reduce uncertainty before asking the user. Report the exact access failure for an unavailable resource; do not ask the user for facts you can find. Only edit the plan in this phase; do not implement or mutate project state via bash. This is an instruction, not a filesystem restriction.
2. Infer which decisions the human reserves in this particular project. Use their request, User voice, AGENTS.md and prior choices. These can include publication approval or editorial voice in one project, the core experiment in another, or the principles behind an evaluation. Put any proposed change to a protected decision before implementation details, explain its effect and get explicit approval. Do not turn routine reversible implementation choices into approval requests.
3. Then use the grilling skill. Map consequential choices as a design tree and ask the current frontier one short round at a time. Questions should expose differences that would otherwise stay hidden, probe assumptions and challenge inconsistencies. Number each question and recommend an answer with its basis: cite a file or quote, or label it a guess. Recompute the frontier after each answer. Facts are your job; consequential decisions are the user's. Stop when remaining choices would not change the goal, correctness, cost or external effects. Do not use an arbitrary question quota or ask for redundant confirmation once shared understanding is clear. Record each answer, or an unanswered unknown, in ## Interview. Do not silently replace an unknown with an inference. Only withhold Ready for an unanswered protected choice that changes scope, spending or the user-visible result.
4. State the user-visible result before the goals: one concrete sentence naming what the human will inspect when this plan is done. Take it from the original request, not from your implementation plan. Every requested artifact and action must survive into this sentence. An agent-inferred constraint may not replace, defer or contradict it; ask the human if an inference would change the result. Do not present the review menu with a placeholder goal such as "work out the thing", "improve it" or "investigate".
5. When every goal has an object, observable result, settled scope and required approval, draft the plan file and present it. It should be safe to work overnight and present the requested outcome.
1. Capture and briefly present a first draft, visibly labelled provisional. TODOs and candid uncertainty (likely, tentative, not checked, depends on a decision) are welcome; do not invent numerical precision. This is a working proposal, not a claim of readiness.
2. Explore. Read every user-supplied link and resource that available tools can access, the existing plan, applicable instructions and relevant project files. Grep or search to resolve facts and reduce uncertainty before asking the user. Report the exact access failure for an unavailable resource; do not ask the user for facts you can find. Only edit the plan in this phase; do not implement or mutate project state via bash. This is an instruction, not a filesystem restriction.
3. Infer which decisions the human reserves in this particular project. Use their request, User voice, AGENTS.md and prior choices. These can include publication approval or editorial voice in one project, the core experiment in another, or the principles behind an evaluation. Put any proposed change to a protected decision before implementation details, explain its effect and get explicit approval. Do not turn routine reversible implementation choices into approval requests.
4. Use the grilling skill for consequential unresolved choices. Map consequential choices as a design tree and ask the current frontier one short round at a time. Questions should expose differences that would otherwise stay hidden, probe assumptions and challenge inconsistencies. Number each question and recommend an answer with its basis: cite a file or quote, or label it a guess. Recompute the frontier after each answer. Facts are your job; consequential decisions are the user's. Stop when remaining choices would not change the goal, correctness, cost or external effects. Do not use an arbitrary question quota or ask for redundant confirmation once shared understanding is clear. Record each answer, or an unanswered unknown, in ## Interview. Do not silently replace an unknown with an inference. Only withhold Ready for an unanswered protected choice that changes scope, spending or the user-visible result.
5. State the user-visible result before the goals: one concrete sentence naming what the human will inspect when this plan is done. Take it from the original request, not from your implementation plan. Every requested artifact and action must survive into this sentence. An agent-inferred constraint may not replace, defer or contradict it; ask the human if an inference would change the result. Do not present the review menu with a placeholder goal such as "work out the thing", "improve it" or "investigate".
6. When every goal has an object, observable result, settled scope and required approval, save the settled draft and call RequestPlanReview to present it for acceptance. It should be safe to work overnight and present the requested outcome.
How this mode ends: after each changed settled draft the human gets a menu (Ready / Discuss / Edit / Cancel).
Saving, redrafting, interview answers and Log updates never request acceptance. Do not print the entire plan again or call RequestPlanReview while questions remain open. Only an intentional RequestPlanReview call (or the human's /goals review) displays the full draft and menu (Ready / Discuss / Edit / Cancel).
Plan mode ends only when they pick Ready. Discuss continues ordinary chat. Edit opens the full
plan. When a new requirement arrives, fold it in, say what changed, and present the plan again.
plan. When a new requirement arrives, fold it in and briefly say what changed; continue discussion before intentionally requesting acceptance again.
Detail that doesn't change a goal or a discriminator belongs in the appendix, not in the goals.
Right-size it:
@@ -125,16 +126,21 @@ Conventions:
- Appendix: unlimited and unverified. Alternatives, links, dead ends, and the settled detail that
is not part of the approved goals. Nothing here is approved and nothing here is checked.
When the goals are drafted, present them and say the plan is final. Do not begin execution.`;
A first draft is provisional, not final. Keep exploring and redrafting until ready for intentional acceptance. Do not begin execution before the human picks Ready.`;
// Planning and interview. Keep the full drafting guide one-shot rather than repeating it each turn.
export function planning(planPath: string): string {
return `Plan only in ${planPath}; do not implement or launch workers before Ready. Unless the user explicitly asks for speed or no questions: first read their supplied links/resources and inspect or search the project, then infer project-specific decisions that require their approval, then use the grilling skill for consequential unresolved choices before drafting. Facts are your job; do not ask for information tools can find. Put proposed changes to protected intent, editorial/publication authority, core research design or evaluation principles first and get explicit approval. Record unanswered unknowns and present Ready when the outcome, scope and spending are settled. Preserve the user's exact deliverable, preferences and voice. Preserve concrete technical deliverable nouns and verbs in visible goals; do not replace them with vague benefits or readiness. Use "I know it when I see it" to judge actual results in hindsight, not to rename the requested work. Put observable examples, constraints, failure modes, discriminators and evidence expectations beneath each goal, above ## Log; do not invent numerical gates to replace judgment. Record the requested worker model in preferences. When your drafted plan is ready for human review, finish your turn; the interface displays the draft and approval choices automatically. Do not ask the user to type a command to see the proposal. /goals review reopens it on request; /goals exit preserves the draft.`;
return `Plan only in ${planPath}; do not implement or launch workers before Ready. Default to a brief explicit first provisional draft (TODOs and candid uncertainty are welcome), then explore supplied resources/project facts, grill consequential gaps, and intentionally request acceptance. Redraft throughout. Respect requested ordering and shortcuts such as "skip questions" or "just propose a plan"; do not force questions when none matter. Facts are your job; do not ask for information tools can find. Put proposed changes to protected intent, editorial/publication authority, core research design or evaluation principles first and get explicit approval. Record unanswered unknowns; request acceptance only when the outcome, scope and spending are settled. Preserve the user's exact deliverable, preferences and voice. Preserve concrete technical deliverable nouns and verbs in visible goals; do not replace them with vague benefits or readiness. Use "I know it when I see it" to judge actual results in hindsight, not to rename the requested work. Put observable examples, constraints, failure modes, discriminators and evidence expectations beneath each goal, above ## Log; do not invent numerical gates to replace judgment. Record the requested worker model in preferences. Saving/redrafting/interview/Log updates do not request acceptance: do not repeat the entire plan or bury unanswered questions under Ready. When intentionally presenting the settled draft for human acceptance, call RequestPlanReview; it displays the plan and existing Ready/Discuss/Edit/Cancel menu. Only human Ready authorizes execution. /goals review also opens it on explicit request; /goals exit preserves the draft.`;
}
export function planningSeed(objective: string, planPath: string): string {
return `Enter a planning conversation focused on the user's goals. ${objective ? `Initial idea: ${objective}.` : "Use the existing conversation; ask what the user wants to achieve if it is unclear."} Read any existing plan at ${planPath} first, then discuss and draft it with the user. Do not infer approval to implement from starting this conversation. ${planning(planPath)}\n\n${planDrafting}`;
}
export const planDocument = (objective: string) => `# ${objective.split("\n")[0] || "Goal plan"}\n\n## Objective\n${objective}\n\n## Goals\n\n## Log\n`;
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 or a mode change cancel this request; request again only when settled.",
};
export const discuss = "Type your changes in chat; the draft stays open.";
// Ready and explicit native peer attachment. No worker environment or agent-file contract.