/** * LoopReducer — LoopKernel 的纯状态转换函数。 * * 每次循环迭代发出一个 LoopEvent;reducer 产生下一个不可变 LoopState。 * 这种设计支持: * - 确定性重放:相同的事件序列总是产生相同的状态。 * - 时间旅行调试:可以从任意事件序列重建状态。 * - 审计追踪:事件序列就是完整的循环执行日志。 * * 纯函数约束:reducer 不得执行副作用(无 I/O、无输入状态变异)。 * * @see loop-kernel.ts — 驱动事件通过此 reducer 的引擎 * @see ../kernel/events.ts — LoopEventType 定义 */ import type { GateDecision } from "../gates/findings.ts"; import type { KdPhase } from "../types.ts"; import type { ActionPlan } from "./action-plan.ts"; // ── 循环状态 ────────────────────────────────────────────────────────────── /** 循环运行时状态。 */ export type LoopStatus = "running" | "completed" | "blocked" | "stuck" | "budget_exhausted"; // ── 循环观察 ────────────────────────────────────────────────────────────── /** * 动作执行后的单次观察结果。 * * 观察是"Observe"步骤的输出:工具返回了什么、是否成功、产生了哪些证据。 */ export interface LoopObservation { /** 产生此观察的动作 ID。 */ actionPlanId: string; /** 调用的工具名称。 */ toolName: string; /** 工具执行是否成功。 */ success: boolean; /** 输出摘要。 */ summary: string; /** 原始输出文本(为状态存储做截断)。 */ output: string; /** 此观察产生的证据文件路径。 */ evidencePaths: string[]; /** 执行期间发现的源码引用。 */ sourceRefs: string[]; /** ISO 时间戳。 */ timestamp: string; } // ── 循环状态 ────────────────────────────────────────────────────────────── /** * 循环在某一时点的完整、不可变状态。 * * LoopState 仅由 LoopReducer 产生。 * 任何组件不得直接变异它(immutable 约束)。 */ export interface LoopState { /** 当前迭代计数(0 起始,在每个 Reason 步骤前递增)。 */ turn: number; /** 已执行的所有动作。 */ actions: ActionPlan[]; /** 动作执行后捕获的所有观察。 */ observations: LoopObservation[]; /** 遇到的所有门禁决策。 */ gateDecisions: GateDecision[]; /** 循环期间收集的证据路径。 */ evidencePaths: string[]; /** 当前 harness 阶段。 */ phase: KdPhase; /** 运行时状态。 */ status: LoopStatus; /** 描述当前状态的人类可读消息。 */ message: string; /** 停止钩子原因(若由 hook 停止)。 */ stopHook?: string; /** 循环开始的 ISO 时间戳。 */ startedAt: string; /** 循环停止的 ISO 时间戳(若已停止)。 */ stoppedAt?: string; } // ── 循环事件 ────────────────────────────────────────────────────────────── /** * 驱动状态转换的判别联合事件。 * * 每种事件类型对应 reducer 中的一个 case。 * 使用判别联合(discriminated union)确保类型安全和穷举检查。 */ export type LoopEvent = | LoopStartEvent | LoopStepEvent | LoopBlockedEvent | LoopCompletedEvent | LoopStuckEvent | LoopBudgetExhaustedEvent; export interface LoopStartEvent { type: "loop.start"; payload: { phase: KdPhase; }; } export interface LoopStepEvent { type: "loop.step"; payload: { action: ActionPlan; gateDecision?: GateDecision; observation?: LoopObservation; }; } export interface LoopBlockedEvent { type: "loop.blocked"; payload: { reason: string; gateDecision?: GateDecision; action?: ActionPlan; }; } export interface LoopCompletedEvent { type: "loop.completed"; payload: { message: string; }; } export interface LoopStuckEvent { type: "loop.stuck"; payload: { reason: string; turnsAtSameState: number; }; } export interface LoopBudgetExhaustedEvent { type: "loop.budget_exhausted"; payload: { maxIterations: number; }; } // ── 初始状态工厂 ────────────────────────────────────────────────────────── /** * 创建新循环运行的初始 LoopState。 * * @param phase - 循环启动时的 harness 阶段 * @returns 初始 LoopState(turn=0, status="running") */ export function createInitialLoopState(phase: KdPhase): LoopState { return { turn: 0, actions: [], observations: [], gateDecisions: [], evidencePaths: [], phase, status: "running", message: "Loop started.", startedAt: new Date().toISOString(), }; } // ── 循环 Reducer ────────────────────────────────────────────────────────── /** * 纯函数:将单个 LoopEvent 应用到当前 LoopState。 * * Reducer 是状态转换的唯一权威。 * 它不得执行副作用——无 I/O、无输入状态变异。 * * @param state 当前循环状态(不会被变异) * @param event 要应用的事件 * @returns 反映事件后的新 LoopState */ export function loopReducer(state: LoopState, event: LoopEvent): LoopState { switch (event.type) { case "loop.start": return reduceLoopStart(state, event); case "loop.step": return reduceLoopStep(state, event); case "loop.blocked": return reduceLoopBlocked(state, event); case "loop.completed": return reduceLoopCompleted(state, event); case "loop.stuck": return reduceLoopStuck(state, event); case "loop.budget_exhausted": return reduceLoopBudgetExhausted(state, event); default: { // Exhaustiveness check — TypeScript will error if a case is missing. const _exhaustive: never = event; return state; } } } // ── 事件处理器 ──────────────────────────────────────────────────────────── /** 处理 loop.start 事件:重置阶段和状态。 */ function reduceLoopStart(state: LoopState, event: LoopStartEvent): LoopState { return { ...state, phase: event.payload.phase, status: "running", message: "Loop started.", startedAt: new Date().toISOString(), }; } /** 处理 loop.step 事件:追加动作、观察和门禁决策,递增 turn。 */ function reduceLoopStep(state: LoopState, event: LoopStepEvent): LoopState { const { action, gateDecision, observation } = event.payload; const newActions = [...state.actions, action]; const newGateDecisions = gateDecision ? [...state.gateDecisions, gateDecision] : state.gateDecisions; const newObservations = observation ? [...state.observations, observation] : state.observations; const newEvidence = observation?.evidencePaths ? [...state.evidencePaths, ...observation.evidencePaths] : state.evidencePaths; return { ...state, turn: state.turn + 1, actions: newActions, observations: newObservations, gateDecisions: newGateDecisions, evidencePaths: newEvidence, status: "running", message: `Step ${state.turn + 1}: ${action.intent}`, }; } /** 处理 loop.blocked 事件:记录阻塞原因和门禁决策。 */ function reduceLoopBlocked(state: LoopState, event: LoopBlockedEvent): LoopState { const { reason, gateDecision, action } = event.payload; const newGateDecisions = gateDecision ? [...state.gateDecisions, gateDecision] : state.gateDecisions; const newActions = action ? [...state.actions, action] : state.actions; return { ...state, actions: newActions, gateDecisions: newGateDecisions, status: "blocked", message: `Blocked: ${reason}`, stoppedAt: new Date().toISOString(), }; } /** 处理 loop.completed 事件:标记循环正常完成。 */ function reduceLoopCompleted(state: LoopState, event: LoopCompletedEvent): LoopState { return { ...state, status: "completed", message: event.payload.message, stoppedAt: new Date().toISOString(), }; } /** 处理 loop.stuck 事件:标记循环陷入 stuck 状态。 */ function reduceLoopStuck(state: LoopState, event: LoopStuckEvent): LoopState { return { ...state, status: "stuck", stopHook: "stuck", message: `Stuck after ${event.payload.turnsAtSameState} turns: ${event.payload.reason}`, stoppedAt: new Date().toISOString(), }; } /** 处理 loop.budget_exhausted 事件:标记预算耗尽。 */ function reduceLoopBudgetExhausted(state: LoopState, event: LoopBudgetExhaustedEvent): LoopState { return { ...state, status: "budget_exhausted", stopHook: "budget_exhausted", message: `Budget exhausted: reached ${event.payload.maxIterations} iterations.`, stoppedAt: new Date().toISOString(), }; } // ── 确定性重放 ──────────────────────────────────────────────────────────── /** * 重放事件序列以重建最终 LoopState。 * * 确定性:相同的事件序列总是产生相同的状态。 * 用途:调试、审计、追踪重建。 * * @param events 有序的循环事件序列 * @param phase 循环初始阶段(默认 "execute") * @returns 所有事件应用后的最终 LoopState */ export function replayLoopEvents( events: ReadonlyArray, phase: KdPhase = "execute", ): LoopState { let state = createInitialLoopState(phase); for (const event of events) { state = loopReducer(state, event); } return state; }