Skip to content

Core

Schemas, policy parsing, the check function and audit verification from @intentlock/core.

@intentlock/core holds the pieces every other package shares. It has no I/O: no network, no database, and no clock except one you pass in. You rarely need it directly, but it is there for tooling, tests and custom gates.

parsePolicy turns YAML into a validated policy, with line and column numbers for every error:

check-policy.ts
import { readFileSync } from "node:fs";
import { parsePolicy } from "@intentlock/core";

const parsed = parsePolicy(readFileSync("policy.yaml", "utf8"));
if (!parsed.ok) {
  for (const e of parsed.errors) {
    console.error(`${e.line}:${e.col} ${e.path} ${e.message}`);
  }
  process.exit(1);
}
console.log(parsed.policy.rules.map((r) => r.id));

STARTER_POLICY is the YAML every new agent starts with.

Zod schemas for the formats on the wire: Plan, PlanStep, ArgConstraint, PolicySet, Rule, Match, ApproverSpec, ToolCall, Decision, GateDecision, AuditEntry and ClientEntry. TOOL_NAME is the tool name pattern.

Verdict, BlockReason, ClientBlockReason, ReportOutcome and ClientEntryKind hold the values that cross packages and the wire.

check(bundle, call, usage) is the pure decision function used by the server and the gates. It takes the verified token claims, the call and the per-step usage counts, and never throws: any error becomes a block with internal_error. decide() wraps it with token verification and usage counting.

verifyChain(entries) walks exported audit entries in order and returns { ok: true, count } or the first broken sequence number with a reason. It is what intentlock audit verify runs.

verify-audit.ts
import { readFileSync } from "node:fs";
import { verifyChain } from "@intentlock/core";

const lines = readFileSync("audit.jsonl", "utf8").split("\n").filter(Boolean);
const result = await verifyChain(lines.map((line) => JSON.parse(line) as unknown));
if (result.ok) console.log(`ok, ${result.count} entries`);
else console.log(`broken at ${result.firstBrokenSeq}: ${result.reason}`);

API_ENDPOINTS lists every server route, and apiPath fills in parameters:

TypeScript
import { API_ENDPOINTS, apiPath } from "@intentlock/core";

const path = apiPath(API_ENDPOINTS.HOLD_WAIT, { id: "hold_123" });
const url = `https://intentlock.acme.dev/api${path}`;