import type { Role, RoleSpec, StartStep, WorkflowContext, WorkflowMessage, } from "@uncaged/nerve-core"; import type { LlmProvider } from "@uncaged/nerve-workflow-utils"; import { extractMetaOrThrow, type ZodMetaSchema } from "@uncaged/nerve-workflow-utils"; import type { z } from "zod"; export function zodMeta(zod: z.ZodType): ZodMetaSchema { return { witness: null, zod }; } export type CompileRoleSpecDeps = { provider: LlmProvider; createContext: (start: StartStep, messages: WorkflowMessage[]) => WorkflowContext; }; /** * RFC-003 RoleSpec → runtime Role (extract uses global nerve.yaml provider via `provider`; * dryRun follows each invocation's start frame). */ export function compileRoleSpec>( spec: RoleSpec, deps: CompileRoleSpecDeps, ): Role { return async (start, messages) => { const ctx = deps.createContext(start, messages); const promptText = typeof spec.prompt === "string" ? spec.prompt : await spec.prompt(start, messages); const raw = await spec.adapter(promptText, ctx); const zod = (spec.meta as ZodMetaSchema).zod; const meta = await extractMetaOrThrow(raw, zod, { provider: deps.provider, dryRun: start.meta.dryRun, }); return { content: raw, meta }; }; } export function defaultAgentCreateContext( workdir: string, ): (start: StartStep, messages: WorkflowMessage[]) => WorkflowContext { return (start, messages) => ({ start, messages, workdir, signal: new AbortController().signal, }); }