Getting started

Install the SDK, run one governed agent, and learn where each concept lives. Ten minutes, one API key (or none — the ungoverned example works offline with a recorded cassette).

Using an AI coding assistant? Cendor’s types teach the correct call-shape inline (on hover and completion), so Copilot / Claude / Cursor get it right as you type — and there’s a trap-sheet you can paste into your assistant: For AI assistants.

Fastest start. npx @cendor/init (Node) or uvx cendor-init (Python) wires Cendor and your AI assistant in one step — it detects your project, writes the correct assistant rules files, can add the MCP config, and scaffolds a working instrument() call. Offline, no key. A companion … doctor catches wiring mistakes before they bite. See For AI assistants.

1. Install

pip install "cendor-sdk[openai,anthropic]"
# Using uv? Same names, same extras: `uv add` instead of `pip install`.

Provider SDKs are optional extras — [openai], [anthropic], [google], [bedrock], [ollama], [huggingface], [azure], [foundry-local], plus [mcp], [otel], and [all]. The install pulls in the seven libraries the SDK is built on — cendor-core (the instrument() seam + event bus), contextkit, squeeze, tokenguard, cendor-guardrails, cassette, and acttrace — by dependency; import only from cendor.sdk.

npm i @cendor/sdk openai

Provider SDKs are peer dependencies — add openai and/or @anthropic-ai/sdk for the providers you call. ESM-only; Node LTS first, edge runtimes supported. Everything imports from @cendor/sdk.

2. Bring one env var

The SDK builds the provider client for you — so you don’t set a Cendor key, you set the provider’s own env var. For OpenAI (used in the quickstart below) that’s OPENAI_API_KEY:

export OPENAI_API_KEY="sk-..."         # macOS / Linux
$env:OPENAI_API_KEY = "sk-..."         # Windows PowerShell

Prefer to pass it in code? Use Agent(api_key=…) / new Agent({ apiKey: … }), or hand over a pre-built client with Agent(client=…). Every other provider reads its own standard variable (ANTHROPIC_API_KEY, GOOGLE_API_KEY, the AWS credential chain for Bedrock, …) — the full table is in API keys & credentials. The SDK doesn’t read .env files; load them yourself. No key yet? The ungoverned and testable examples below run offline from a recorded cassette — no credentials needed.

3. A governed agent in 10 lines

One agent, one tool, and all four governance layers — budget cap, PII guard, audit chain, and a cost/usage receipt:

from cendor.sdk import Agent, tool, run, budget, guard, Policy, AuditLog

@tool
def get_weather(city: str) -> str:
    """Current weather for a city."""      # schema derived from type hints + docstring
    return f"Sunny in {city}"

agent = Agent(name="assistant", model="gpt-4o", tools=[get_weather],
              instructions="Answer using tools when helpful.")

log = AuditLog(system="support", risk_tier="limited", path="audit.jsonl")
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)                        # the final answer
print(result.cost, result.usage)            # Decimal money, real token usage
print([s.name for s in result.tool_steps])  # ["get_weather"]
import { Agent, tool, run, withBudget, guard, Policy, AuditLog } from '@cendor/sdk';
import { z } from 'zod';

const getWeather = tool(({ city }) => `Sunny in ${city}`, {
  name: 'get_weather',
  description: 'Current weather for a city',
  parameters: z.object({ city: z.string() }),   // TS has no runtime type hints — zod is the schema
});

const agent = new Agent({ name: 'assistant', model: 'gpt-4o', tools: [getWeather],
                          instructions: 'Answer using tools when helpful.' });

const audit = new AuditLog('support', { riskTier: 'limited', path: 'audit.jsonl' });
const result = await withBudget({ usd: 0.25, onExceed: 'block' }, () =>
  guard({ policy: Policy.default(), audit }, () =>
    run(agent, "What's the weather in Paris?", { audit })));

console.log(result.output);                          // the final answer
console.log(result.cost?.toString(), result.usage);  // decimal money, real token usage
console.log(result.toolSteps.map((s) => s.name));    // ["get_weather"]

What each line buys you (and the library it comes from):

  • @tool / tool(...) — the JSON Schema comes from type hints + docstring (Python) or a zod 4 schema (TS); it’s formatted per provider automatically.
  • budget(usd=0.25, on_exceed="block") — a pre-flight cap: the over-budget call never runs (from tokenguard).
  • guard(Policy.default(), ...) — PII is redacted before the provider sees it (from acttrace).
  • AuditLog(...) — every model and tool call lands in a tamper-evident hash chain (verify("audit.jsonl") checks it offline) (from acttrace).
  • result.cost — decimal money, never a float, aggregated across the whole run (priced by tokenguard).

Not shown here but one argument away: Agent(context_budget=…) fits history to a token budget via contextkit/squeeze, and Agent(guardrails=[…]) gates the loop with cendor-guardrails.

4. Run it ungoverned — core only

Every governance layer is optional. Drop the wrappers and it’s a bare loop on cendor-core:

from cendor.sdk import Agent, run

agent = Agent(name="a", model="gpt-4o", instructions="Be brief.")
result = run(agent, "Hello")               # sync
result = await run.aio(agent, "Hello")     # async — same signature
import { Agent, run } from '@cendor/sdk';

const agent = new Agent({ name: 'a', model: 'gpt-4o', instructions: 'Be brief.' });
const result = await run(agent, 'Hello');   // TS is async throughout

5. Make it testable

Record the run once; replay it offline forever — deterministic, no network, no keys. Cost and tokens are real on replay, so tests can assert spend too:

from cendor import cassette

with cassette.using("tests/fixtures/run.json"):   # records on first run, replays after
    result = run(agent, "What's the weather in Paris?")
import { using } from '@cendor/cassette';

const result = await using('tests/fixtures/run.json', () =>   // records once, replays after
  run(agent, "What's the weather in Paris?"));

This is the foundation of the eval harness, which turns recorded trajectories into CI regression tests.

6. Where each concept lives

I want to…Go toLibrary underneath
Understand Agent, tool, run, ResultAgents & the loopcendor-core
Cap spend, attribute costGovernancetokenguard
Audit, redact PIIGovernanceacttrace
Block / redact / flag at four stagesGuardrailscendor-guardrails
Fit history to a token budgetAgents & the loopcontextkit + squeeze
Make the agent remember across turns/processesMemory & sessionscontextkit
Give the agent my documentsRetrieval (RAG)contextkit
Use more than one agentMulti-agent— (SDK orchestration)
Connect Gemini / Bedrock / Ollama / Hugging Face / AzureProviderscendor-core
Consume MCP tools, serve A2A, emit OTel spansEcosystem & interopcendor-core
Survive crashes, retries, long runsProduction hardening— (SDK)
Record once / replay; gate regressions in CIEval & regression testingcassette

The full map — every SDK symbol, the library that powers it, and where it plugs into the loop — is on Architecture.

Coming from the libraries? budget, track, Policy, AuditLog, and trace are the identical library objects here, re-exported for one-import convenience — and guard is acttrace’s policy enforcement in the SDK’s scope form. Nothing to relearn. The reverse also holds: see FAQ → libraries or SDK.