@kontextmind/kxm
Version:
KXM local-first multi-agent orchestration and operator dashboard
767 lines (720 loc) • 37.3 kB
text/typescript
import { createHash } from "node:crypto";
import { kxmCanonicalJson, kxmPortablePath, type JsonObject, type JsonValue } from "./project-config.ts";
import {
KXM_COMPILED_WORKFLOW_SCHEMA,
type KxmCompiledAssignments,
type KxmCompiledEvidence,
type KxmCompiledJoin,
type KxmCompiledPlan,
type KxmCompiledStep,
type KxmCompiledTransition,
type KxmRepositoryAccess,
} from "./engine-compile.ts";
import {
KXM_ABSENT_MEMORY_REVISION,
runtimeError,
type KxmRunEventStore,
type KxmRunRecord,
} from "./runtime-store.ts";
export const KXM_RUN_PLAN_SCHEMA = "kxm.run-plan.v2";
const SUPPORTED_KINDS = new Set(["agent", "moa", "gate", "approval", "wait"]);
const TERMINAL = new Set(["completed", "failed", "cancelled"]);
const JOIN_STRATEGIES = new Set(["all", "all-settled", "quorum", "first-success"]);
const EVIDENCE_KINDS = new Set(["assignment-result", "gate", "receipt", "approval", "artifact"]);
const REPOSITORY_ACCESS = new Set(["none", "read", "write"]);
const DISTINCT_BY = new Set(["provider", "model", "profile"]);
const ENVELOPE_REQUIRED = ["schema", "runId", "projectId", "homeRuntimeId", "workflowId", "revisions", "projectLimits", "plan", "gates"] as const;
const GATES_REQUIRED = ["registry", "definitions", "controlRoot"] as const;
const REGISTRY_PIN_REQUIRED = ["schema", "hash"] as const;
const CONTROL_ROOT_REQUIRED = ["repositoryId", "projectKey"] as const;
const GATE_REGISTRY_SCHEMA = "kxm.gate-registry.v1";
const REVISION_REQUIRED = ["config", "executorPolicy", "toolPolicy", "memory"] as const;
const PROJECT_LIMIT_REQUIRED = ["maxConcurrentRuns"] as const;
const PROJECT_LIMIT_OPTIONAL = ["maxRunDurationMs", "maxAgentTimeMs", "agentStepTimeoutMs"] as const;
const PLAN_REQUIRED = ["schema", "workflowId", "coordinator", "limits", "transitionBudget", "hasBackEdges", "entryStepId", "order", "steps", "requirePlanHash"] as const;
const PLAN_OPTIONAL = ["sourcePath", "reproOracle", "planHash"] as const;
const LIMIT_OPTIONAL = ["maxTransitions", "maxRunDurationMs", "maxAgentTimeMs", "maxModelCost", "currency"] as const;
const STEP_REQUIRED = [
"id",
"index",
"kind",
"maxAttempts",
"safeSpeculation",
"repositories",
"secrets",
"requiredEvidence",
"outcomes",
"transitions",
"requiresPlanHash",
"assignments",
"join",
] as const;
const STEP_OPTIONAL = ["description", "instructions", "timeoutMs", "tools", "model"] as const;
const ASSIGNMENT_REQUIRED = ["allowedAgents", "minimum", "target", "maximum", "maxParallel", "maxAttemptsPerAssignment", "distinctBy"] as const;
const ASSIGNMENT_OPTIONAL = ["maxWriteRepositories"] as const;
const JOIN_REQUIRED = ["strategy"] as const;
const JOIN_OPTIONAL = ["minimumPassed", "cancelRemaining"] as const;
const EVIDENCE_REQUIRED = ["key", "kind", "minimum", "reusableAcrossAttempts"] as const;
const EVIDENCE_OPTIONAL = ["producerPolicy"] as const;
export type KxmGateDefinition =
| { readonly kind: "command"; readonly argv: readonly string[]; readonly timeoutMs: number; readonly cwd?: "control" }
| { readonly kind: "artifacts-exist"; readonly paths: readonly string[] }
| { readonly kind: "reserved" };
export interface KxmPinnedGates {
readonly registry: { readonly schema: typeof GATE_REGISTRY_SCHEMA; readonly hash: string } | null;
readonly definitions: Readonly<Record<string, KxmGateDefinition>>;
readonly controlRoot: { readonly repositoryId: "control"; readonly projectKey: string };
}
export interface KxmRunPlanEnvelope {
readonly schema: typeof KXM_RUN_PLAN_SCHEMA;
readonly runId: string;
readonly projectId: string;
readonly homeRuntimeId: string;
readonly workflowId: string;
readonly revisions: {
readonly config: string;
readonly executorPolicy: string;
readonly toolPolicy: string;
readonly memory: string;
};
readonly projectLimits: {
readonly maxConcurrentRuns: number;
readonly maxRunDurationMs?: number;
readonly maxAgentTimeMs?: number;
readonly agentStepTimeoutMs?: number;
};
readonly plan: KxmCompiledPlan;
readonly gates: KxmPinnedGates;
}
export function kxmSha256(input: string): string {
return `sha256:${createHash("sha256").update(input, "utf8").digest("hex")}`;
}
/**
* Stable identity of what one step asks of one agent. It is equal across runs
* of the same compiled step and agent, and it excludes the run, assignment and
* attempt ids, the run objective, repositories, model and the context packet,
* so a repeated ask is recognisable whatever the run was asked to do.
*/
export function kxmStepAskSha256(plan: KxmCompiledPlan, stepId: string, agentId: string): string {
const step = Object.hasOwn(plan.steps, stepId) ? plan.steps[stepId] : undefined;
if (!step) throw runtimeError("run_plan_corrupt", plan.workflowId, `compiled plan is missing step ${stepId}`);
return kxmSha256(kxmCanonicalJson({
v: 1,
workflowId: plan.workflowId,
stepId,
kind: step.kind,
agentId,
instructions: step.instructions ?? null,
outcomes: [...step.outcomes],
requiredEvidence: step.requiredEvidence.map((entry) => entry.key),
}));
}
/**
* The record-time verdict of one settled attempt. Only gate-negative results
* are known when the attempt settles: a back edge is 'blocked', and a producer
* error, an unknown outcome or a terminal failure is 'failed'. A forward edge
* or a completed terminal is undecided here; acceptance is resolved later from
* the event log, so this never returns 'accepted'.
*/
export function kxmAttemptFinalOutcome(
step: Pick<KxmCompiledStep, "transitions">,
settled: { readonly resultClass: string; readonly outcome?: string | undefined },
): "blocked" | "failed" | undefined {
if (settled.resultClass !== "outcome") return "failed";
const outcome = settled.outcome;
const transition = outcome !== undefined && Object.hasOwn(step.transitions, outcome) ? step.transitions[outcome] : undefined;
if (!transition) return "failed";
if (transition.to === "terminal") return transition.terminalStatus === "completed" ? undefined : "failed";
return transition.edge === "back" ? "blocked" : undefined;
}
export function hashKxmRunPlanEnvelope(envelope: KxmRunPlanEnvelope): string {
return kxmSha256(kxmCanonicalJson(envelope as unknown as JsonValue));
}
export function freezeKxmCompiledPlan(plan: KxmCompiledPlan): KxmCompiledPlan {
const steps = Object.create(null) as Record<string, KxmCompiledStep>;
for (const id of plan.order) {
const step = plan.steps[id];
if (!step) throw runtimeError("run_plan_corrupt", plan.workflowId, `compiled plan is missing step ${id}`);
steps[id] = freezeStep(step);
}
return deepFreeze({
...plan,
steps,
order: [...plan.order],
requirePlanHash: [...plan.requirePlanHash],
});
}
function freezeStep(step: KxmCompiledStep): KxmCompiledStep {
const transitions = Object.create(null) as Record<string, KxmCompiledTransition>;
for (const outcome of step.outcomes) {
const transition = step.transitions[outcome];
if (!transition) throw runtimeError("run_plan_corrupt", step.id, `compiled plan is missing transition ${outcome}`);
transitions[outcome] = { ...transition };
}
const repositories = Object.create(null) as Record<string, KxmRepositoryAccess>;
for (const [id, access] of Object.entries(step.repositories)) repositories[id] = access;
return deepFreeze({
...step,
outcomes: [...step.outcomes],
transitions,
repositories,
secrets: step.secrets.map((entry) => ({ ...entry })),
requiredEvidence: step.requiredEvidence.map((entry) => ({ ...entry })),
assignments: {
...step.assignments,
allowedAgents: [...step.assignments.allowedAgents],
distinctBy: [...step.assignments.distinctBy],
},
join: { ...step.join },
});
}
export function parseKxmRunPlanEnvelope(raw: string, run: KxmRunRecord, expectedHash: string): KxmRunPlanEnvelope {
let parsed: unknown;
try {
parsed = JSON.parse(raw) as unknown;
} catch {
throw runtimeError("run_plan_corrupt", run.runId, "run plan envelope is not JSON");
}
const envelope = parseEnvelope(parsed, run);
const hash = hashKxmRunPlanEnvelope(envelope);
if (hash !== expectedHash) {
throw runtimeError("run_plan_corrupt", run.runId, "run plan envelope hash does not match");
}
return envelope;
}
export function rehydrateKxmCompiledPlanFromStore(store: KxmRunEventStore, run: KxmRunRecord): KxmCompiledPlan {
return loadPinnedEnvelope(store, run).plan;
}
export function loadKxmRunPlanEnvelope(store: KxmRunEventStore, run: KxmRunRecord): KxmRunPlanEnvelope {
return loadPinnedEnvelope(store, run);
}
function loadPinnedEnvelope(store: KxmRunEventStore, run: KxmRunRecord): KxmRunPlanEnvelope {
const row = store.runPlan(run.runId);
if (!row) throw runtimeError("run_plan_missing", run.runId, "run has no pinned plan");
const events = store.events(run.runId, 0, row.pinnedSequence);
const pinEvent = events.find((event) => event.sequence === row.pinnedSequence);
const eventHash = pinEvent?.payload.runPlanHash;
if (typeof eventHash !== "string" || eventHash !== row.runPlanHash) {
throw runtimeError("run_plan_corrupt", run.runId, "pinned runPlanHash does not match the plan row");
}
return parseKxmRunPlanEnvelope(row.envelope, run, row.runPlanHash);
}
export function absentMemoryRevision(): string {
return KXM_ABSENT_MEMORY_REVISION;
}
function parseEnvelope(parsed: unknown, run: KxmRunRecord): KxmRunPlanEnvelope {
const value = asObject(parsed, run.runId, "run plan envelope");
assertExactKeys(value, ENVELOPE_REQUIRED, [], run.runId, "run plan envelope");
if (value.schema !== KXM_RUN_PLAN_SCHEMA) {
throw runtimeError("run_plan_corrupt", run.runId, "schema is not kxm.run-plan.v2");
}
const runId = asString(value.runId, run.runId, "runId");
const projectId = asString(value.projectId, run.runId, "projectId");
const homeRuntimeId = asString(value.homeRuntimeId, run.runId, "homeRuntimeId");
const workflowId = asString(value.workflowId, run.runId, "workflowId");
if (runId !== run.runId || projectId !== run.projectId || homeRuntimeId !== run.homeRuntimeId || workflowId !== run.workflowId) {
throw runtimeError("run_plan_corrupt", run.runId, "run plan envelope identity does not match the run");
}
const revisionsValue = asObject(value.revisions, run.runId, "revisions");
assertExactKeys(revisionsValue, REVISION_REQUIRED, [], run.runId, "revisions");
const revisions = {
config: asString(revisionsValue.config, run.runId, "revisions.config"),
executorPolicy: asString(revisionsValue.executorPolicy, run.runId, "revisions.executorPolicy"),
toolPolicy: asString(revisionsValue.toolPolicy, run.runId, "revisions.toolPolicy"),
memory: asString(revisionsValue.memory, run.runId, "revisions.memory"),
};
if (
revisions.config !== run.configRevision
|| revisions.executorPolicy !== run.executorPolicyRevision
|| revisions.toolPolicy !== run.toolPolicyRevision
|| revisions.memory !== run.memoryRevision
) {
throw runtimeError("run_plan_corrupt", run.runId, "run plan envelope revisions do not match the run");
}
const limitsValue = asObject(value.projectLimits, run.runId, "projectLimits");
assertExactKeys(limitsValue, PROJECT_LIMIT_REQUIRED, PROJECT_LIMIT_OPTIONAL, run.runId, "projectLimits");
const projectLimits: KxmRunPlanEnvelope["projectLimits"] = {
maxConcurrentRuns: asCount(limitsValue.maxConcurrentRuns, run.runId, "projectLimits.maxConcurrentRuns"),
...(limitsValue.maxRunDurationMs !== undefined ? { maxRunDurationMs: asDuration(limitsValue.maxRunDurationMs, run.runId, "projectLimits.maxRunDurationMs") } : {}),
...(limitsValue.maxAgentTimeMs !== undefined ? { maxAgentTimeMs: asDuration(limitsValue.maxAgentTimeMs, run.runId, "projectLimits.maxAgentTimeMs") } : {}),
...(limitsValue.agentStepTimeoutMs !== undefined ? { agentStepTimeoutMs: asAgentStepTimeout(limitsValue.agentStepTimeoutMs, run.runId, "projectLimits.agentStepTimeoutMs") } : {}),
};
const plan = freezeKxmCompiledPlan(parseCompiledPlan(value.plan, run.runId));
return {
schema: KXM_RUN_PLAN_SCHEMA,
runId,
projectId,
homeRuntimeId,
workflowId,
revisions,
projectLimits,
plan,
gates: freezeGates(parseGates(value.gates, plan, run.runId)),
};
}
function parseGates(raw: unknown, plan: KxmCompiledPlan, runId: string): KxmPinnedGates {
const value = asObject(raw, runId, "gates");
assertExactKeys(value, GATES_REQUIRED, [], runId, "gates");
const referenced = new Set<string>();
for (const stepId of plan.order) {
const step = plan.steps[stepId];
if (step?.kind === "gate") referenced.add(step.gate);
}
const registry = parseRegistryPin(value.registry, runId, referenced.size > 0);
const controlRoot = parseControlRoot(value.controlRoot, runId);
const definitionsValue = asObject(value.definitions, runId, "gates.definitions");
const defined = Object.keys(definitionsValue);
for (const stepId of plan.order) {
const step = plan.steps[stepId];
if (step?.kind !== "gate") continue;
if (!(step.gate in definitionsValue)) {
throw runtimeError("run_plan_corrupt", runId, `gate step ${stepId} has no pinned definition`);
}
}
for (const gateId of defined) {
if (!referenced.has(gateId)) {
throw runtimeError("run_plan_corrupt", runId, `definition ${gateId} is not referenced`);
}
}
const definitions = Object.create(null) as Record<string, KxmGateDefinition>;
for (const gateId of defined) {
definitions[gateId] = parseGateDefinition(definitionsValue[gateId], gateId, runId);
}
return { registry, definitions, controlRoot };
}
function parseRegistryPin(raw: unknown, runId: string, required: boolean): KxmPinnedGates["registry"] {
if (raw === null) {
if (required) throw runtimeError("run_plan_corrupt", runId, "gate steps require a non-null registry pin");
return null;
}
const value = asObject(raw, runId, "gates.registry");
assertExactKeys(value, REGISTRY_PIN_REQUIRED, [], runId, "gates.registry");
if (value.schema !== GATE_REGISTRY_SCHEMA) {
throw runtimeError("run_plan_corrupt", runId, "gates.registry.schema is not kxm.gate-registry.v1");
}
const hash = asString(value.hash, runId, "gates.registry.hash");
if (!/^sha256:[a-f0-9]{64}$/.test(hash)) {
throw runtimeError("run_plan_corrupt", runId, "gates.registry.hash is not a sha256 digest");
}
return { schema: GATE_REGISTRY_SCHEMA, hash };
}
function parseControlRoot(raw: unknown, runId: string): KxmPinnedGates["controlRoot"] {
const value = asObject(raw, runId, "gates.controlRoot");
assertExactKeys(value, CONTROL_ROOT_REQUIRED, [], runId, "gates.controlRoot");
const repositoryId = asString(value.repositoryId, runId, "gates.controlRoot.repositoryId");
if (repositoryId !== "control") {
throw runtimeError("run_plan_corrupt", runId, "gates.controlRoot.repositoryId must be control");
}
return { repositoryId: "control", projectKey: asString(value.projectKey, runId, "gates.controlRoot.projectKey") };
}
export function parseGateDefinition(raw: unknown, gateId: string, runId: string): KxmGateDefinition {
const value = asObject(raw, runId, `gates.definitions.${gateId}`);
const kind = asString(value.kind, runId, `gates.definitions.${gateId}.kind`);
if (kind === "command") {
assertExactKeys(value, ["kind", "argv", "timeoutMs"], ["cwd"], runId, `gates.definitions.${gateId}`);
const argv = asStringArray(value.argv, runId, `gates.definitions.${gateId}.argv`);
if (argv.length < 1 || argv.length > 64) {
throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId}.argv length is invalid`);
}
for (const [index, entry] of argv.entries()) {
if (entry.length === 0 || entry.length > 4096 || entry.includes("\u0000")) {
throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId}.argv[${index}] is invalid`);
}
}
const executable = argv[0]!;
if (executable === "." || executable === ".." || executable.includes("\\") || (!executable.startsWith("/") && executable.includes("/"))) {
throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId} argv[0] must be a bare executable or absolute POSIX path`);
}
const timeoutMs = value.timeoutMs;
if (typeof timeoutMs !== "number" || !Number.isInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2_147_483_647) {
throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId}.timeoutMs is invalid`);
}
if (value.cwd !== undefined && value.cwd !== "control") {
throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId}.cwd is invalid`);
}
return {
kind: "command",
argv,
timeoutMs,
...(value.cwd === "control" ? { cwd: "control" as const } : {}),
};
}
if (kind === "artifacts-exist") {
assertExactKeys(value, ["kind", "paths"], [], runId, `gates.definitions.${gateId}`);
const paths = asStringArray(value.paths, runId, `gates.definitions.${gateId}.paths`);
if (paths.length < 1 || paths.length > 64) {
throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId}.paths length is invalid`);
}
if (new Set(paths).size !== paths.length) {
throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId}.paths are not unique`);
}
for (const path of paths) {
if (path === "." || !kxmPortablePath(path) || path.startsWith("/") || path.includes("\\")) {
throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId} path is not a portable relative path`);
}
}
return { kind: "artifacts-exist", paths };
}
if (kind === "reserved") {
assertExactKeys(value, ["kind"], [], runId, `gates.definitions.${gateId}`);
return { kind: "reserved" };
}
throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId} kind is invalid`);
}
function freezeGates(gates: KxmPinnedGates): KxmPinnedGates {
const definitions = Object.create(null) as Record<string, KxmGateDefinition>;
for (const [id, definition] of Object.entries(gates.definitions)) {
definitions[id] = deepFreeze({ ...definition, ...(definition.kind === "command" ? { argv: [...definition.argv] } : {}), ...(definition.kind === "artifacts-exist" ? { paths: [...definition.paths] } : {}) } as KxmGateDefinition);
}
return deepFreeze({
registry: gates.registry ? { ...gates.registry } : null,
definitions,
controlRoot: { ...gates.controlRoot },
});
}
function parseCompiledPlan(raw: unknown, runId: string): KxmCompiledPlan {
const value = asObject(raw, runId, "compiled plan");
assertExactKeys(value, PLAN_REQUIRED, PLAN_OPTIONAL, runId, "compiled plan");
if (value.schema !== KXM_COMPILED_WORKFLOW_SCHEMA) {
throw runtimeError("run_plan_corrupt", runId, "compiled plan schema is not kxm.compiled-workflow.v1");
}
const order = asStringArray(value.order, runId, "order");
const stepsValue = asObject(value.steps, runId, "steps");
const stepIds = Object.keys(stepsValue);
if (!sameSet(stepIds, order)) {
throw runtimeError("run_plan_corrupt", runId, "compiled plan step keys do not equal order");
}
const steps = Object.create(null) as Record<string, KxmCompiledStep>;
for (const stepId of order) {
steps[stepId] = parseStep(stepsValue[stepId], stepId, runId);
}
const entryStepId = asString(value.entryStepId, runId, "entryStepId");
if (!steps[entryStepId] || !order.includes(entryStepId)) {
throw runtimeError("run_plan_corrupt", runId, "entryStepId is not in compiled steps");
}
const limitsObject = asObject(value.limits, runId, "limits");
assertExactKeys(limitsObject, [], LIMIT_OPTIONAL, runId, "limits");
const limits: KxmCompiledPlan["limits"] = {
...(limitsObject.maxTransitions !== undefined ? { maxTransitions: asCount(limitsObject.maxTransitions, runId, "limits.maxTransitions") } : {}),
...(limitsObject.maxRunDurationMs !== undefined ? { maxRunDurationMs: asDuration(limitsObject.maxRunDurationMs, runId, "limits.maxRunDurationMs") } : {}),
...(limitsObject.maxAgentTimeMs !== undefined ? { maxAgentTimeMs: asDuration(limitsObject.maxAgentTimeMs, runId, "limits.maxAgentTimeMs") } : {}),
...(limitsObject.maxModelCost !== undefined ? { maxModelCost: asCost(limitsObject.maxModelCost, runId, "limits.maxModelCost") } : {}),
...(limitsObject.currency !== undefined ? { currency: asString(limitsObject.currency, runId, "limits.currency") } : {}),
};
const requirePlanHash = asStringArray(value.requirePlanHash, runId, "requirePlanHash");
for (const stageId of requirePlanHash) {
if (!steps[stageId]) throw runtimeError("run_plan_corrupt", runId, `requirePlanHash references unknown step ${stageId}`);
}
const plan: KxmCompiledPlan = {
schema: KXM_COMPILED_WORKFLOW_SCHEMA,
workflowId: asString(value.workflowId, runId, "workflowId"),
...(value.sourcePath !== undefined ? { sourcePath: asStringAllowEmpty(value.sourcePath, runId, "sourcePath") } : {}),
coordinator: asString(value.coordinator, runId, "coordinator"),
limits,
transitionBudget: asCount(value.transitionBudget, runId, "transitionBudget"),
hasBackEdges: asBoolean(value.hasBackEdges, runId, "hasBackEdges"),
entryStepId,
order,
steps,
...(value.reproOracle !== undefined ? { reproOracle: parseOracle(value.reproOracle, steps, runId, "reproOracle") } : {}),
...(value.planHash !== undefined ? { planHash: parseOracle(value.planHash, steps, runId, "planHash") } : {}),
requirePlanHash,
};
return plan;
}
function parseStep(raw: unknown, stepId: string, runId: string): KxmCompiledStep {
const value = asObject(raw, runId, `step ${stepId}`);
const kind = asString(value.kind, runId, `${stepId}.kind`);
if (!SUPPORTED_KINDS.has(kind)) {
throw runtimeError("run_plan_corrupt", runId, `step ${stepId} has unsupported kind ${kind}`);
}
const extraRequired = kind === "agent" || kind === "moa"
? ["agent"]
: kind === "gate"
? ["gate", "expect"]
: kind === "wait"
? ["signal"]
: [];
assertExactKeys(value, [...STEP_REQUIRED, ...extraRequired], STEP_OPTIONAL, runId, `step ${stepId}`);
const id = asString(value.id, runId, `${stepId}.id`);
if (id !== stepId) throw runtimeError("run_plan_corrupt", runId, `step ${stepId} id does not match its key`);
const outcomes = asStringArray(value.outcomes, runId, `${stepId}.outcomes`);
if (new Set(outcomes).size !== outcomes.length) {
throw runtimeError("run_plan_corrupt", runId, `step ${stepId} outcomes are not unique`);
}
const sorted = [...outcomes].sort();
if (outcomes.join("\0") !== sorted.join("\0")) {
throw runtimeError("run_plan_corrupt", runId, `step ${stepId} outcomes are not sorted`);
}
const transitionsValue = asObject(value.transitions, runId, `${stepId}.transitions`);
if (!sameSet(Object.keys(transitionsValue), outcomes)) {
throw runtimeError("run_plan_corrupt", runId, `step ${stepId} transition keys do not equal outcomes`);
}
const transitions = Object.create(null) as Record<string, KxmCompiledTransition>;
for (const outcome of outcomes) {
transitions[outcome] = parseTransition(transitionsValue[outcome], stepId, outcome, runId);
}
const repositoriesValue = asObject(value.repositories, runId, `${stepId}.repositories`);
const repositories = Object.create(null) as Record<string, KxmRepositoryAccess>;
for (const [repositoryId, access] of Object.entries(repositoriesValue)) {
if (typeof access !== "string" || !REPOSITORY_ACCESS.has(access)) {
throw runtimeError("run_plan_corrupt", runId, `step ${stepId} repository ${repositoryId} has invalid access`);
}
repositories[repositoryId] = access as KxmRepositoryAccess;
}
const base = {
id,
index: asIndex(value.index, runId, `${stepId}.index`),
...(value.description !== undefined ? { description: asStringAllowEmpty(value.description, runId, `${stepId}.description`) } : {}),
...(value.instructions !== undefined ? { instructions: asStringAllowEmpty(value.instructions, runId, `${stepId}.instructions`) } : {}),
maxAttempts: asCount(value.maxAttempts, runId, `${stepId}.maxAttempts`),
...(value.timeoutMs !== undefined ? { timeoutMs: asDuration(value.timeoutMs, runId, `${stepId}.timeoutMs`) } : {}),
safeSpeculation: asBoolean(value.safeSpeculation, runId, `${stepId}.safeSpeculation`),
repositories,
...(value.tools !== undefined ? { tools: asJsonObject(value.tools, runId, `${stepId}.tools`) } : {}),
secrets: asObjectArray(value.secrets, runId, `${stepId}.secrets`).map((entry) => ({ ...entry })),
...(value.model !== undefined ? { model: cloneJson(value.model) } : {}),
requiredEvidence: asUnknownArray(value.requiredEvidence, runId, `${stepId}.requiredEvidence`).map((entry, index) => (
parseEvidence(entry, stepId, index, runId)
)),
outcomes,
transitions,
requiresPlanHash: asBoolean(value.requiresPlanHash, runId, `${stepId}.requiresPlanHash`),
assignments: parseAssignments(value.assignments, stepId, runId),
join: parseJoin(value.join, stepId, runId),
};
if (kind === "agent" || kind === "moa") {
return { ...base, kind, agent: asString(value.agent, runId, `${stepId}.agent`) };
}
if (kind === "gate") {
const expect = asString(value.expect, runId, `${stepId}.expect`);
if (expect !== "pass" && expect !== "fail") {
throw runtimeError("run_plan_corrupt", runId, `step ${stepId} expect is invalid`);
}
return { ...base, kind, gate: asString(value.gate, runId, `${stepId}.gate`), expect };
}
if (kind === "wait") {
return { ...base, kind, signal: asString(value.signal, runId, `${stepId}.signal`) };
}
return { ...base, kind: "approval" };
}
function parseTransition(raw: unknown, stepId: string, outcome: string, runId: string): KxmCompiledTransition {
const value = asObject(raw, runId, `${stepId}.${outcome}`);
const to = asString(value.to, runId, `${stepId}.${outcome}.to`);
const maxTransitions = value.maxTransitions !== undefined
? asCount(value.maxTransitions, runId, `${stepId}.${outcome}.maxTransitions`)
: undefined;
if (to === "step") {
assertExactKeys(value, ["to", "target", "edge"], ["maxTransitions"], runId, `${stepId}.${outcome}`);
const edge = asString(value.edge, runId, `${stepId}.${outcome}.edge`);
if (edge !== "forward" && edge !== "back") {
throw runtimeError("run_plan_corrupt", runId, `step ${stepId} transition ${outcome} edge is invalid`);
}
return {
to: "step",
target: asString(value.target, runId, `${stepId}.${outcome}.target`),
edge,
...(maxTransitions !== undefined ? { maxTransitions } : {}),
};
}
if (to === "terminal") {
assertExactKeys(value, ["to", "terminalStatus"], ["maxTransitions"], runId, `${stepId}.${outcome}`);
const terminalStatus = asString(value.terminalStatus, runId, `${stepId}.${outcome}.terminalStatus`);
if (!TERMINAL.has(terminalStatus)) {
throw runtimeError("run_plan_corrupt", runId, `step ${stepId} has an illegal terminal status`);
}
return {
to: "terminal",
terminalStatus: terminalStatus as "completed" | "failed" | "cancelled",
...(maxTransitions !== undefined ? { maxTransitions } : {}),
};
}
throw runtimeError("run_plan_corrupt", runId, `step ${stepId} transition ${outcome} is not a discriminated transition`);
}
function parseAssignments(raw: unknown, stepId: string, runId: string): KxmCompiledAssignments {
const value = asObject(raw, runId, `${stepId}.assignments`);
assertExactKeys(value, ASSIGNMENT_REQUIRED, ASSIGNMENT_OPTIONAL, runId, `${stepId}.assignments`);
const distinctBy = asStringArray(value.distinctBy, runId, `${stepId}.assignments.distinctBy`);
if (distinctBy.some((entry) => !DISTINCT_BY.has(entry))) {
throw runtimeError("run_plan_corrupt", runId, `step ${stepId} distinctBy is invalid`);
}
return {
allowedAgents: asStringArray(value.allowedAgents, runId, `${stepId}.assignments.allowedAgents`),
minimum: asCount(value.minimum, runId, `${stepId}.assignments.minimum`),
target: asCount(value.target, runId, `${stepId}.assignments.target`),
maximum: asCount(value.maximum, runId, `${stepId}.assignments.maximum`),
maxParallel: asCount(value.maxParallel, runId, `${stepId}.assignments.maxParallel`),
maxAttemptsPerAssignment: asCount(value.maxAttemptsPerAssignment, runId, `${stepId}.assignments.maxAttemptsPerAssignment`),
...(value.maxWriteRepositories !== undefined
? { maxWriteRepositories: asCount(value.maxWriteRepositories, runId, `${stepId}.assignments.maxWriteRepositories`) }
: {}),
distinctBy: distinctBy as Array<"provider" | "model" | "profile">,
};
}
function parseJoin(raw: unknown, stepId: string, runId: string): KxmCompiledJoin {
const value = asObject(raw, runId, `${stepId}.join`);
assertExactKeys(value, JOIN_REQUIRED, JOIN_OPTIONAL, runId, `${stepId}.join`);
const strategy = asString(value.strategy, runId, `${stepId}.join.strategy`);
if (!JOIN_STRATEGIES.has(strategy)) {
throw runtimeError("run_plan_corrupt", runId, `step ${stepId} join strategy is invalid`);
}
return {
strategy: strategy as KxmCompiledJoin["strategy"],
...(value.minimumPassed !== undefined ? { minimumPassed: asCount(value.minimumPassed, runId, `${stepId}.join.minimumPassed`) } : {}),
...(value.cancelRemaining !== undefined ? { cancelRemaining: asBoolean(value.cancelRemaining, runId, `${stepId}.join.cancelRemaining`) } : {}),
};
}
function parseEvidence(raw: unknown, stepId: string, index: number, runId: string): KxmCompiledEvidence {
const value = asObject(raw, runId, `${stepId}.requiredEvidence[${index}]`);
assertExactKeys(value, EVIDENCE_REQUIRED, EVIDENCE_OPTIONAL, runId, `${stepId}.requiredEvidence[${index}]`);
const kind = asString(value.kind, runId, `${stepId}.requiredEvidence[${index}].kind`);
if (!EVIDENCE_KINDS.has(kind)) {
throw runtimeError("run_plan_corrupt", runId, `step ${stepId} evidence kind is invalid`);
}
return {
key: asString(value.key, runId, `${stepId}.requiredEvidence[${index}].key`),
kind: kind as KxmCompiledEvidence["kind"],
minimum: asCount(value.minimum, runId, `${stepId}.requiredEvidence[${index}].minimum`),
reusableAcrossAttempts: asBoolean(value.reusableAcrossAttempts, runId, `${stepId}.requiredEvidence[${index}].reusableAcrossAttempts`),
...(value.producerPolicy !== undefined ? { producerPolicy: parseProducerPolicy(value.producerPolicy, stepId, runId) } : {}),
};
}
function parseProducerPolicy(raw: unknown, stepId: string, runId: string): NonNullable<KxmCompiledEvidence["producerPolicy"]> {
const value = asObject(raw, runId, `${stepId}.producerPolicy`);
assertExactKeys(value, ["minimumProducers", "eligibleAgents", "acceptedStatuses"], ["degradation"], runId, `${stepId}.producerPolicy`);
const accepted = value.acceptedStatuses;
if (!Array.isArray(accepted) || accepted.length !== 1 || accepted[0] !== "passed") {
throw runtimeError("run_plan_corrupt", runId, `step ${stepId} producerPolicy.acceptedStatuses is invalid`);
}
let degradation: { minimumProducers: number } | undefined;
if (value.degradation !== undefined) {
const nested = asObject(value.degradation, runId, `${stepId}.producerPolicy.degradation`);
assertExactKeys(nested, ["minimumProducers"], [], runId, `${stepId}.producerPolicy.degradation`);
degradation = { minimumProducers: asCount(nested.minimumProducers, runId, `${stepId}.producerPolicy.degradation.minimumProducers`) };
}
return {
minimumProducers: asCount(value.minimumProducers, runId, `${stepId}.producerPolicy.minimumProducers`),
eligibleAgents: asStringArray(value.eligibleAgents, runId, `${stepId}.producerPolicy.eligibleAgents`),
acceptedStatuses: ["passed"],
...(degradation ? { degradation } : {}),
};
}
function parseOracle(
raw: unknown,
steps: Readonly<Record<string, KxmCompiledStep>>,
runId: string,
label: string,
): { stageId: string; evidenceKey: string } {
const value = asObject(raw, runId, label);
assertExactKeys(value, ["stageId", "evidenceKey"], [], runId, label);
const stageId = asString(value.stageId, runId, `${label}.stageId`);
const evidenceKey = asString(value.evidenceKey, runId, `${label}.evidenceKey`);
const step = steps[stageId];
if (!step) throw runtimeError("run_plan_corrupt", runId, `${label} references unknown stage ${stageId}`);
if (!step.requiredEvidence.some((item) => item.key === evidenceKey)) {
throw runtimeError("run_plan_corrupt", runId, `${label} references undeclared evidence ${evidenceKey}`);
}
return { stageId, evidenceKey };
}
function asObject(value: unknown, runId: string, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw runtimeError("run_plan_corrupt", runId, `${label} is not an object`);
}
return value as Record<string, unknown>;
}
function assertExactKeys(
value: Record<string, unknown>,
required: readonly string[],
optional: readonly string[],
runId: string,
label: string,
): void {
const allowed = new Set([...required, ...optional]);
for (const key of required) {
if (!(key in value)) throw runtimeError("run_plan_corrupt", runId, `${label} is missing ${key}`);
}
for (const key of Object.keys(value)) {
if (!allowed.has(key)) throw runtimeError("run_plan_corrupt", runId, `${label} has unexpected key ${key}`);
}
}
function asString(value: unknown, runId: string, label: string): string {
if (typeof value !== "string" || value.length === 0) {
throw runtimeError("run_plan_corrupt", runId, `${label} is not a non-empty string`);
}
return value;
}
function asStringAllowEmpty(value: unknown, runId: string, label: string): string {
if (typeof value !== "string") throw runtimeError("run_plan_corrupt", runId, `${label} is not a string`);
return value;
}
function asBoolean(value: unknown, runId: string, label: string): boolean {
if (typeof value !== "boolean") throw runtimeError("run_plan_corrupt", runId, `${label} is not a boolean`);
return value;
}
function asCount(value: unknown, runId: string, label: string): number {
if (typeof value !== "number" || !Number.isInteger(value) || value < 1) {
throw runtimeError("run_plan_corrupt", runId, `${label} is not a positive integer`);
}
return value;
}
function asDuration(value: unknown, runId: string, label: string): number {
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
throw runtimeError("run_plan_corrupt", runId, `${label} is not a duration integer`);
}
return value;
}
function asAgentStepTimeout(value: unknown, runId: string, label: string): number {
if (typeof value !== "number" || !Number.isInteger(value) || value < 60_000) {
throw runtimeError("run_plan_corrupt", runId, `${label} is below 60000`);
}
return value;
}
function asCost(value: unknown, runId: string, label: string): number {
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
throw runtimeError("run_plan_corrupt", runId, `${label} is not a positive cost`);
}
return value;
}
function asIndex(value: unknown, runId: string, label: string): number {
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
throw runtimeError("run_plan_corrupt", runId, `${label} is not a non-negative integer`);
}
return value;
}
function asStringArray(value: unknown, runId: string, label: string): string[] {
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
throw runtimeError("run_plan_corrupt", runId, `${label} is not a string array`);
}
return [...value];
}
function asUnknownArray(value: unknown, runId: string, label: string): unknown[] {
if (!Array.isArray(value)) throw runtimeError("run_plan_corrupt", runId, `${label} is not an array`);
return value;
}
function asObjectArray(value: unknown, runId: string, label: string): JsonObject[] {
if (!Array.isArray(value)) throw runtimeError("run_plan_corrupt", runId, `${label} is not an array`);
return value.map((entry, index) => asJsonObject(entry, runId, `${label}[${index}]`));
}
function asJsonObject(value: unknown, runId: string, label: string): JsonObject {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw runtimeError("run_plan_corrupt", runId, `${label} is not an object`);
}
return cloneJson(value) as JsonObject;
}
function cloneJson(value: unknown): JsonValue {
return JSON.parse(JSON.stringify(value)) as JsonValue;
}
function sameSet(left: readonly string[], right: readonly string[]): boolean {
if (left.length !== right.length) return false;
const set = new Set(right);
return left.every((entry) => set.has(entry));
}
function deepFreeze<T>(value: T): T {
if (value === null || typeof value !== "object") return value;
if (Array.isArray(value)) {
for (const entry of value) deepFreeze(entry);
} else {
for (const entry of Object.values(value as Record<string, unknown>)) deepFreeze(entry);
}
return Object.freeze(value);
}