Govern SDK
The AnyBio AI governance SDK for JavaScript and TypeScript. Install @anybio/govern, watch the gate hold a synthetic message, then put it in front of your own model, with adapters for the Vercel AI SDK and LangChain.
@anybio/govern wraps the evaluate call for JavaScript and TypeScript, with adapters for the Vercel AI SDK and LangChain. It runs server-side only: the constructor throws if it finds a browser window, because the application key is a secret.
Pre-1.0. The package is 0.2.x. The public surface may change in a minor version, so pin it and read the changelog before upgrading.
Prerequisites
Watch it hold something
Before wiring anything into your own code, make the gate stop a message. To see it without installing anything, try the gate in your browser first. The CLI posts a synthetic draft that names a cure and a medication change, and prints what came back:
npm install @anybio/govern
npx @anybio/govern test-messageSuccess looks like a refusal. In enforce you see the hold inline. In observe the gate runs off the request path, so the CLI hands you a decision command to read the would-have a moment later. No patient data is involved, and the throwaway conversation gets a disclosure recorded first, so the turn is complete rather than a fragment.
npx @anybio/govern init reads your package.json, detects the Vercel AI SDK or LangChain, and prints the install line, the environment variable and the file to add. It writes nothing unless you pass --write, and it never edits a file you already have.
Gate your own model
guard is the short path. It runs your draft function, evaluates what it returns, and hands you what to deliver:
import { Govern } from "@anybio/govern";
const govern = new Govern({
apiKey: process.env.ANYBIO_GOVERN_KEY,
application: "care-companion",
});
const reply = await govern.guard(
() => llm.chat(messages),
{ conversationId, turnId, channel: "sms", audience: "patient" },
);
if (reply.text !== "") await sendSms(patient.phone, reply.text);That if is the whole contract. reply.text is the draft when the gate allows it. When it does not, text is either the holding line your clinical owner wrote in advance, or the empty string when nothing should be sent to this patient at all - because they opted out, consent is missing or revoked, or the frequency cap is reached. The empty string is the one "send nothing" value, so a caller that sends whatever it is handed sends nothing.
Call reply.ack() after you deliver. If you do not, the next turn in the same conversation reports the previous one for you.
reply.decisionId is the record. Everything else about the decision, including the reasons and the policy version that made it, is on the same object.
Framework adapters
The adapters are the install; the raw client is the escape hatch. Each ships as a subpath export and declares its framework as an optional peer dependency.
import { wrapLanguageModel, streamText } from "ai";
import { withGovern } from "@anybio/govern/vercel-ai";
const model = wrapLanguageModel({
model: openai("gpt-4o"),
middleware: withGovern({ govern, channel: "web_chat", audience: "patient" }),
});
const result = streamText({
model,
messages,
providerOptions: {
anybioGovern: { conversationId: chatId, turnId: messageId },
},
});A patient-audience stream is buffered and released whole after the decision, or replaced. A clinician-audience stream passes through and is evaluated once it ends. The decision lands in providerMetadata.anybioGovern.
After a hold
A hold waits for a person. Three ways to learn it resolved, in ascending order of how much you want to run:
waitForRelease(decisionId)polls and returns the released content, or null.- A webhook.
handleWebhookverifies the signature and parses the event;WEBHOOK_EVENTSnames what can arrive. getDecision(decisionId)reads the current state whenever you ask.
If your first call fails
| What you see | Likely cause | Fix |
|---|---|---|
Constructor throws about window | The client was constructed in browser code | It is server-side only. Move it behind an API route |
GovernConfigError | A missing key, or a draft function returning something that is not text | The message names the field |
GovernNotAvailableError in enforce | The API could not be reached | This is a fail-closed hold, not a crash. reply.text carries the holding line when one is owed |
| Every decision comes back allow | The application's policy is in observe | Observe records what would have happened. Check wouldHave |
A replayed turnId returns the same decision | Working as intended | turnId is the idempotency key |
Reference
The package README is the exhaustive reference and ships with the code, so it never drifts from the version you installed: modes, channel rules, the holding line, consent and the frequency cap, hold resolution, disclosure, outcomes, streaming, and what the SDK does not cover.
@anybio/governon npm- Concepts - application, policy, decision, mode, hold
- The policy model - what a policy can say
- The observe engagement - two weeks of would-have before anything is enforced
Try the gate
Run a draft patient message through the AnyBio governance gate in your browser. No account, no key, nothing stored. See which word and which policy line hold it.
Govern for n8n
Put AnyBio Govern between a workflow's AI step and the patient. The n8n-nodes-anybio community package adds an AnyBio Govern node with three outputs, Deliver, Show Hold Text and Send Nothing, and a trigger for Govern webhooks. Install on self-hosted n8n, watch the gate route three drafts, then wire it into your own workflow.