/** * DelegationPlan — 结构化委派计划。 * * 每次子 agent 委派必须产生一个 DelegationPlan, * 经过 GateRunner 的 pre-delegation 检查点后才能执行。 * * @see docs/AGENT_GATE_LOOP_REFACTOR_PLAN.md — "DelegationPlan" * @see src/harness/delegation.ts — DelegationRole 定义 */ import type { GateCheckpoint, GateSeverity } from "./action-plan.ts"; import type { GateContext, GateDecision, GateFinding } from "../gates/findings.ts"; import type { GateRunner } from "../gates/gate-runner.ts"; import type { AuthorizationModel } from "../gates/authorization.ts"; import { evaluateGateWithAuth } from "../gates/authorization.ts"; import type { ActiveRun, KdPhase } from "../types.ts"; // --------------------------------------------------------------------------- // Delegation types // --------------------------------------------------------------------------- /** 委派角色——与 src/harness/delegation.ts 保持一致。 */ export type DelegationRole = "research" | "doc" | "code" | "review" | "verify"; /** 子 agent 写入权限。 */ export type DelegationAllowedWrites = | "none" | "docs-and-current-phase-artifact" | "plan-approved-files-only" | "evidence-through-kd_verify_result"; /** * DelegationPlan — 通过门禁的结构化委派计划。 * * 每个 DelegationPlan 必须: * - 绑定一个 CompletionPromise(completionPromiseId)。 * - 携带门禁追踪 ID(gateTraceId)。 * - 包含 capsule(上下文包)。 * - 声明写入权限(allowedWrites)。 */ export interface DelegationPlan { /** 唯一标识,格式:plan-{timestamp}-{random_hex_4}。 */ planId: string; /** 委派角色。 */ role: DelegationRole; /** 任务描述。 */ task: string; /** 上下文包——发送给子 agent 的压缩上下文。 */ capsule: string; /** 子 agent 写入权限。 */ allowedWrites: DelegationAllowedWrites; /** 关联的 Completion Promise ID。 */ completionPromiseId: string; /** 关联的门禁决策追踪 ID。 */ gateTraceId: string; } // --------------------------------------------------------------------------- // Role → AllowedWrites mapping // --------------------------------------------------------------------------- /** 角色到写入权限的默认映射——与 src/harness/delegation.ts 保持一致。 */ const ROLE_DEFAULT_ALLOWED_WRITES: Record = { research: "none", doc: "docs-and-current-phase-artifact", code: "plan-approved-files-only", review: "none", verify: "evidence-through-kd_verify_result", }; /** 只读角色集合。 */ const READ_ONLY_ROLES: ReadonlySet = new Set(["research", "review", "verify"]); /** * 判断角色是否为只读。 * * @param role - 委派角色。 * @returns 是否只读。 */ export function isReadOnlyDelegationRole(role: DelegationRole): boolean { return READ_ONLY_ROLES.has(role); } /** * 获取角色的默认写入权限。 * * @param role - 委派角色。 * @returns 写入权限。 */ export function getDefaultAllowedWrites(role: DelegationRole): DelegationAllowedWrites { return ROLE_DEFAULT_ALLOWED_WRITES[role]; } // --------------------------------------------------------------------------- // Validation // --------------------------------------------------------------------------- /** DelegationPlan 验证错误。 */ export interface DelegationPlanValidationError { severity: GateSeverity; checkpoint: GateCheckpoint; message: string; } /** * DelegationPlan 验证结果——包含结构化验证错误和门禁决策。 */ export interface DelegationPlanValidationResult { /** 结构化验证错误(空数组表示通过)。 */ errors: DelegationPlanValidationError[]; /** GateRunner 门禁决策(仅在提供 gateRunner 时存在)。 */ gateDecision?: GateDecision; /** 最终是否允许委派。 */ allowed: boolean; } /** * 校验 DelegationPlan 的 allowedWrites 是否与 role 一致。 * * @param plan - 待校验的 DelegationPlan。 * @returns 所有验证错误;空数组表示通过。 */ export function validateDelegationPlanBasic(plan: DelegationPlan): DelegationPlanValidationError[] { const errors: DelegationPlanValidationError[] = []; const expectedWrites = ROLE_DEFAULT_ALLOWED_WRITES[plan.role]; if (plan.allowedWrites !== expectedWrites) { errors.push({ severity: "soft-deny", checkpoint: "pre-delegation", message: `角色 "${plan.role}" 的默认写入权限为 "${expectedWrites}",收到 "${plan.allowedWrites}"`, }); } if (!plan.task.trim()) { errors.push({ severity: "hard-deny", checkpoint: "pre-delegation", message: "委派任务不能为空", }); } if (!plan.capsule.trim()) { errors.push({ severity: "hard-deny", checkpoint: "pre-delegation", message: "委派上下文包(capsule)不能为空", }); } return errors; } /** * 校验 DelegationPlan 并集成 GateRunner 门禁检查。 * * 流程: * 1. 执行基本结构校验(allowedWrites、task、capsule)。 * 2. 执行委派专属校验(code 角色仅在 execute 阶段允许)。 * 3. 若提供 gateRunner,运行 pre-delegation 门禁并应用授权模型。 * 4. 汇总所有结果,返回 DelegationPlanValidationResult。 * * @param plan - 待校验的 DelegationPlan。 * @param options - 可选的 gateRunner、authModel、run、cwd。 * @returns 验证结果。 */ export async function validateDelegationPlan( plan: DelegationPlan, options?: { gateRunner?: GateRunner; authModel?: AuthorizationModel; run?: ActiveRun; cwd?: string; }, ): Promise { // 1. Basic structural validation. const errors = validateDelegationPlanBasic(plan); // 2. Delegation-specific authorization checks. const authErrors = validateDelegationAuthorization(plan, options?.run); errors.push(...authErrors); // 3. If gateRunner provided, run pre-delegation gate. let gateDecision: GateDecision | undefined; if (options?.gateRunner) { const ctx: GateContext = { cwd: options.cwd ?? process.cwd(), checkpoint: "pre-delegation", run: options.run, delegationRole: plan.role, payload: { planId: plan.planId, task: plan.task }, }; if (options.authModel) { gateDecision = await evaluateGateWithAuth(options.gateRunner, ctx, options.authModel); } else { gateDecision = await options.gateRunner.runGate(ctx); } } // 4. Compute final allowed: structural errors + gate decision. const hasStructuralBlock = errors.some((e) => e.severity === "hard-deny"); const gateBlocked = gateDecision ? !gateDecision.allowed : false; const allowed = !hasStructuralBlock && !gateBlocked; return { errors, gateDecision, allowed }; } // --------------------------------------------------------------------------- // Authorization checks // --------------------------------------------------------------------------- /** * 委派专属授权校验。 * * 规则: * - code 角色仅在 execute 阶段允许。 * - 无活跃 run 时拒绝 code 委派。 */ function validateDelegationAuthorization( plan: DelegationPlan, run?: ActiveRun, ): DelegationPlanValidationError[] { const errors: DelegationPlanValidationError[] = []; // Code delegation requires active run in execute phase. if (plan.role === "code") { if (!run) { errors.push({ severity: "hard-deny", checkpoint: "pre-delegation", message: "code 委派需要活跃的需求运行(ActiveRun)。", }); } else if (run.phase !== "execute") { errors.push({ severity: "hard-deny", checkpoint: "pre-delegation", message: `code 委派仅在 execute 阶段允许,当前阶段为 "${run.phase}"。`, }); } } return errors; } // --------------------------------------------------------------------------- // Plan ID generation (shared format) // --------------------------------------------------------------------------- function generatePlanId(): string { const timestamp = Date.now(); const randomHex = Math.floor(Math.random() * 0x10000) .toString(16) .padStart(4, "0"); return `plan-${timestamp}-${randomHex}`; } // --------------------------------------------------------------------------- // Factory // --------------------------------------------------------------------------- /** * 创建 DelegationPlan 实例。 * * 自动根据 role 设置默认 allowedWrites,除非显式指定。 * * @param role - 委派角色。 * @param task - 任务描述。 * @param capsule - 上下文包。 * @param completionPromiseId - 关联的 Completion Promise ID。 * @param gateTraceId - 关联的门禁追踪 ID。 * @param options - 可选项。 * @param options.allowedWrites - 显式写入权限(默认按角色自动设置)。 * @returns 新建的 DelegationPlan。 */ export function createDelegationPlan( role: DelegationRole, task: string, capsule: string, completionPromiseId: string, gateTraceId: string, options?: { allowedWrites?: DelegationAllowedWrites }, ): DelegationPlan { return { planId: generatePlanId(), role, task, capsule, allowedWrites: options?.allowedWrites ?? ROLE_DEFAULT_ALLOWED_WRITES[role], completionPromiseId, gateTraceId, }; }