agent-contracts-runtime
Version:
Runtime bridge for executing agent-contracts workflows on Agent SDKs
266 lines (263 loc) • 8.79 kB
JavaScript
// 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