UNPKG

agent-contracts-runtime

Version:

Runtime bridge for executing agent-contracts workflows on Agent SDKs

266 lines (263 loc) 8.79 kB
// src/lib/dsl-context.ts import { readFile } from "fs/promises"; import YAML from "yaml"; // src/lib/schema-converter.ts import { z } from "zod"; function applyStringValidations(schema, def) { let s = schema; if (typeof def.minLength === "number") s = s.min(def.minLength); if (typeof def.maxLength === "number") s = s.max(def.maxLength); if (typeof def.pattern === "string") s = s.regex(new RegExp(def.pattern)); return s; } function applyNumberValidations(schema, def, isInteger) { let n = schema; if (isInteger) n = n.int(); if (typeof def.minimum === "number") n = n.min(def.minimum); if (typeof def.maximum === "number") n = n.max(def.maximum); return n; } function applyArrayValidations(schema, def) { let a = schema; if (typeof def.minItems === "number") a = a.min(def.minItems); if (typeof def.maxItems === "number") a = a.max(def.maxItems); return a; } function jsonSchemaToZod(schema) { if (!schema || typeof schema !== "object") return z.unknown(); if (schema.enum) { const vals = schema.enum; if (vals.length === 0) return z.unknown(); return z.enum(vals); } if (schema.const !== void 0) { const val = schema.const; if (typeof val === "string" || typeof val === "number" || typeof val === "boolean") { return z.literal(val); } return z.unknown(); } const type = schema.type; if (type === "string") return applyStringValidations(z.string(), schema); if (type === "integer") return applyNumberValidations(z.number(), schema, true); if (type === "number") return applyNumberValidations(z.number(), schema, false); if (type === "boolean") return z.boolean(); if (type === "array") { const items = schema.items; const inner = items ? jsonSchemaToZod(items) : z.unknown(); return applyArrayValidations(z.array(inner), schema); } if (type === "object") { const props = schema.properties; if (!props) return z.record(z.string(), z.unknown()); const required = new Set(schema.required ?? []); const shape = {}; for (const [key, val] of Object.entries(props)) { const fieldSchema = jsonSchemaToZod(val); shape[key] = required.has(key) ? fieldSchema : fieldSchema.optional(); } return z.object(shape).passthrough(); } if (schema.allOf) { const parts = schema.allOf.map(jsonSchemaToZod); if (parts.length === 0) return z.unknown(); if (parts.length === 1) return parts[0]; let merged = parts[0]; for (let i = 1; i < parts.length; i++) { const part = parts[i]; if (merged instanceof z.ZodObject && part instanceof z.ZodObject) { merged = merged.merge(part); } } return merged; } return z.unknown(); } // src/lib/dsl-context.ts var DslValidationError = class extends Error { missing; constructor(missing) { super( "Required entities missing after DSL merge:\n" + missing.map((m) => ` - ${m}`).join("\n") ); this.name = "DslValidationError"; this.missing = missing; } }; function buildAgentRegistry(agents) { const registry = {}; for (const [id, agent] of Object.entries(agents)) { const rawMode = agent.mode ?? "read-only"; const mode = rawMode === "read-write" ? "read-write" : "read-only"; registry[id] = { id, role_name: agent.role_name ?? id, purpose: agent.purpose ?? "", mode, responsibilities: agent.responsibilities ?? [], constraints: agent.constraints ?? [], rules: agent.rules ?? [], can_invoke_agents: agent.can_invoke_agents ?? void 0, can_execute_tools: agent.can_execute_tools ?? void 0 }; } return registry; } function buildTaskRegistry(tasks) { const registry = {}; for (const [id, task] of Object.entries(tasks)) { registry[id] = { id, description: task.description ?? "", target_agent: task.target_agent ?? "", result_handoff: task.result_handoff ?? "", completion_criteria: task.completion_criteria ?? [], model_class: task.model_class }; } return registry; } function buildWorkflowStep(step) { if (step.type === "gate") { return { type: "gate", gate_kind: step.gate_kind ?? "", description: step.description ?? "", depends_on: step.depends_on }; } return { type: "delegate", task: step.task ?? "", from_agent: step.from_agent ?? "", description: step.description ?? "", optional: step.optional ?? false, max_retries: step.max_retries ?? 0, max_follow_ups: step.max_follow_ups, depends_on: step.depends_on, retry: step.retry }; } function buildWorkflowRegistry(workflows) { const registry = {}; for (const [id, wf] of Object.entries(workflows)) { const steps = (wf.steps ?? []).map(buildWorkflowStep); registry[id] = { id, description: wf.description ?? "", steps }; } return registry; } function buildHandoffSchemas(handoffTypes) { const schemas = {}; for (const [id, ht] of Object.entries(handoffTypes)) { const schema = ht.schema; if (schema) { schemas[id] = jsonSchemaToZod(schema); } } return schemas; } function buildGuardrailRulesFromDsl(dsl) { const raw = dsl._guardrailRules; if (!raw) return void 0; return { commandRules: raw.commandRules ?? [], fileRules: raw.fileRules ?? [], contentRules: raw.contentRules ?? [] }; } function buildRegistriesFromDsl(dsl) { const agents = dsl.agents ?? {}; const tasks = dsl.tasks ?? {}; const workflows = dsl.workflow ?? {}; const handoffTypes = dsl.handoff_types ?? {}; return { agentRegistry: buildAgentRegistry(agents), taskRegistry: buildTaskRegistry(tasks), workflowRegistry: buildWorkflowRegistry(workflows), handoffSchemas: buildHandoffSchemas(handoffTypes) }; } function validateRequiredEntities(dsl, required) { const missing = []; const workflows = dsl.workflow ?? {}; const tasks = dsl.tasks ?? {}; const handoffTypes = dsl.handoff_types ?? {}; if (required.workflows) { for (const wfId of required.workflows) { if (!(wfId in workflows)) { missing.push(`workflow "${wfId}" is required by the CLI but not found in the resolved DSL`); } } } if (required.tasks) { for (const taskId of required.tasks) { if (!(taskId in tasks)) { missing.push(`task "${taskId}" is required by the CLI but not found in the resolved DSL`); continue; } const task = tasks[taskId]; const invHandoff = task.invocation_handoff; const resHandoff = task.result_handoff; if (invHandoff && !(invHandoff in handoffTypes)) { missing.push(`handoff_type "${invHandoff}" (invocation_handoff of task "${taskId}") not found`); } if (resHandoff && !(resHandoff in handoffTypes)) { missing.push(`handoff_type "${resHandoff}" (result_handoff of task "${taskId}") not found`); } } } if (required.workflows) { for (const wfId of required.workflows) { if (!(wfId in workflows)) continue; const wf = workflows[wfId]; const steps = wf.steps ?? []; for (const step of steps) { if (step.type !== "delegate") continue; const taskId = step.task; if (taskId && taskId in tasks) { const task = tasks[taskId]; const resHandoff = task.result_handoff; if (resHandoff && !(resHandoff in handoffTypes)) { missing.push(`handoff_type "${resHandoff}" (result of task "${taskId}" in workflow "${wfId}") not found`); } } } } } if (missing.length > 0) { throw new DslValidationError(missing); } } async function loadDslContext(options) { let dsl; if (options.projectDslPath) { const { resolve: resolveDsl, mergeDsl, expandDefaults } = await import("agent-contracts"); const raw = await readFile(options.projectDslPath, "utf8"); const projectData = YAML.parse(raw); if (projectData.extends === "/$embedded/") { const { extends: _extends, ...projectOverrides } = projectData; const merged = mergeDsl(options.embeddedDsl, projectOverrides); dsl = expandDefaults(merged); } else { const projectResult = await resolveDsl(options.projectDslPath); const merged = mergeDsl(options.embeddedDsl, projectResult.data); dsl = expandDefaults(merged); } } else { dsl = options.embeddedDsl; } if (options.requiredEntities) { validateRequiredEntities(dsl, options.requiredEntities); } const registries = buildRegistriesFromDsl(dsl); const guardrailRules = buildGuardrailRulesFromDsl(dsl); return { registries, dsl, guardrailRules }; } export { jsonSchemaToZod, DslValidationError, buildGuardrailRulesFromDsl, buildRegistriesFromDsl, loadDslContext }; //# sourceMappingURL=chunk-D2QTXQB2.js.map