Architecture — the libraries inside the SDK

cendor-sdk is two layers, one install: an agent loop on top, and the seven Cendor libraries underneath. The loop gives you Agent, tool, run, sessions, RAG, multi-agent handoff, and interop; every governance behaviour is a library beneath it — the SDK contains no governance logic of its own, it just wires the libraries to the loop through cendor-core’s event bus. This page is the map: where each library shows up in the SDK, and which SDK surface it powers.

The two layers

The SDK adds the orchestration — the ReAct loop, tool dispatch, handoff, memory, retrieval — and delegates every governance concern (context, cost, safety, evidence, testing) downward. So the count that matters is seven libraries: the six tools plus the cendor-core foundation they all stand on.

before the call · pre-flight
contextkitassemble
squeezecompress
tokenguardbudget
guardrailsinput gate
acttraceredact
the call
coreinstrument()
after · automatic, via the bus
guardrailstool + output gate
cassetterecord
acttraceaudit
cendor-core — the instrument() seam + one event bus beneath it all. Every stage inside the amber band is a library subscribing on this bus, never a direct import; the band itself is cendor-sdk's run() loop, driving each turn. The SDK adds the loop, not the governance.

Two libraries act on both sides of a call — guardrails gates the input and the output, acttrace guards (redacts) before send and audits after. Everything ships under the cendor.* (Python) / @cendor/* (TypeScript) namespace; the SDK re-exports the objects you reach for, so from cendor.sdk import budget is the same budget as from cendor.tokenguard import budget — drop down to the libraries at any time, it’s continuous, never a migration.

Where each library is used in the SDK

One section per library. Each carries a “Where it’s used in the SDK” table — the SDK symbol or parameter, what the library does there, and the SDK concept page that documents it — plus the absolute link to the library’s own docs.

contextkit — fit history to the window

Every turn, the loop assembles the running conversation (and any retrieved passages) into the model’s token budget. Set Agent(context_budget=…) / contextBudget and each turn packs history through contextkit, emitting an audited AssemblyReport — a receipt of what was kept, shrunk, or dropped.

SDK surfaceWhat contextkit does thereConcept page
Agent(context_budget=…) / contextBudgetassemble history to a token budget, once per turnMemory & sessions
the runner’s per-turn assemblypacks Session history + retriever passages into the windowAgents & the loop
RAG passage injectionretrieved context enters assembly like any other blockRetrieval (RAG)

Full library docs: /docs/contextkit.

squeeze — compress oversized blocks

squeeze has no direct SDK API — and that’s deliberate. It is a hard dependency, but you never call it from cendor.sdk. At import it registers a Compressor on cendor-core’s protocol seam; contextkit picks it up whenever a block is marked evict="compress", and session trimming benefits the same way. Install it and it engages; without it present, evict="compress" falls back to truncation.

SDK surfaceWhat squeeze does thereConcept page
no direct symbolauto-wires as a Compressor on core’s seam at import(this section)
Agent(context_budget=…) + evict="compress" blockscontextkit calls squeeze to shrink oversized blocks reversiblyMemory & sessions
session trimmingthe same compression path keeps long memory boundedMemory & sessions

Full library docs: /docs/squeeze.

tokenguard — cap and attribute spend

Every governed run prices its calls and can refuse an over-budget one before it fires. The budget, attribution, and price-table objects are tokenguard’s, re-exported from cendor.sdk.

SDK surfaceWhat tokenguard does thereConcept page
budget(...) / withBudget(...), BudgetExceededpre-flight (or post-flight) spend cap around a runGovernance → budgets
track(...) / report(...)attribute ambient spend by feature / user / agentGovernance → attribution
Agent(max_usd=…) / maxUsdper-agent pre-flight cap (block semantics), enforced per segmentMulti-agent → per-agent budgets
register_model_price(...) / configure(...)price unpriced / custom-deployment models so USD caps bindProviders → pricing

Full library docs: /docs/tokenguard.

guardrails — the four-stage gate

Agent(guardrails=[…]) attaches the deterministic cendor-guardrails Gate to four points of the loop — input, tool call, tool result, output — with block / redact / flag. The rule factories, the LLM-judge helpers, the presets, and the policy loader all come from the library.

SDK surfaceWhat guardrails does thereConcept page
Agent(guardrails=[…], guardrail_mode=…)gate four stages; blocking or parallel executionGuardrails
rules.* (keyword_deny, regex_rule, intent, custom_category, …)the deterministic + semantic rule factoriesGuardrails → core concepts
judge / task_adherencebuild an LLM-judge check (intent / tool-call alignment)Guardrails → task adherence
presets, policy_schema, load_policycurated starters + versioned policy filesGuardrails → hosted rails & config

Full library docs: /docs/guardrails.

cassette — record once, replay forever

Testability is cassette. The eval harness drives it, and you scope a recording with cassette directly — but cassette is not re-exported by the SDK. Import it from the umbrella: from cendor import cassette (Python) / @cendor/cassette (TypeScript). It surfaces in the SDK only through the eval harness (evaluate / EvalCase).

SDK surfaceWhat cassette does thereConcept page
from cendor import cassette (not cendor.sdk)record/replay a whole run, deterministic + offlineGovernance → testing
the eval harness (evaluate, EvalCase)replays recorded trajectories as CI tests — behaviour and spendEval & regression testing

Full library docs: /docs/cassette.

acttrace — redact + tamper-evident audit

Privacy and evidence are acttrace. guard(Policy…) redacts PII before the provider sees it; an AuditLog records every step in a hash chain you can verify() offline; and the SDK bridges acttrace’s detector catalogue into the Gate as rules.pii / secrets / entropy.

SDK surfaceWhat acttrace does thereConcept page
guard(Policy…), Policy, PolicyViolationprocess-global PII/secret redaction on the interceptor seamGovernance → audit + redaction
AuditLog, verify(...)append every event to a tamper-evident chain; verify offlineGovernance → audit + redaction
rules.pii / rules.secrets / rules.entropyacttrace detectors bridged into the Gate, gating all four stages (incl. tool_output)Guardrails → PII & secrets

Full library docs: /docs/acttrace. Evidence to support compliance — never a compliance guarantee.

core — the seam every call rides

cendor-core is the foundation: the instrument() seam, the in-process event bus, the price table, and the trace correlation the whole loop rides. The SDK’s only hard dependency is core; every other library cooperates through it.

SDK surfaceWhat core does thereConcept page
the loop’s model calls (instrument)wrap the provider client once; publish a normalized LLMCallAgents & the loop → how it works
trace(...) / current_trace_idcorrelate every call & tool in a run under one idAgents & the loop
prices / provider detectioncount tokens, price usage, detect the provider from the model idProviders

Full library docs: /docs/core.

The feature → library map

The canonical map of which library powers which SDK surface. (The Home and Getting started pages carry short versions; this is the full one.)

I want to…SDK surfaceLibrary underneath
Understand Agent / tool / run / Resultthe loopcendor-core
Fit history to a token budgetAgent(context_budget=…)contextkit (+ squeeze when installed)
Compress oversized blocksauto via evict="compress"squeeze — no direct SDK symbol
Cap spend / attribute costbudget / track / report, Agent(max_usd=…)tokenguard
Block / redact / flag at four stagesAgent(guardrails=[…]), rules.*cendor-guardrails
Redact PII, keep tamper-evident evidenceguard(Policy…), AuditLog, verifyacttrace
Record once / replay; gate regressions in CIfrom cendor import cassette, evaluate / EvalCasecassette
Remember across turns / processesSession, SQLiteSessionStore, context_budgetcontextkit
Give the agent my documentsAgent(retriever=…), VectorIndex, embedcontextkit + cendor-core
Use more than one agenthandoff / supervisor / sequential / parallel— (SDK orchestration)
Connect Gemini / Bedrock / Ollama / HF / AzureAgent(provider=…)cendor-core
Survive crashes, retries, long runsRetryPolicy, checkpoint=— (SDK)

The bottom two rows are the SDK’s own layer — orchestration and hardening — the only behaviours not delegated to a library.

How it works

One governed turn, top to bottom — governance fires at core’s seam, not inside your code, and every touchpoint is a library on the bus:

%%{init: {"flowchart": {"htmlLabels": false}} }%%
graph TD
    IN["run(agent, input)"]
    ASM["assemble context<br/>(contextkit; squeeze compresses oversized blocks)"]
    PRE["pre-flight<br/>(tokenguard budget · guardrails input gate · acttrace guard redact)"]
    CALL["the model call<br/>core.instrument() → the bus, inside trace(run_id)"]
    TOOLS{"tool calls<br/>requested?"}
    EXEC["execute tools<br/>(guardrails gate tool_call + tool_output)"]
    OUTG["output gate<br/>(guardrails)"]
    AUD["record + audit<br/>(cassette records · acttrace chains the evidence)"]
    DONE["Result<br/>output · steps · usage · cost"]

    IN --> ASM --> PRE --> CALL --> TOOLS
    TOOLS -->|yes| EXEC --> ASM
    TOOLS -->|"no (or max_turns)"| OUTG --> AUD --> DONE

    classDef seam fill:#2563EB,color:#ffffff,stroke:#1E40AF;
    classDef stop fill:#F43F5E,color:#ffffff,stroke:#E11D48;
    class CALL seam;
    class PRE stop;

Because every model and tool call flows through the same bus under one trace_id, each library sees the others’ work for free — acttrace’s audit log already knows the context decisions, the cost, the tool calls, and the guardrail verdicts, without importing contextkit, tokenguard, or guardrails.

Honest limits

  • The SDK owns orchestration, not governance. The loop, handoff, and hardening are the SDK’s; everything else is a library you can also use standalone. If a claim is about cost, safety, or evidence, the library docs — and the parity matrix — are the source of truth.
  • squeeze engages only when installed. With no squeeze present, evict="compress" truncates instead of compressing — no error, just a coarser result.
  • Evidence, not compliance. acttrace produces evidence to support a compliance case; it doesn’t make one, and the SDK inherits that limit unchanged.
  • Parity is documented, not version-coupled. Where the TypeScript SDK trails Python (e.g. reask_on_output_trip, stream_check_window), the pages say so and the parity matrix is authoritative.