export interface AdversaryVerdict { verdict: "continue" | "complete"; feedback: string; review?: { durationMs: number; session: "fresh" | "resumed"; runId?: string; turns?: number; toolCount?: number; tokens?: number; }; } export const ADVERSARY_SCHEMA = { type: "object", additionalProperties: false, required: ["verdict", "feedback"], properties: { verdict: { type: "string", enum: ["continue", "complete"], description: "Whether independently verified gaps remain.", }, feedback: { type: "string", description: "Direct, evidence-based instructions for the Main Agent. Do not restate the verdict, mention a review or reviewer, or include session and usage statistics.", }, }, } as const; function messageText(content: unknown): string { if (typeof content === "string") return content.trim(); if (!Array.isArray(content)) return ""; return content .map((part) => { if (!part || typeof part !== "object") return ""; const candidate = part as { type?: unknown; text?: unknown }; return candidate.type === "text" && typeof candidate.text === "string" ? candidate.text.trim() : ""; }) .filter(Boolean) .join("\n"); } export function extractUserGoal(entries: readonly unknown[]): string { const messages: string[] = []; for (const entry of entries) { if (!entry || typeof entry !== "object") continue; const candidate = entry as { type?: unknown; message?: { role?: unknown; content?: unknown } }; if (candidate.type !== "message" || candidate.message?.role !== "user") continue; const text = messageText(candidate.message.content); if (text) messages.push(text); } return messages.map((message, index) => `User message ${index + 1}:\n${message}`).join("\n\n"); } export function buildAdversaryTask(userGoal = ""): string { const goal = userGoal.trim(); return [ "Your role is the Main Agent's independent adversarial verifier and strategic thinking partner.", "The Main Agent owns implementation. You own verification, challenge, and feedback.", "", "Focus on whether the goal given by the user has been fully achieved in practice. The user's messages are authoritative; the meta files are the Main Agent's working interpretation and may be incomplete or wrong.", "Do not confuse an attempted implementation, a plausible result, passing partial checks, or the Main Agent's completion claim with the user's goal actually being fulfilled.", ...(goal ? ["", "User-given goal:", goal] : []), "", "Read `.pi/meta/GOAL.md`, `.pi/meta/LEDGER.md`, `.pi/meta/STATUS.md`, and `.pi/meta/ROADMAP.md`, then investigate whatever workspace evidence matters.", "Actively verify the result when useful: run relevant tests, builds, and checks; exercise live behavior; reproduce important claims; and probe edge cases, failure modes, and unintended effects.", "Follow important user-facing paths end to end under realistic conditions when feasible, rather than treating isolated tests or implementation inspection as sufficient evidence.", "When end-to-end or live evidence is missing, ask the Main Agent to obtain it and report the actual commands or actions, observed outcomes, and any remaining limitations.", "You may create or edit artifacts that help the review. Do not take over the implementation or complete the user's task yourself.", "Think broadly as well as locally. Question assumptions, focus on what actually matters, step back from repeated approaches, and suggest a materially different direction when useful.", "Always push toward the full user-given goal. Never recommend shrinking, renaming, downgrading, or substituting a smaller goal because the work is large, difficult, or incomplete. Only the user may change the goal.", "When much remains, turn that into forward pressure: identify the full remaining work, encourage decomposition and parallel delegation, and tell the Main Agent to keep implementing beyond the next batch until everything is complete.", 'Return exactly one JSON object and no other text: {"verdict":"complete"|"continue","feedback":"..."}. Use `complete` only when the goal is genuinely supported by evidence, including end-to-end or live evidence where feasible. Otherwise use `continue` with direct instructions addressed to the Main Agent: explain what remains and give useful direction, encouragement, or big-picture reframing that preserves the full goal. Do not restate the verdict, mention this review or reviewer, or include session and usage statistics in feedback.', ].join("\n"); } export function buildAdversaryFollowUpTask(): string { return [ "The Main Agent has ended another substantive work pass.", "Continue from your existing review memory. Re-read the current durable notes and inspect the changed workspace evidence; focus on unresolved concerns, new claims, and whether the user's goal is now fully achieved rather than repeating a broad review from scratch.", "Run targeted tests or live checks when they provide useful evidence. Look for realistic end-to-end evidence of important user-facing behavior, not only isolated checks. Probe important edge cases and challenge premature completion, but do not take over implementation.", "Push upward toward the full user-given goal. Never suggest reducing or relabeling the goal to fit partial progress. If the remaining work is large, encourage decomposition, parallel delegation, and sustained implementation rather than stopping after another small batch.", 'Return exactly one JSON object and no other text: {"verdict":"complete"|"continue","feedback":"..."}. Use `complete` only when the full goal is genuinely supported by evidence. Otherwise use `continue` with concise, direct instructions that push the Main Agent through all remaining work, including missing live or end-to-end evidence. Do not restate the verdict, mention this review or reviewer, or include session and usage statistics in feedback.', ].join("\n\n"); } function parseJsonText(value: string): unknown { const trimmed = value.trim(); const fenced = trimmed.match(/^```(?:json)?\s*([\s\S]*?)\s*```$/i); try { return JSON.parse(fenced?.[1] ?? trimmed); } catch { return undefined; } } export function parseAdversaryVerdict(value: unknown): AdversaryVerdict | undefined { const parsed = typeof value === "string" ? parseJsonText(value) : value; if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return undefined; const candidate = parsed as Record; if (candidate.verdict !== "continue" && candidate.verdict !== "complete") return undefined; if (typeof candidate.feedback !== "string" || !candidate.feedback.trim()) return undefined; return { verdict: candidate.verdict, feedback: candidate.feedback.trim() }; } export function formatAdversaryVerdict(result: AdversaryVerdict): string { if (result.verdict === "complete") return `COMPLETE\n\n${result.feedback}`; return [ "Continue working head down toward the full user goal.", "", result.feedback, "", "Work through the entire remaining queue now. Use subagents for independent workstreams and keep working on your own workstream while they run. Do not stop after a small batch, a commit, a progress report, or a documentation update, and do not stop to seek assessment or approval. Continue until no known tasks, gaps, unchecked roadmap items, stubs, failing checks, or unverified behaviors remain.", ].join("\n"); }