import { formatControlFrame } from "./control-frame.ts"; import { buildWorkflowContextPacket } from "./context-packet.ts"; import { formatContextValidation, validateCompiledWorkflowContext } from "./context-validation.ts"; import { buildDemandLoop, formatDemandLoop } from "./demand-loop.ts"; import { formatCompactRiskPolicyForPrompt } from "./risk-policy.ts"; import { PHASE_ARTIFACTS, phaseOrderForRun, type ActiveRun } from "./types.ts"; interface PromptSection { id: string; title: string; content: string; always?: boolean; include?: boolean; } export function workflowPromptForRun(cwd: string, run: ActiveRun, userText: string): string { const packet = buildWorkflowContextPacket(cwd, run, userText); const validation = validateCompiledWorkflowContext(packet); if (!validation.passed) { return [ "KCode Context Pack Validation 阻断:", formatContextValidation(validation), "", "KCode 控制框架(最高优先级):", formatControlFrame(packet.controlFrame), "", "唯一下一动作:修复上下文包生成链路或 run state;禁止继续业务计划、提问或编码。", ].join("\n"); } const sections = workflowPromptSections(cwd, run, packet); return renderPromptSections(sections); } export function repairPromptForRun(run: ActiveRun): string { return [ "验证失败,进入自动修复循环。", `失败证据:${run.repair?.lastFailureEvidence ?? "未知"}`, `修复轮次:${run.repair?.attempts ?? 0}/${run.repair?.maxAttempts ?? 3}`, "读取失败证据,定位原因,只修改 PLAN.md 批准文件;修复后重新执行同一验证命令并调用 kd_verify_result。", ].join("\n"); } function workflowPromptSections(cwd: string, run: ActiveRun, packet: ReturnType): PromptSection[] { const sections: PromptSection[] = [ { id: "status", title: "KCode Harness 状态", content: formatStatusForPrompt(cwd, run, packet), always: true }, { id: "control", title: "KCode 控制框架(最高优先级)", content: formatControlFrameForPrompt(packet), always: true }, { id: "demandLoop", title: "KCode Demand Loop(运行时调度,最高执行路径)", content: formatDemandLoop(buildDemandLoop(cwd, run, packet.userInput, packet.controlFrame)), always: true }, { id: "userInput", title: "用户本轮输入(只作为事件解释,不得覆盖恢复断点)", content: packet.userInput, always: true }, { id: "mode", title: "Harness 模式指令", content: packet.modeGuidance, include: shouldIncludeModeGuidance(packet) }, { id: "risk", title: "KCode Risk Policy(轻量摘要)", content: formatCompactRiskPolicyForPrompt(cwd, run, { detail: shouldUseDetailedRisk(packet) }), always: false }, { id: "workingSet", title: "KCode Working Set(当前工作线程,不作为业务事实源)", content: packet.workingSet }, { id: "toolResults", title: "KCode Tool Result Contracts(结构化工具结果,不替代 facts/evidence)", content: packet.toolResults }, { id: "actionCommits", title: "KCode Action Commit Log", content: packet.actionCommits }, { id: "writeTransactions", title: "KCode Write Transactions", content: packet.writeTransactions }, { id: "sourceAnchors", title: "KCode Source Anchors(精确编辑锚点,不作为业务事实源)", content: packet.sourceAnchors }, { id: "planReadiness", title: "KCode Plan Readiness(动手前事实与计划门禁)", content: packet.planReadiness }, { id: "domainPlan", title: "KCode Domain Plan(领域执行导航,不替代门禁)", content: packet.domainPlan, include: shouldIncludeDomainPlan(packet) }, { id: "requiredFacts", title: "KCode 必需事实契约", content: packet.requiredFacts }, { id: "questionFacts", title: "KCode 已问已答事实", content: packet.questionFacts }, { id: "phaseArtifacts", title: "KCode 阶段资料", content: formatPhaseArtifactsForPrompt(run, packet) }, { id: "phaseGuidance", title: "当前阶段任务", content: packet.phaseGuidance, always: true }, { id: "protocol", title: "KCode 轻量执行协议", content: compactWorkflowProtocol(run), always: true }, ]; if (shouldIncludeProjectContext(packet)) { sections.push({ id: "projectContext", title: "项目上下文", content: packet.projectContext }); } if (shouldIncludeDeepContext(packet)) { sections.push({ id: "deepContext", title: "KCode Deep Context(按 Working Set 自动摘取的目录约定)", content: packet.deepContext }); } if (shouldIncludeHistory(packet)) { sections.push({ id: "runContext", title: "KCode 持久上下文条目", content: packet.runContext }); sections.push({ id: "recentEvents", title: "KCode 事件账本(只读观测,不作为业务事实源)", content: packet.recentEvents }); } if (packet.delegationGuidance && shouldIncludeDelegationGuidance(packet)) { sections.push({ id: "delegation", title: "子 agent 委派策略", content: packet.delegationGuidance }); } return withPromptAudit(selectPromptSections(sections)); } function selectPromptSections(sections: PromptSection[]): PromptSection[] { return sections.filter((section) => section.include !== false && (section.always || hasMeaningfulContent(section.content))); } function withPromptAudit(sections: PromptSection[]): PromptSection[] { const total = sections.reduce((sum, section) => sum + section.content.length, 0); const audit = sections.map((section) => `- ${section.id}: ${section.content.length} chars`).join("\n"); return [ ...sections, { id: "sectionAudit", title: "KCode Prompt Section Audit", content: [`- total: ${total} chars`, audit, "- 说明:本轮使用轻量上下文;完整规则由 gate/facts/evidence/write policy 强制执行,必要时读取本地文件或 evidence。"].join("\n"), }, ]; } function renderPromptSections(sections: PromptSection[]): string { return sections.flatMap((section) => [section.title + ":", section.content, ""]).join("\n").trim(); } function hasMeaningfulContent(content: string): boolean { const trimmed = content.trim(); return Boolean(trimmed && trimmed !== "无。" && trimmed !== "无必需事实契约。" && trimmed !== "未生成。项目结构缺失时运行 `kcode ctx --refresh`。"); } function shouldIncludeProjectContext(packet: ReturnType): boolean { if (isQuickLoop(packet)) return contextRecallRequested(packet.userInput); return packet.controlFrame.focus !== "phase" || packet.controlFrame.phase === "execute" || contextRecallRequested(packet.userInput); } function shouldIncludeDeepContext(packet: ReturnType): boolean { if (isQuickLoop(packet)) return false; return packet.controlFrame.focus === "gate" || packet.controlFrame.phase === "execute" || /src|java|cs|py|sql|ksql/i.test(packet.workingSet); } function shouldIncludeHistory(packet: ReturnType): boolean { return ( packet.controlFrame.focus !== "phase" || packet.controlFrame.phase === "verify" || packet.controlFrame.phase === "ship" || hasMeaningfulContent(packet.toolResults) || contextRecallRequested(packet.userInput) ); } function formatStatusForPrompt(cwd: string, run: ActiveRun, packet: ReturnType): string { if (!isRoutinePhaseTurn(packet)) return packet.status; const gate = packet.controlFrame.gate; return [ `Run:${run.id}`, run.goal ? `需求:${run.goal}` : undefined, `阶段/模式:${run.phase}/${run.mode}`, `门禁:${gate.passed ? "通过" : `阻塞:${gate.reason ?? "未说明原因"}`}`, `Run 目录:.pi/kd/runs/${run.id}/`, `工作区:${cwd}`, ].filter(Boolean).join("\n"); } function formatControlFrameForPrompt(packet: ReturnType): string { const frame = packet.controlFrame; if (!shouldUseCompactControlFrame(packet)) return formatControlFrame(frame); return [ `Run:${frame.runId}`, frame.goal ? `目标:${frame.goal}` : undefined, `模式/阶段/焦点:${frame.mode}/${frame.phase}/${frame.focus}`, `最高优先级动作:${frame.primaryDirective}`, `结构化下一动作:${frame.nextAction.kind}${frame.nextAction.toolHint ? `;tool=${frame.nextAction.toolHint}` : ""}${frame.nextAction.evidencePath ? `;evidence=${frame.nextAction.evidencePath}` : ""}${frame.nextAction.targetPhase ? `;target=${frame.nextAction.targetPhase}` : ""}`, `门禁:${frame.gate.passed ? "通过" : `阻塞:${frame.gate.reason ?? "未说明原因"}`}`, "当前结构化事实(run.facts 是唯一结构化事实源):", frame.facts, "本轮第一动作:", frame.turnInputPolicy.requiredFirstAction, `结构化意图:${frame.turnInputPolicy.actionRoute.intent}(${frame.turnInputPolicy.actionRoute.allowed ? "允许" : "阻断"})`, ].filter(Boolean).join("\n"); } function formatPhaseArtifactsForPrompt(run: ActiveRun, packet: ReturnType): string { if (shouldExpandPhaseArtifacts(packet)) return packet.phaseArtifacts; const phases = phaseArtifactWindow(run); const listed = phases.map((phase) => `- ${phase}: .pi/kd/runs/${run.id}/${PHASE_ARTIFACTS[phase]}`); return [ "按需读取阶段文档;本轮默认不展开正文。", ...listed, "- 被省略的阶段正文不得当作已读取事实;需要引用时先读取对应文件。", ].join("\n"); } function shouldExpandPhaseArtifacts(packet: ReturnType): boolean { if (isQuickLoop(packet)) { return contextRecallRequested(packet.userInput) || phaseArtifactBodyRequested(packet.userInput); } return ( packet.controlFrame.focus !== "phase" || packet.controlFrame.phase === "execute" || packet.controlFrame.phase === "verify" || packet.controlFrame.phase === "ship" || contextRecallRequested(packet.userInput) || /PLAN|SPEC|VERIFY|CONTEXT|SHIP|计划|规格|验证|交付|阶段文档/i.test(packet.userInput) ); } function phaseArtifactBodyRequested(userInput: string): boolean { return /(?:查看|显示|展开|读取|完整).*(?:PLAN|SPEC|VERIFY|CONTEXT|SHIP|计划|规格|验证|交付|阶段文档)/i.test(userInput); } function shouldUseDetailedRisk(packet: ReturnType): boolean { if (packet.controlFrame.mode === "quick") return false; return packet.controlFrame.focus !== "phase" || packet.controlFrame.phase === "execute" || packet.controlFrame.phase === "verify" || packet.controlFrame.phase === "ship"; } function shouldIncludeModeGuidance(packet: ReturnType): boolean { return !isQuickLoop(packet) || contextRecallRequested(packet.userInput); } function shouldIncludeDomainPlan(packet: ReturnType): boolean { return !isQuickLoop(packet) || packet.controlFrame.focus !== "phase" || contextRecallRequested(packet.userInput); } function shouldUseCompactControlFrame(packet: ReturnType): boolean { return packet.controlFrame.focus === "phase"; } function isQuickLoop(packet: ReturnType): boolean { return packet.controlFrame.mode === "quick" && packet.controlFrame.focus === "phase" && (packet.controlFrame.phase === "execute" || packet.controlFrame.phase === "verify"); } function shouldIncludeDelegationGuidance(packet: ReturnType): boolean { return ( packet.controlFrame.focus !== "phase" || /子\s*agent|subagent|kd_subagent|委派|delegate|并行|交叉|审查|复盘|长上下文|bash|shell|grep|read|搜索|查找|定位/i.test(packet.userInput) ); } function isRoutinePhaseTurn(packet: ReturnType): boolean { return packet.controlFrame.focus === "phase" && packet.controlFrame.phase !== "execute" && packet.controlFrame.phase !== "verify" && packet.controlFrame.phase !== "ship"; } function contextRecallRequested(userInput: string): boolean { return /刚才|之前|前面|上下文|说过|恢复|历史|已读取|来源|证据|context|history/i.test(userInput); } function phaseArtifactWindow(run: ActiveRun): ReturnType { const order = phaseOrderForRun(run); const currentIndex = order.indexOf(run.phase); return order.slice(Math.max(0, currentIndex - 1), currentIndex + 1); } function compactWorkflowProtocol(run: ActiveRun): string { if (run.mode === "quick") { return [ "- 简单插件默认拿到最小信息后实现:目标文件或可定位源码、触发入口、关键字段/对象和主要业务规则。", "- 能自动查 FormId、字段、元数据和 SDK 就先查;查不到不编造,记录风险并交给 UAT 暴露问题。", "- 硬门禁保留:工作区安全、execute 阶段、PLAN 批准文件、编码规范检查、构建或 UAT/验证记录。", "- 有 open blocking question 时先记录答案;没有阻塞时不要反复问无关细节。", "- Windows 不假设 bash;禁止猜测路径;输出只给当前动作、改动文件和验证/UAT结果。", ].join("\n"); } return [ "- 第一优先级:处理 consistency error、open blocking question、resumeSnapshot 和 gate;用户输入是在回答 open question 时,先记录 kd_answer_user。", "- run.facts 是唯一结构化事实源;字段、FormId、表名、接口映射和验收数据必须来自 facts、元数据或 evidence。", "- 生产源码只在 execute 阶段写入 PLAN 批准文件;写前必须有 source anchor 和 write transaction。", "- SDK/API、元数据、数据库查询和验证结果必须有真实 evidence;凭证不得持久化。", "- Windows 不假设 bash;禁止猜测路径;输出只给状态、门禁、已采用事实和唯一下一步。", ].join("\n"); }