import type { ActiveRun, GateResult, KdQuestion } from "./types.ts"; export type KdActionIntent = | "answer-question" | "revise-fact" | "stop-or-reset" | "advance-phase" | "verify-result" | "read-context" | "write-code" | "ask-question" | "phase-input"; export interface KdActionRoute { intent: KdActionIntent; allowed: boolean; reason: string; requiredAction: string; } export function routeHarnessAction(input: { run: ActiveRun; gate: GateResult; openQuestion?: KdQuestion; userText: string; }): KdActionRoute { const text = input.userText.trim(); const openQuestion = input.openQuestion; const inferred = inferIntent(text); if (openQuestion) { if (inferred === "stop-or-reset") { return { intent: inferred, allowed: true, reason: `当前存在 ${openQuestion.id},但用户输入表达停止、废弃或重开。`, requiredAction: "先确认是否废弃当前断点;若继续当前 run,必须先回答 open question。", }; } if (inferred === "revise-fact") { return { intent: inferred, allowed: true, reason: `当前存在 ${openQuestion.id},用户输入可能是事实更正。`, requiredAction: "使用 kd_answer_user action=revise 记录更正事实和原因,或要求用户明确更正对象。", }; } if (looksLikeQuestionAnswer(text)) { return { intent: "answer-question", allowed: true, reason: `当前唯一允许的正常动作是回答 ${openQuestion.id}。`, requiredAction: `调用 kd_answer_user action=answer id=${openQuestion.id} answer=<本轮输入>。`, }; } return { intent: inferred, allowed: false, reason: `当前存在 open blocking question ${openQuestion.id},本轮输入不能解释为 ${inferred}。`, requiredAction: `先判断本轮输入是否回答 ${openQuestion.id};不是答案时要求用户回答、修订事实或明确停止当前 run。`, }; } if (!input.gate.passed && inferred === "write-code") { return { intent: inferred, allowed: false, reason: `当前门禁阻塞:${input.gate.reason ?? "未知阻塞"}。`, requiredAction: input.gate.reason ? `只处理门禁阻塞:${input.gate.reason}` : "刷新门禁并处理唯一阻塞项。", }; } if (input.run.phase !== "execute" && inferred === "write-code") { return { intent: inferred, allowed: false, reason: `当前阶段是 ${input.run.phase},不能写代码。`, requiredAction: "完成当前阶段和门禁后推进到 execute;事实不足时使用 kd_ask_user 登记当前事实组问题。", }; } return { intent: inferred, allowed: true, reason: "本轮输入可按当前 control frame 焦点处理。", requiredAction: defaultActionForIntent(inferred), }; } function inferIntent(text: string): KdActionIntent { if (!text) return "phase-input"; if (/^(停止|暂停|废弃|取消|重开|重新开始|stop|cancel|reset)\b/i.test(text)) return "stop-or-reset"; if (/修订|更正|改成|不是.*而是|应为|应该是|revise/i.test(text)) return "revise-fact"; if (isPlainAdvanceRequest(text)) return "advance-phase"; if (/^(推进|下一阶段|继续推进|advance)\b|^\/kd\s+(next|phase|advance)\b/i.test(text)) return "advance-phase"; if (/验证|测试|构建|release:check|npm test|npm run|exit\s*code|kd_verify_result|verify/i.test(text)) return "verify-result"; if (/读取|查看|搜索|查找|定位|打开|read|grep|find|rg|get-content|ls\b|dir\b/i.test(text)) return "read-context"; if (/写代码|编码|实现|修改|修复|新增|生成|提交代码|write|edit|patch|implement|fix/i.test(text)) return "write-code"; if (/[??]$|请问|确认.*什么|是什么/.test(text)) return "ask-question"; return "phase-input"; } export function isPlainAdvanceRequest(text: string): boolean { const normalized = text .trim() .replace(/[。.!!]+$/g, "") .replace(/\s+/g, " ") .toLowerCase(); return [ "继续", "继续推进", "继续下一步", "下一步", "下一阶段", "下个阶段", "推进", "开始下一步", "advance", "continue", "next", ].includes(normalized); } function looksLikeQuestionAnswer(text: string): boolean { if (!text || text.startsWith("/")) return false; if (text.length > 300) return false; if (/[??]/.test(text)) return false; if (/^(先|暂停|等等|不用|不要|继续|开始|实现|修改|修复|提交|发布|push)\b/i.test(text)) return false; if (/(然后|同时|顺便|接着|写代码|编码|提交|发布|push)/i.test(text)) return false; return true; } function defaultActionForIntent(intent: KdActionIntent): string { if (intent === "answer-question") return "记录 open question 答案。"; if (intent === "revise-fact") return "记录结构化事实修订。"; if (intent === "advance-phase") return "刷新门禁,通过后推进阶段。"; if (intent === "verify-result") return "运行或记录真实验证命令和 evidence。"; if (intent === "read-context") return "读取当前项目真实文件并记录结论。"; if (intent === "write-code") return "仅在 execute 阶段写 PLAN.md 批准文件。"; if (intent === "ask-question") return "使用 kd_ask_user 登记当前事实组问题。"; if (intent === "stop-or-reset") return "确认停止、废弃或重开当前 run 的后果。"; return "按当前阶段协议处理输入。"; }