@agenticrail/core
v0.1.2
Published
AgenticRail gate client for JavaScript and TypeScript
Maintainers
Readme
@agenticrail/core
AgenticRail gate client for JavaScript and TypeScript.
Works with any JS agent framework: LangGraph.js, Mastra, Genkit, custom loops.
npm install @agenticrail/coreQuick start
import { RailClient } from "@agenticrail/core";
const client = new RailClient({ apiKey: "DEMO-AGENTICRAIL-PUBLIC-2026" });
// RailSequence manages step_order state automatically
const seq = client.sequence("my-run-001", [
"verify_identity",
"assess_risk",
"execute_transfer",
"audit_ledger",
]);
await seq.next("verify_identity"); // → ALLOW
await seq.next("assess_risk"); // → ALLOW
await seq.next("execute_transfer"); // → ALLOW
await seq.next("audit_ledger"); // → ALLOW — seals sequence
// Any violation raises RailDenied
await seq.next("verify_identity"); // throws RailDenied: SEALED_SEQUENCELangGraph.js integration
import { RailClient, RailDenied } from "@agenticrail/core";
import { StateGraph, END } from "@langchain/langgraph";
const client = new RailClient({ apiKey: "YOUR_KEY" });
const STEP_ORDER = ["research", "analyze", "write"];
function railNode(stepName: string, fn: Function) {
// Track which sequences have been initialized (step_order sent)
const initialized = new Set<string>();
return async (state: any, config?: any) => {
const threadId =
config?.configurable?.thread_id ??
config?.configurable?.sequence_id ??
"default";
const stepOrder = initialized.has(threadId) ? undefined : STEP_ORDER;
if (!initialized.has(threadId)) initialized.add(threadId);
await client.evaluate({
sequenceId: threadId,
step: stepName,
stepOrder,
});
return fn(state, config);
};
}
const builder = new StateGraph({ channels: { messages: null } });
builder.addNode("research", railNode("research", researchFn));
builder.addNode("analyze", railNode("analyze", analyzeFn));
builder.addNode("write", railNode("write", writeFn));
builder.setEntryPoint("research");
builder.addEdge("research", "analyze");
builder.addEdge("analyze", "write");
builder.addEdge("write", END);
const graph = builder.compile();
await graph.invoke(
{ messages: [] },
{ configurable: { thread_id: "run-2026-001" } },
);API reference
RailClient
const client = new RailClient({
apiKey: "...", // Required
modelId: "agent", // Optional. Default: "agent"
baseUrl: undefined, // Optional. Default: api.agenticrail.nz/v1/evaluate
timeoutMs: 10_000, // Optional. Default: 10s
});client.evaluate(opts)
const decision = await client.evaluate({
sequenceId: "run-001",
step: "verify_identity",
actionType: "CHECK_STATE", // Optional
action: "verify user identity", // Optional
inputs: { userId: "u123" }, // Optional
stepOrder: [...], // Required on every call — gate reads it from each payload
raiseOnDeny: true, // Optional. Default: true
});
decision.decision // "ALLOW" | "DENY" | "HALT"
decision.allowed // true if ALLOW
decision.packId // SHA-256 receipt hash
decision.reasons // string[] — reason codes on DENY/HALT
decision.receipt // full signed receipt objectclient.sequence(sequenceId, stepOrder)
Returns a RailSequence that calls seq.next(step) without managing stepOrder manually.
RailDenied
try {
await seq.next("execute_transfer");
} catch (e) {
if (e instanceof RailDenied) {
console.log(e.decision); // "DENY" | "HALT"
console.log(e.reasons); // ["SEQUENCE_VIOLATION"]
console.log(e.packId); // receipt hash
console.log(e.step); // "execute_transfer"
}
}Requirements
- Node.js >= 18 (uses native
fetchandcrypto.randomUUID) - TypeScript >= 5.0 (for type declarations)
TUARA KURI LIMITED — trading as AgenticRail. Hokianga, New Zealand.
