Files
张宗平 1eb98cdb3f chore: add extensions/ directory (real location for goal-driven-loop extension)
Skill lives at .agents/skills/goal-driven-loop (symlinked from .pi/skills/)
Extension lives at extensions/goal-driven-loop (symlinked from .pi/extensions/)
2026-06-10 21:04:28 +08:00

107 lines
2.5 KiB
TypeScript

/**
* UI prompts for goal-driven-loop.
*
* Wraps Pi's UI methods with timed auto-defaults to avoid
* blocking the user forever.
*/
export interface UIContext {
hasUI: boolean;
select: (
title: string,
options: string[],
opts?: { timeout?: number }
) => Promise<string | undefined>;
notify: (message: string, level?: "info" | "warn" | "error") => void;
confirm: (
title: string,
message: string,
opts?: { timeout?: number }
) => Promise<boolean>;
}
const DEFAULT_TIMEOUT_MS = 10_000;
const STAGNATION_NOTIFY_THRESHOLD = 2;
/**
* Prompt the user to resolve a stale goal state.
* Returns the choice or undefined on timeout.
* Auto-default: "continue"
*/
export async function promptStaleState(
ctx: UIContext
): Promise<"continue" | "replace" | "abandon" | undefined> {
if (!ctx.hasUI) {
return "continue";
}
const choice = await ctx.select(
"已有目标在进行中",
["继续当前目标", "替换为新目标", "放弃当前目标"],
{ timeout: DEFAULT_TIMEOUT_MS }
);
switch (choice) {
case "继续当前目标":
return "continue";
case "替换为新目标":
return "replace";
case "放弃当前目标":
return "abandon";
default:
return "continue";
}
}
/**
* Notify the user that the goal is being delegated to comet.
*/
export function notifyCoarseGrainedDelegation(
ctx: UIContext,
goal: string
): void {
if (!ctx.hasUI) return;
ctx.notify(
`目标粒度较大,已委托给 comet 全流程处理:${goal}`,
"info"
);
}
/**
* Notify the user about mid-loop granularity upgrade.
*/
export function notifyMidLoopUpgrade(ctx: UIContext, goal: string): void {
if (!ctx.hasUI) return;
ctx.notify(
`执行中发现目标比预期复杂,已升级到 comet:${goal}`,
"info"
);
}
/**
* Notify the user about stagnation that auto-recovery couldn't fix.
* Called after STAGNATION_NOTIFY_THRESHOLD auto-recovery attempts.
*/
export function notifyStagnation(
ctx: UIContext,
goal: string,
passedCount: number,
totalCount: number,
iteration: number
): void {
if (!ctx.hasUI) return;
ctx.notify(
`目标停滞:${goal} (${passedCount}/${totalCount} 通过, 第 ${iteration} 轮)。自动策略调整未奏效,请人工介入。`,
"warn"
);
}
/**
* Check if we should notify the user about stagnation yet.
*/
export function shouldNotifyStagnation(
consecutiveStagnations: number
): boolean {
return consecutiveStagnations > STAGNATION_NOTIFY_THRESHOLD;
}