A governed agent in 10 lines.
cendor-sdk is a governed, provider-agnostic agent SDK. Cost budgets, a deterministicguardrails gate, tamper-evident audit, PII redaction, and record/replay testing are the foundation, not plugins — so the same ten lines run in production and in an offline test. In Python and TypeScript.
from cendor.sdk import Agent, tool, run, budget, guard, rules, Policy, AuditLog
@tool
def get_weather(city: str) -> str:
"Current weather for a city."
return f"Sunny in {city}"
agent = Agent(name="assistant", model="gpt-4o", tools=[get_weather],
guardrails=[rules.keyword_deny(["ignore previous instructions"])])
log = AuditLog(system="support", path="audit.jsonl")
# budget caps pre-flight; the Gate blocks for $0; guard() redacts PII pre-send
with budget(usd=0.25, on_exceed="block"), guard(Policy.default(), audit=log):
result = run(agent, "What's the weather in Paris?", audit=log)
print(result.output, result.cost) # governed, gated, audited, replayableimport OpenAI from 'openai';
import { Agent, run, tool, withBudget, guard, rules, Policy, AuditLog } from '@cendor/sdk';
import { z } from 'zod';
const getWeather = tool((a: { city: string }) => `Sunny in ${a.city}`, {
name: 'get_weather', description: 'Current weather for a city',
parameters: z.object({ city: z.string() }),
});
const agent = new Agent({
name: 'assistant', model: 'gpt-4o', tools: [getWeather], client: new OpenAI(),
guardrails: [rules.keywordDeny(['ignore previous instructions'])],
});
const log = new AuditLog('support', { path: 'audit.jsonl' });
// budget caps pre-flight; the Gate blocks for $0; guard() redacts PII pre-send
const result = await withBudget({ usd: 0.25, onExceed: 'block' }, () =>
guard({ policy: Policy.default(), audit: log }, () =>
run(agent, "What's the weather in Paris?", { audit: log })));
console.log(result.output, result.cost.toString());Drop the budget/guard context and it's a plain agent on cendor-core alone — governance is opt-in, never a rewrite.
The governed loop, running.
The story, the real ten-line agent running, and the seven-library map — the anchor for the SDK sub-series.
Governance is the foundation.
Other SDKs bolt observability on afterward. Here, every run is budgeted, gated, audited, and replayable by construction — built on the same seven libraries you can drop down to at any time. The cards below compose the six governance tools into the loop; squeeze engages automatically and core is the seam every call rides. Every chip is a link — drop into any library.
Pre-flight caps block or downgrade an over-budget call before a token is spent; per-feature / per-user cost attribution for free.
Agent(guardrails=[…]) gates four stages — input, tool call, tool result, output — and blocks, redacts, or flags. Deterministic rules run in microseconds for $0; opt-in detection tiers reach up to LLM judges and hosted rails, and a per-run override swaps rules per request.
guard() scans and redacts before send (or blocks) via a policy — detected secrets and PII are stripped before the request leaves your process, and the refusal is auditable.
Every call, tool, and decision — gate verdicts included — lands on a hash-chained log that verifies offline; one edited byte fails. Evidence to support compliance, not a guarantee.
Capture a whole run once, replay it offline forever — deterministic evals over cassettes, no API key, no flakiness.
Agent(context_budget=…) assembles history to a token budget on every turn — oversized blocks compressed reversibly via squeeze (no direct call — it auto-wires when installed), with an honest receipt of what was kept or dropped.
The seven libraries, inbuilt.
The loop on top, the full SDK-symbol → library map underneath — the SDK adds the loop, not the governance.
What run() actually does.
Every step below happens on a single run(agent, input) — no per-call wiring. The same loop runs sync, async, and streaming.
- Input gate
guardrails checks the user turn first — a block raises before any spend; a redact rewrites what the model will see.
- Assemble
contextkit packs history into the token budget; squeeze compresses oversized blocks, reversibly.
- Pre-flight
tokenguard projects the cost and blocks or downgrades over-cap calls; acttrace's
guard()redacts secrets & PII before send. - The call core
Every provider call rides core's
instrument()seam onto the event bus — one canonical shape, ten providers. - Tool gates
Each tool call and tool result passes the gate too — a blocked tool returns
[blocked by …]to the model and the loop continues. - Output gate
The final answer is gated before you see it. On a block, opt into a bounded re-ask or buffered streaming checks (Python today — parity).
- Evidence
Calls, tools, costs, and every gate decision land on the tamper-evident chain — and the whole run replays offline via cassette.
Every run leaves evidence.
One governed run writes one hash-chained log — every entry correlated by a single trace id, verifiable offline, forever.
Re-walks the chain with no network and no key — one edited byte fails at the exact entry.
A gate trip lands as a guardrail_decision; with a policy file, the decision records which policy was active (its hash and version).
log.export(…, framework=…) maps entries to EU AI Act, ISO 42001, GDPR, or NIST AI RMF controls — evidence to support compliance, never a guarantee.
Everything an agent SDK needs.
Sync + async, streaming, bounded max_turns; steps harvested into Result.steps.
@tool derives a JSON Schema from type hints (Optional/Union/Literal/nested Pydantic).
Cross-provider handoff carries canonical history; supervisor() routing, segment-bound.
Session, SQLite store, summarizing memory, crash-resume checkpointing.
dataclass / Pydantic / JSON-schema output_type with provider-native modes.
MCP client — call any external MCP server's tools from your agent; A2A server; M365 / Foundry adapter; post-hoc OTel gen_ai.* span trees — see the whole run journey live in Cendor Monitor.
One canonical message shape. Ten providers.
The provider is inferred from the model id (or forced). Provider extras stay optional — pip install "cendor-sdk[openai,anthropic]".
…and beyond providers:
Point your assistant at the SDK docs.
Agent-mode assistants (Claude Code, Cursor, Copilot, Windsurf) can call the Cendor MCP server to look up the exact Agent / run / guardrails call-shape live — e.g. get_page("sdk/agents") — instead of guessing. Read-only, pull-based: your code never leaves your machine. Set it up →
One video per capability.
The SDK sub-series — the loop, memory & retrieval, multi-agent, providers & interop, and production-readiness. Each one story-first, then the real code.
Start governed.
Bring one env var: OPENAI_API_KEY (or your provider's) — or pass api_key= / a pre-built client. The SDK builds the client for you; there's no Cendor key. Keys & providers →
Fastest: npx @cendor/init / uvx cendor-init detects the SDK and writes your AI-assistant rules files — offline, no key.
Pulls the seven governance libraries as hard deps. Already have a framework? Use the libraries beneath it instead.