import type { LoopIteration, GodModeRaceResult, PerturbationResult } from "../core/types.js"; import { IndependentVerifier, type VerificationResult } from "./independent-verifier.js"; import { ModelRouter, type TaskComplexity, type RoutingResponse } from "./model-router.js"; import { type FusionPanelSlug, type FusionResult, FUSION_PANELS, type GodModePanelSlug, } from "../core/fusion-types.js"; import { GodModeClassic } from "../upgrades/godmode-classic.js"; import { Parseltongue } from "../upgrades/parseltongue.js"; // ── Types ────────────────────────────────────────────────── export type WorkflowPattern = "fan-out-synthesize" | "adversarial-verify" | "loop-until-done" | "fusion-panel" | "godmode-race" | "parseltongue-test"; export type FanOutSubtaskFlow = "direct" | "planning" | "review"; export interface FanOutSubtaskRouteContext { task: string; subTask: string; index: number; flow: FanOutSubtaskFlow; reason: string; route: RoutingResponse; } export type FanOutExecutor = ( subTask: string, index: number, routeContext?: FanOutSubtaskRouteContext, ) => LoopIteration | Promise; export interface WorkflowStep { id: string; label: string; agent: (ctx: Record) => LoopIteration | Promise; dependsOn?: string[]; } export interface WorkflowDefinition { id: string; task: string; pattern: WorkflowPattern; steps: WorkflowStep[]; maxConcurrency?: number; } export interface MixtureOfAgentsMetadata { provider: "fable-fan-out"; workers: number; synthesizer: "caller-provided"; maxConcurrency: number; } export interface FanOutResult { iterations: LoopIteration[]; routeContexts: FanOutSubtaskRouteContext[]; synthesized: LoopIteration; synthesisVerdict: VerificationResult; mixtureOfAgents: MixtureOfAgentsMetadata; } export interface AdversarialResult { maker: LoopIteration; verifier: VerificationResult; passed: boolean; gapSummary: string; } export interface LoopUntilDoneResult { iterations: LoopIteration[]; finalVerdict: VerificationResult; totalRounds: number; } export interface FusionPanelResult { task: string; panelSlug: FusionPanelSlug; panelists: LoopIteration[]; synthesized: LoopIteration; synthesisVerdict: VerificationResult; fusionAnalysis: string; } // ── Dynamic Workflows ────────────────────────────────────── export class DynamicWorkflows { private verifier: IndependentVerifier; private modelRouter: ModelRouter; private godMode: GodModeClassic | null; private parseltongue: Parseltongue | null; constructor(verifier?: IndependentVerifier, modelRouter?: ModelRouter) { this.verifier = verifier ?? new IndependentVerifier(); this.modelRouter = modelRouter ?? new ModelRouter(); this.godMode = null; this.parseltongue = null; } /** * Enable GodMode Classic racing workflows. */ enableGodMode(gm?: GodModeClassic): void { this.godMode = gm ?? new GodModeClassic(); } /** * Enable Parseltongue perturbation testing workflows. */ enableParseltongue(pt?: Parseltongue): void { this.parseltongue = pt ?? new Parseltongue(); } /** * Fan-out-and-synthesize: split work into N independent pieces, * run an agent on each in parallel, synthesize results. * * Best when each sub-task benefits from its own clean context window — * e.g., evaluating each rule in a Skill against historical examples. */ async fanOutAndSynthesize( task: string, subTasks: string[], executor: FanOutExecutor, synthesizer: (results: LoopIteration[], originalTask: string) => LoopIteration | Promise, maxConcurrency: number = 3, ): Promise { // Phase 1: Fan out — run all sub-tasks (with concurrency limit) const routeContexts: FanOutSubtaskRouteContext[] = new Array(subTasks.length); const iterations = await this.mapWithConcurrency( subTasks.length, maxConcurrency, (index) => { const subTask = subTasks[index]; const routeContext = this.routeFanOutSubtask(task, subTask, index); routeContexts[index] = routeContext; return Promise.resolve(executor(subTask, index, routeContext)); }, ); // Phase 2: Synthesize const synthesized = await Promise.resolve(synthesizer(iterations, task)); // Phase 3: Verify the synthesis const synthesisVerdict = this.verifier.verify(synthesized, task); return { iterations, routeContexts, synthesized, synthesisVerdict, mixtureOfAgents: { provider: "fable-fan-out", workers: subTasks.length, synthesizer: "caller-provided", maxConcurrency, }, }; } /** * Adversarial verification: for a maker agent, spawn an independent verifier * with no exposure to the maker's reasoning. The structural fix for * self-preferential bias. * * The maker cannot see the verifier's criteria evaluation; the verifier only * sees the artifact and the rubric. This guarantees independent assessment. */ async adversarialVerify( makerIteration: LoopIteration, task: string, verifierConfig?: { criteria?: Array<{ id: string; label: string; description: string }>; minPassRate?: number }, ): Promise { // Create an independent verifier with optional custom criteria const verifier = verifierConfig ? new IndependentVerifier({ criteria: verifierConfig.criteria ?? [ { id: "correctness", label: "Correctness", description: "Output is technically correct" }, { id: "completeness", label: "Completeness", description: "All requirements met" }, { id: "consistency", label: "Consistency", description: "No contradictions within output" }, ], minPassRate: verifierConfig.minPassRate ?? 0.75, requireAllEssential: true, }) : this.verifier; // The verifier sees ONLY the artifact (executed/observation), NOT the reasoning trail const sanitized: LoopIteration = { ...makerIteration, plan: "", reflection: "", refinement: "", }; const result = verifier.verify(sanitized, task); const passed = result.verdict === "PASS"; const gapSummary = result.gaps.length > 0 ? result.gaps.join("; ") : "All criteria met"; return { maker: makerIteration, verifier: result, passed, gapSummary }; } /** * Loop-until-done: loop spawning agents until a stop condition is met. * Pairs with /goal to set a hard completion requirement. */ async loopUntilDone( task: string, executor: (iteration: number, previous: LoopIteration | null) => LoopIteration | Promise, options?: { maxIterations?: number; convergenceThreshold?: number; stopCondition?: (iteration: LoopIteration, result: VerificationResult) => boolean; }, ): Promise { const maxIterations = options?.maxIterations ?? 10; const convergenceThreshold = options?.convergenceThreshold ?? 0.75; const iterations: LoopIteration[] = []; let finalVerdict: VerificationResult = { verdict: "FAIL", criteriaResults: [], gaps: ["No iterations completed"], matches: [], suggestion: "No iterations ran", }; for (let i = 0; i < maxIterations; i++) { const previous = iterations.length > 0 ? iterations[iterations.length - 1] : null; const iteration = await Promise.resolve(executor(i, previous)); iterations.push(iteration); finalVerdict = this.verifier.verify(iteration, task); const passRate = finalVerdict.criteriaResults.filter((r) => r.passed).length / Math.max(finalVerdict.criteriaResults.length, 1); // Check stop conditions if (options?.stopCondition && options.stopCondition(iteration, finalVerdict)) { break; } if (finalVerdict.verdict === "PASS" && passRate >= convergenceThreshold) { break; } } return { iterations, finalVerdict, totalRounds: iterations.length }; } /** * Fusion Panel: dispatch the same task to N panelists in parallel, * then synthesize results with the judge model. * * Implements the draft → critique → fuse pattern from fusion-fable: * - All panelists get the same prompt, independently * - No "lenses" or personas — just raw independent execution * - Judge synthesizes with structured analysis */ async fusionPanel( task: string, executor: (prompt: string, panelistIndex: number) => LoopIteration | Promise, judge: (panelists: LoopIteration[], originalTask: string) => LoopIteration | Promise, panelSlug: FusionPanelSlug = "opus4.8-4.8", maxConcurrency: number = 3, ): Promise { const panel = FUSION_PANELS[panelSlug]; const panelistCount = panel.modelIds.length; // Phase 1: Run all panelists in parallel (with concurrency limit) const panelists = await this.mapWithConcurrency( panelistCount, maxConcurrency, (index) => Promise.resolve(executor(task, index)), ); // Phase 2: Judge synthesizes const synthesized = await Promise.resolve(judge(panelists, task)); // Phase 3: Verify synthesis const synthesisVerdict = this.verifier.verify(synthesized, task); return { task, panelSlug, panelists, synthesized, synthesisVerdict, fusionAnalysis: synthesized.observation ?? "Fusion complete", }; } /** * Get the recommended grader model for a workflow subtask. * Routes by complexity — cheap graders for simple tasks. */ getGraderForWorkflow(complexity: TaskComplexity): string { return this.modelRouter.getGraderForWorkflow(complexity); } // ── GodMode Race ──────────────────────────────────────────── /** * GodMode Race: Run N models in parallel, return best/first result. * Inspired by G0DM0D3's GODMODE CLASSIC. */ async godmodeRace( task: string, panelSlug?: GodModePanelSlug, ): Promise { if (!this.godMode) { this.enableGodMode(); } const gm = this.godMode!; return await gm.race(task, panelSlug); } // ── Parseltongue Test ─────────────────────────────────────── /** * Parseltongue Test: Run perturbation suite against a safety gate. * Inspired by G0DM0D3's Parseltongue red-teaming engine. */ async parseltongueTest( input: string, gateFn: (perturbed: string) => Promise | boolean, options?: { category?: "encoding" | "injection" | "obfuscation" | "framing" | "logic_trap" | "adversarial"; intensity?: "low" | "medium" | "high"; }, ): Promise<{ results: PerturbationResult[]; summary: { totalTests: number; bypassesDetected: number; gateWeaknesses: string[] }; }> { if (!this.parseltongue) { this.enableParseltongue(); } const pt = this.parseltongue!; return await pt.testGate(input, gateFn, options); } private routeFanOutSubtask(task: string, subTask: string, index: number): FanOutSubtaskRouteContext { const lower = `${subTask} ${task}`.toLowerCase(); const isPlanning = /\b(plan|design|architecture|strategy|roadmap|approach|proposal|scope|spec|mvp)\b/i.test(lower); const isReview = /\b(review|audit|check|verify|validation|assessment|safety|test|diff)\b/i.test(lower); const flow: FanOutSubtaskFlow = isPlanning ? "planning" : isReview ? "review" : "direct"; const complexity = flow === "direct" ? "simple" : "medium"; const domain: "code" | "planning" | "review" = flow === "planning" ? "planning" : flow === "review" ? "review" : "code"; const route = this.modelRouter.route({ task: `${task} ${subTask}`, domain, complexity, requiresVision: false, }); return { task, subTask, index, flow, reason: flow === "direct" ? "Direct/simple fanout -> cheap flow" : `Plan/review fanout -> routed as ${flow}`, route, }; } private async mapWithConcurrency( count: number, maxConcurrency: number, worker: (index: number) => Promise, ): Promise { const results: T[] = new Array(count); let nextIndex = 0; const workerCount = Math.max(1, Math.min(maxConcurrency, count)); await Promise.all( Array.from({ length: workerCount }, async () => { while (nextIndex < count) { const index = nextIndex++; results[index] = await worker(index); } }), ); return results; } }