TypeScript quickstart¶
npm install @responsibleai/agent-hooks
The package resolves a prebuilt native binary for your platform through
optionalDependencies (linux-x64-gnu, darwin-x64, darwin-arm64,
win32-x64-msvc today).
A minimal host with one control interceptor:
import {
AgentContextBuilder,
InterceptionBlocked,
InterceptionEmitter,
Verdict,
} from "@responsibleai/agent-hooks";
const CONFIDENTIAL = ["account_balance", "ssn"];
const egressGuard = {
intercept(context) {
if (context.interception_point !== "pre_tool_call") return Verdict.allow();
const args = JSON.stringify(context.tool_call.args);
if (CONFIDENTIAL.some((m) => args.includes(m))) {
return { decision: "deny", reason: "egress_blocked" };
}
return Verdict.allow();
},
};
const emitter = new InterceptionEmitter();
emitter.register(egressGuard, "egress-guard");
const build = new AgentContextBuilder({
agentId: "support-agent",
framework: "quickstart",
sessionId: "s-001",
});
const ok = await emitter.emit(build.preToolCall("t-1", "web_search", { q: "docs" }));
console.log("allowed:", ok.record.verdict.decision);
try {
await emitter.emit(build.preToolCall("t-2", "send_email", { body: "account_balance: 991" }));
} catch (e) {
if (!(e instanceof InterceptionBlocked)) throw e;
console.log("denied:", e.result.verdict.reason);
console.log("decided_by:", e.result.decided_by, "profile:", e.result.composition.profile);
}
Output:
allowed: allow
denied: egress_blocked
decided_by: 0 profile: sequential/first_deny
Things to notice:
- Builder methods take positional arguments:
preToolCall(callId, name, args). emit()throwsInterceptionBlockedon any block, with the record as.result; on success it resolves to{ record, target }, and your action must consume the effectivetarget.- A deny is a plain wire object;
Verdict.allow(),Verdict.warn()andVerdict.escalate()sugar exists for the other shapes. - The emitter pre-scans contexts for non-finite numbers before any
serialization, because JavaScript's
JSON.stringifywould silently turnNaNintonull; the scan fails closed instead.