Bahini
Open app

Core

Agents as code

Provision and govern agents from your pipeline — create one, grant its tools, bound its capabilities, gate risky actions, set a budget, and attach a golden-suite eval. Read the whole config back to diff against your desired state.

Create an agent#

instructions must be at least 10 characters; modelProfileId is optional (discover ids with listModelProfiles).

 
ts
const agent = await bahini.createAgent({
  key: "ticket-triage",
  name: "Ticket Triage",
  role: "Support triage assistant",
  instructions: "Classify inbound tickets by urgency and draft a first reply.",
});
// → { id, key, name }

Grant tools#

setAgentToolsis set-equality — it grants what's missing and revokes the rest. grantAgentTool / revokeAgentTool are the additive single-key forms. Discover valid keys with listTools.

 
ts
await bahini.setAgentTools(agent.id, ["http.request", "documents.search"]);
// → { added, removed, unchanged, skipped }

Bound its capabilities#

Capability scope is fail-closed. Restrict which hosts http.request may reach, deny tools outright, and fence storage to key prefixes.

 
ts
await bahini.setAgentCapabilityScope(agent.id, {
  allowedHttpDomains: ["api.stripe.com", "hooks.slack.com"],
  deniedTools: ["storage.delete"],
  storageKeyPrefixes: ["triage/"],
});

Gate risky actions#

 
ts
await bahini.setAgentApprovalPolicy(agent.id, {
  approvalGatedTools: ["email.send", "http.request"],
});

Set a budget#

Hard ceilings — the run halts when it crosses one.

 
ts
await bahini.setAgentBudget(agent.id, {
  maxIterations: 12,
  maxTokens: 200_000,
  maxDurationMs: 120_000,
  maxConsecutiveErrors: 3,
});

Attach a golden suite#

Declare the quality bar as data. Each case is a set of expectations (contains / equals / regex / numeric / shape, JSON-path aware). Run the agent, then read getRunEval for the verdict — a CI gate.

 
ts
await bahini.setAgentGoldenSuite(agent.id, [
  {
    name: "classifies urgent",
    expectations: [{ path: "$.priority", equals: "urgent" }],
  },
]);

Read & apply whole configs#

getAgentConfig(agentId)

One call returns tools + capability scope + approval policy + response format + guardrails + budget + golden suite — the canonical snapshot to diff against your desired IaC state.

 
ts
const config = await bahini.getAgentConfig(agent.id);

applyAgentConfig(agentId, config)

The write side. Only the surfaces you pass are touched; each goes through its own endpoint, so a failure on one is collected in errors and the rest still apply. Assert on the result in your pipeline.

 
ts
const { applied, errors } = await bahini.applyAgentConfig(agent.id, {
  tools: ["http.request"],
  capabilityScope: { allowedHttpDomains: ["api.stripe.com"] },
  budget: { maxIterations: 12 },
});
if (errors.length) throw new Error(JSON.stringify(errors));
Teardown. Agents are archived, not hard-deleted (they carry run history). updateAgent(id, { status: 'ARCHIVED' }) is the IaC teardown primitive; 'PAUSED' stops it from running, 'ACTIVE' re-enables it.