UNPKG

akm-cli

Version:

akm (Agent Knowledge Manager) — a portable, local-first capability library for AI agents. Discover, load, share, and improve reusable skills, scripts, workflows, and knowledge across any shell-capable coding agent, including Claude Code, OpenCode, and Cur

103 lines (102 loc) • 4.02 kB
// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. /** * Summary-validation gate for workflow step/workflow completion (#506). * * Takes a step's `completionCriteria` and the summary of work the agent claims * to have done, asks the configured LLM to judge whether the summary * demonstrates each criterion is met, and returns either a pass or structured * corrective feedback steering the agent on what to finish/fix. * * The judge call is injected so the gate is unit-testable without a live * endpoint. A declared gate is fail-closed: only a well-formed affirmative * verdict advances the step. * * @module workflows/validate-summary */ import validateSummaryJudgePrompt from "../assets/prompts/validate-summary-judge.md" with { type: "text" }; import { parseJsonResponse } from "../core/parse.js"; /** * The one verdict parser: a well-formed verdict, or `undefined` when malformed * (which the engine treats as a verifier failure, never a rejection). */ export function parseJudgeVerdict(raw) { const parsed = parseJsonResponse(raw); if (!parsed || typeof parsed.complete !== "boolean") return undefined; return parsed; } const JUDGE_SYSTEM = validateSummaryJudgePrompt; function buildUserPrompt(input) { const criteria = input.completionCriteria.map((c, i) => `${i + 1}. ${c}`).join("\n"); return [ `Step: ${input.stepTitle}`, "", "Completion criteria:", criteria, "", "Agent's summary of work done:", input.summary.trim(), "", "Return the JSON verdict now.", ].join("\n"); } /** * Run the summary-validation gate. * * No criteria skips verification. A missing, failing, or malformed judge is a * rejection: declared criteria are never silently bypassed. */ export async function validateStepSummary(input, judge, signal) { const criteria = input.completionCriteria.filter((c) => c.trim().length > 0); if (criteria.length === 0) { return { complete: true, missing: [], skipped: true }; } if (!judge) { return { complete: false, missing: criteria, feedback: "This step declares completion criteria but no verification judge is available.", }; } let raw; try { if (signal?.aborted) throw interruptionReason(signal); raw = await judge({ system: JUDGE_SYSTEM, user: buildUserPrompt({ ...input, completionCriteria: criteria }) }); if (signal?.aborted) throw interruptionReason(signal); } catch { if (signal?.aborted) throw interruptionReason(signal); return { complete: false, missing: criteria, feedback: "The verification judge failed. Retry after fixing the verifier configuration or service.", }; } const parsed = parseJudgeVerdict(raw); if (!parsed) { return { complete: false, missing: criteria, feedback: "The verification judge returned a malformed verdict instead of the required JSON result.", }; } if (parsed.complete) { return { complete: true, missing: [] }; } const missing = Array.isArray(parsed.missing) ? parsed.missing.filter((m) => typeof m === "string" && m.trim().length > 0) : []; const feedback = typeof parsed.feedback === "string" && parsed.feedback.trim().length > 0 ? parsed.feedback.trim() : "The summary does not yet demonstrate every completion criterion is met. " + "Finish the outstanding work and resubmit with a summary that addresses each criterion."; return { complete: false, missing, feedback }; } function interruptionReason(signal) { return signal.reason instanceof Error ? signal.reason : new Error("Workflow verification interrupted."); }