Replace createCursorRole/createHermesRole with adapter + prompt + zod meta. Add shared compileRoleSpec, cursor ask adapter, nerve.yaml extract defaults. Refs #248 Made-with: Cursor
53 lines
1.5 KiB
TypeScript
53 lines
1.5 KiB
TypeScript
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<T>(zod: z.ZodType<T>): ZodMetaSchema<T> {
|
|
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<M extends Record<string, unknown>>(
|
|
spec: RoleSpec<M>,
|
|
deps: CompileRoleSpecDeps,
|
|
): Role<M> {
|
|
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<M>).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,
|
|
});
|
|
}
|