Skip to main content

Platform updates — Semantic Judge guardrails, and a real fix for guardrails on LangChain and Google ADK agents

Released: September 2026
  • Semantic Judge guardrails can be created again — the custom-prompt LLM judge guardrail type was previously undiscoverable from Add guardrail: it had no template chip and no way to select it from the creation page. It’s no longer deprecated, has its own template chip, and its own configuration UI (judge prompt, pass-threshold slider) — see Guardrails → Creating a guardrail
  • Semantic Judge checks are now metered by plan, with a BYOK path once you exceed it — these checks call a model on every run, so they’re capped at a monthly quota that scales with plan (Solo 100, Pro 1,000, Team 5,000, Scale unlimited). The Guardrails page shows a live usage chip, turning amber then red as you approach the limit, with a direct link to connect your own LLM. Once connected, checks run on your connection instead — unmetered — see Guardrails → AI usage limits and LLM Connections (BYOK)
  • Guardrails now actually block on LangChain and Google ADK agents, not just direct SDK callsguardrails: true on a LangChain chat model or a Google ADK agent previously ran the check and logged the result, but a block verdict didn’t stop the flagged content from reaching your application: LangChain’s internal callback manager silently swallows errors thrown from trace handlers, and ADK’s callback-based integration had no guardrail check wired in at all. Both are fixed at the SDK level — no guardrail configuration changes needed, just upgrade @zespan/sdk
  • A guardrail’s own enable/disable toggle could silently corrupt its configuration — a partial update to a guardrail (including the dashboard’s own Enable/Disable action) could reset its phase, config, action, priority, or agent scoping back to their original defaults, discarding anything you’d customized since. Fixed; partial updates now change only the fields you actually sent

Platform updates — AI suggestions now show what they are based on

Released: September 2026
  • Evaluators no longer report failures when no provider is connected — a project without an LLM connection was recording a failing score for every evaluator on every trace, which also produced issues and regression datasets that were never real. Nothing is scored until a provider is connected, and the evaluations page now says so. It also names evaluators that are not running because the project has no connection for the provider they score with — for example, a project connected only to OpenAI whose evaluators use a different default provider — and no longer shows “ready to run” while any evaluator is blocked. Existing scores recorded this way are not removed automatically; contact support if you want them cleared
  • Every AI suggestion is checked against your data before you see it — suggestions on Issues, Incidents, Costs and the Overview used to be written straight from a model’s answer. Each statement is now checked against the actual record it refers to, and any statement that does not hold up is not shown. Expect suggestions to be shorter and more specific than before: text that was previously confident but unsupported no longer appears
  • Each suggestion shows the record it rests on — the trace, issue, prompt, policy or metric behind a statement is shown with it and links through where the record has a page, so you can check a suggestion rather than take it on trust
  • A surface with nothing to say now says so — where nothing could be checked against your data you will see “No suggestion: nothing here could be checked against your data” instead of a plausible-sounding guess. This is deliberate: a suggestion you cannot verify is worse than none
  • A suggestion that was cut short is labelled — if a run stops early it is shown as incomplete alongside whatever was verified, so a partial answer is never presented as a finished one
  • Evaluator explanations must quote the text they judged — an evaluator’s written reasoning is checked against the content it scored. Where the quoted text cannot be found, the score is kept and the explanation is withheld rather than shown unverified
  • Guardrails now start on the Solo plan — creating, editing, enabling, disabling and deleting a guardrail, and installing one from a template, previously required Pro. They are available from Solo up, and everything you had already built is unchanged — see Guardrails → Plan requirement
  • Policy-as-Code is enforced consistently everywhere — Policies remains a Pro feature, and that requirement now applies to the API and CLI as well as the dashboard. If you author or apply policies through zespan policy apply or the API on a Free or Solo plan, those calls now return a plan error instead of succeeding. Reading your existing policies is unaffected on every plan — see Policies
  • New organizations start on a 14-day Team trial — every feature and limit on the Team plan, no credit card, nothing charged when it ends. You’ll be reminded 7 days, 2 days, and 1 day before it ends, plus a final email on the day it ends. If you choose Solo or Pro, features that need Team become unavailable until you upgrade again; Team or Scale keeps everything. If you don’t choose a plan, your organization moves to Free — ingestion pauses at the Free limit and Guardrails, Policies, and Evaluations become unavailable, but your projects, members, and existing data are kept and come straight back if you upgrade later. See Your 14-day trial

Platform updates — Zespan now finds failures that never errored

Released: September 2026
  • Issue clustering now sees silent failures — until now an Issue could only form from a trace where a span actually threw. Clustering now also considers traces that failed without erroring: an evaluator scored the output below its threshold (or above it, for evaluators like toxicity where high is bad), an evaluator returned a non-passing verdict outright, a tool call was retried or ran far slower than its siblings, or a behavioural signal fired. This is the class of failure where nothing throws — the agent returns a confident wrong answer and no log line says so — see Issues → What counts as a candidate
  • New behavioural signals: user_retry and agent_loopuser_retry means the same question was asked more than once in one session; agent_loop means one tool was called three or more times inside a single trace. Both are evidence, not verdicts: a person may rephrase for their own reasons and some agents legitimately call a tool repeatedly, so trace detail labels them Evidence, renders them neutrally rather than as failures, and shows no pass/fail threshold — there is no rubric they were scored against — see Traces → Verdicts and evidence
  • The Issues list gained a Cause column, replacing Error code — every issue now names what its traces share: an error code where one exists, otherwise the signal that identified them. It is never blank; an issue with neither reads No cause recorded. Two issues on the same operation with different causes stay separate rather than collapsing into one row — see Issues → Signals
  • Issue clustering keys changed — the grouping key gained a signal component, so it is now (verdict level, primary operation, error code, signal). Existing issues are unaffected; they carry an empty signal and cluster exactly as before

Platform updates — Pricing correction and a new fix-verification gate state

Released: September 2026
  • Billing page corrected to match the enforced plan catalog — this page previously stated Free at 10K traces/month, Solo at 39/mo,andSoloat1member;theproducthasenforced50K,39/mo, and Solo at 1 member; the product has enforced 50K, 29/mo, and 2 members respectively for a while, so those three numbers were simply wrong here. Fixed — see Billing → Plans
  • New gate_unverified run status on Closed-Loop Fix Proposal — a fix-verification run whose score compared favourably but whose replay wasn’t faithful (a tool call’s result went uncaptured, or the project’s retention window had already aged the incident’s tool-call data out) previously reused the gate_failed status, which read as a contradictory “gate failed” headline sitting directly above a “Gate: unverified” line. It now has its own status, still withholding approval exactly like an outright failure — see Incidents → Propose Fix

Platform updates — Superadmin operator actions documented

Released: September 2026
  • Documented what Zespan staff can do to your account — ban/unban (with a required reason and optional expiry), password reset, session revocation, impersonation, and organization suspension have been live in the internal superadmin console but were never written down. Impersonation is time-limited to 30 minutes, fully audited, and shows a persistent banner for as long as it’s active; organization suspension blocks dashboard and API access but never stops SDK ingest — telemetry keeps flowing, so nothing is lost while an account is suspended. Every one of these actions is written to an internal audit log — see Security → Staff access to your account
  • Saving a customised Overview layout no longer fails — the Model Risk widget was offered in the Overview Customise panel (and included in the Leadership, Engineering and Ops starting layouts) but the API did not recognise it, so saving any layout containing it returned Invalid widget ID. Because Model Risk is on by default, this affected the first save for every project. The widget is now accepted, the API’s advertised default layout matches the one the page actually renders, and a rejected save now names the widget it did not recognise instead of failing anonymously — see Overview
  • Locally-evaluated policies now honour appliesTo.models and exemptions — a policy scoped to specific models, or carrying an exemption, was enforced correctly by the server but reached the SDK with that scope stripped: the bundle GET /policy-bundle serves is rebuilt from the deployed guardrail rows, and the rebuild carried appliesTo.agents and nothing else, so the SDK recomputed model scope and exemptions as empty. An SDK evaluating locally therefore blocked requests the server would have allowed — a call on an out-of-scope model, a call reporting no model at all against a model-scoped rule, and any call from an exempted agent. The rebuild now carries every scoping field the row holds, so local and server decisions agree; exemption expiry is still judged at check time, so a lapsed exemption stops suppressing the control on the next call rather than at the next bundle refresh — see Local evaluation

Platform updates — Guardrails page redesign

Released: August 2026
  • Guardrails page redesign — the page’s ten stacked blocks (a 5-card posture hero, a 5-card metrics row, three phase-headed rule grids, and a 10-template “Quick install” grid) are now three: a single status band (posture headline, coverage-gap chips linking straight to adding a guardrail, and a stat line — checks, blocked, redacted, avg check, block rate — with a block-rate sparkline), a Rules table (Rule, Type, Phase, Action, Checks, Blocks, P90, sorted phase then priority, each row with a menu for Configure/Enable-Disable/Delete), and two tabs, Rules and Activity (the old Templates tab is gone). Adding a guardrail now always starts from Add guardrail, which opens /guardrails/new — the pipeline diagram that used to sit on the landing page now lives on the individual rule’s detail page, highlighting the rule you’re viewing, and the DB-backed saved presets that used to live in the Templates tab now sit on the /guardrails/new page, below the built-in templates — see Guardrails
  • Guardrails now available on the Pro plan — every guardrail action (create, enable, disable, delete, install from a template) previously returned a 403 requiring the Scale plan even though the page itself rendered for Pro customers; the API now matches the Pro gate the page always advertised — see Guardrails → Plan requirement

SDK updates — August 2026

Released: August 2026 · TypeScript (@zespan/sdk) and Python (zespan) New:
  • CoherewrapCohere() (TypeScript) / patch_cohere() (Python) traces the Cohere v2 chat API, including streaming (chatStream() / chat_stream()), tool calls, and cost from usage.billed_units — see Cohere
  • VectraZespanVectraCallbackHandler (both SDKs) implements vectra-js/vectra-py’s duck-typed callback interface directly, tracing ingestion, retrieval, reranking, and generation with no changes to how you call Vectra — see Vectra
  • Documented sending traces from any OTel-instrumented app with no Zespan SDK — the OTLP ingest endpoint (/v1/traces) has accepted standard OpenTelemetry GenAI-semconv traces for a while, but it was never actually documented; frameworks already instrumented with OpenLLMetry, OpenInference, or their own native OTel exporter can now be pointed at Zespan directly — see Send traces from any OTel-instrumented app
  • 9 new per-framework/gateway OTel integration pagesOllama, DSPy, OpenAI Agents SDK, Strands Agents, Mastra, Agno, and a new Gateways category covering LiteLLM Proxy, Kong AI Gateway, and Cloudflare AI Gateway — each showing that specific framework/gateway’s own OTel auto-instrumentation pointed at Zespan, no Zespan SDK wrapper involved. No-code platforms (n8n, Dify, Langflow) intentionally excluded — none has official OTel support anywhere, only unofficial community patches
Platform:
  • Model detail — a per-model drill-down page — clicking a row on the Models page now opens that model’s own dashboard: daily volume/cost/latency/cache charts, a cost-vs-quality scatter against every other model called in the project with a Cheaper alternatives table (models that cost less per call and score no worse on quality — the direct candidates for “switch this workload here”), reliability (error rate over time, top error codes), a quality tab (average score, pass rate, per-evaluator breakdown, daily trend), and usage broken down by agent and by operation. Quality is computed directly from evaluation_scores grouped by model, matching the pattern already used elsewhere rather than the span-level join the Cost vs Quality page needs. A model with zero calls in the selected range returns an empty found: false shell (200, not 404) instead of erroring — it may simply be unused in that window. Model ids containing a dot or slash round-trip correctly through the page’s URL. Backed by GET /v1/projects/:id/models/:model — see Models → Model detail
  • Policies — author, review, backtest, and apply guardrail policies from the dashboard — a new Policies section (Pro plan, same gate as Guardrails) authors policies as versioned documents instead of files in a repository, and applies them through the identical parseAndValidatecomputePlanapplyPolicies pipeline zespan policy apply uses, so a dashboard-authored policy reaches the SDK bundle exactly like a git-authored one. Every guardrail row now carries a three-way sourceKind (git / zespan / dashboard) rather than a boolean, so the dashboard and CI can each prune only the rows they own — a plan computed from either side reports the other’s rows as a foreign_origin conflict rather than adopting or deleting them, and the dashboard has no --force equivalent: taking a policy from the other writer is always the existing, audited Detach action. Policy documents are versioned: an edit creates a new, immutable revision, and exactly one revision is ever applied per document, which is what makes “is this in sync” answerable by reading one row. The inventory shows every policy in the selected environment with its owner, posture, rule count, test status, and sync status; review renders the same plan the CLI’s zespan policy plan prints, with a dialog per refusal (ENVIRONMENT_NOT_ALLOWED, STALE_PLAN, CONFLICTS, REMOVAL_NOT_ALLOWED, ADOPTION_NOT_ALLOWED, UNTESTED_DENY) naming what happened and offering the action that resolves it — UNTESTED_DENY offers Run backtest now as the primary action ahead of the override, matching the CLI’s deliberate speed-bump-not-a-wall design. policy:author is a new, separate grant from policy:apply — editor gains drafting rights but, as before, not deploy rights. Backed by GET/POST /projects/:id/policy-docs and its /validate, /:docId/revisions, /:docId/archive, /plan, /test, and /apply children, plus GET /projects/:id/policy-applies and /:applyId/restore — see Policies, Policy as code, and zespan policy
  • zespan auth login — the CLI signs in as a person, not as a project key — control-plane commands check your role in the organization, which a project API key cannot supply, so the CLI gained a real sign-in: an OAuth 2.0 device authorization grant (RFC 8628) that prints a short code, opens the dashboard’s new /device approval screen, and receives a credential once you approve. An account that has two-factor authentication enabled must re-prove it to approve, enforced on the server rather than only in the page — so a request that skips the screen entirely is refused too; an account with no second factor confirms and approves instead, because requiring enrolment only here would have been stricter than the dashboard, where the same person can already apply enforcement policy without one. Because a device grant is phishable by construction (someone can start a login on their own machine and read you the code), the approval screen shows what the server observed about the terminal that asked — operating system, CLI version, and the IP the request arrived from — reported by the server, never by the link you followed, so a phoned-in code is recognisable as not-your-terminal. Each machine mints a persistent keypair, so signing in again from the same machine keeps the same device identity instead of registering a new one every time, and zespan auth logout keeps the key. Also new: zespan link, which picks an org and project interactively and writes a committable, secret-free .zespan/config.json carrying readable names beside the id (a project has no slug, so a bare UUID tells a human nothing), and zespan projects list, which prints the same data without prompting for CI. Credentials live in ~/.zespan/credentials.json at 0600, re-tightened on every write, and are refused if sent to a host they were not minted against. Breaking: zespan policy pull, test, plan and apply now require a sign-in and refuse an API key outright — they always failed with a 403 server-side, so the practical change is a clear error naming zespan auth login instead of a misleading “you may not be a member of that org”, but any CI recipe passing ZESPAN_API_KEY to those commands was never working and needs rethinking (zespan policy validate is offline and unaffected). Ingest and the data plane are unchanged/v1/ingest, guardrail checks and zespan doctor still use the API key. Policy applies are now genuinely attributed: the audit trail records the real user, session and device instead of the string "unknown", and a mutation the server cannot attribute to a user is refused rather than recorded against a placeholder. Self-hosters: the API builds the approval URL from NEXT_PUBLIC_APP_URL, which must point at the dashboard origin — see zespan auth, zespan link, zespan policy and CLI overview
  • The audit log names the real actor, not just a user — every audit entry now carries an explicit actor type (user, apiKey, or system) alongside its id, instead of every row implicitly meaning “a user.” An SDK request authenticated with a project API key is attributed to that project, not to whoever created the key; a scheduled job, timer-driven expiry, or other action with no human or key in the loop is attributed to system rather than borrowed from whoever happened to trigger it. GET /v1/orgs/:orgId/audit (and the project-scoped equivalent) render this as an actor object ({ type, id, name, deleted }) and accept a new actorId filter alongside the existing userId filter. The CSV export’s actor column now carries the real actor’s id instead of always a user id, and gains an appended actor_type column so a script parsing the file by column position keeps working — see Audit log
  • New page: Client IP behind a proxy — a deployment constraint self-hosters have to meet, written down rather than assumed. Zespan derives a caller’s address from the proxy chain, so your load balancer, reverse proxy or CDN must overwrite X-Forwarded-For rather than append to whatever the client sent, and the API must not be reachable around it. The page gives the directive per edge (nginx, Caddy, HAProxy, Traefik, Envoy, ingress-nginx, ALB, Cloudflare), a one-request probe to verify it rather than trusting the config, and — the part that matters — what a forgeable address actually costs: the CLI approval screen shows the IP the sign-in came from as corroboration for a human eye against a code read out over the phone, and a chosen address makes that row read as the victim’s own office. Nothing else in the flow leans on it: ownership, the claim, the second factor, revocation and attribution are all checked server-side. Also new on that path: starting a login is rate-limited to 30 codes per 5 minutes per address (polling for the result deliberately is not — one login polls about 120 times), and abandoned grants are swept so nothing outlives roughly 40 minutes. Both need infrastructure that is easy to omit — the rate limit needs REDIS_URL, and the sweep runs only in a worker with no WORKER_TYPE set. Correction: approval requires a second factor from accounts that have one, not from every account — an earlier note here said otherwise
  • Exemptions, appliesTo.environments and appliesTo.models are now enforced — all three were validated and documented but had no effect on anything: a policy could declare an exemption, pass validate, apply cleanly, write an audit row naming it, and still block the exempted agent. Exemptions now suppress the policy’s rules for the agents they name (case-insensitively; an exemption naming no agents covers every agent in the policy’s scope), and a suppressed rule writes no guardrail event rather than one recorded as allowed, which would read as “the control ran and found nothing”. expiresAt is checked on every request, not at apply time — the day named is itself still exempt, compared as a UTC date so the answer does not depend on the server’s timezone, and from the day after the control enforces again on its own with no redeploy. appliesTo.models now scopes a policy per request: a policy scoped to ["gpt-4*"] does not fire for claude-opus-4, and a request reporting no model at all is out of scope rather than blocked, matching the convention model_governance already set. appliesTo.environments is a guard rather than a router — apply refuses (ENVIRONMENT_NOT_ALLOWED, naming every offending policy, checked before every other refusal) to write a policy into an environment the policy does not list, and never expands one apply into several, because routing silently to environments nobody named on the command line is how a policy reaches production unintentionally. The guard compares against the resolved environment, so --env production still satisfies a policy listing prod. Renewing an exemption or changing appliesTo.models is a real change, so zespan policy plan reports it as an update instead of a silent no-op — see file reference, Policy as code and zespan policy
  • Locally-evaluated policy decisions now reach the dashboard — a request blocked in-process by PolicyBundleStore never reached the server, so a policy evaluated locally showed zero hits while blocking continuously; the faster local evaluation was adopted, the blinder the dashboard became. The SDK now batches its local verdicts and reports them to a new POST /v1/policy-events (API-key authenticated, answering 202 rather than 200 because the write is fire-and-forget), landing them as ordinary guardrail events marked evaluated_locally so a silently broken SDK never looks like a quiet policy. Reporting cannot hurt the host process: the queue is bounded and drops rather than buffering under backpressure, a failed or non-2xx send is counted as dropped and never requeued, and nothing on the path throws or adds latency to evaluate(). Drops and sends are exposed as reportsDropped / reportsSent on getStatus(). The drain piggybacks on refresh(), with flushReports() as the escape hatch for a short-lived process that will not refresh again and would otherwise exit with events still queued. Every report carries the rule’s label, action, policy id and enforcement mode — never the matched excerpt; the trace already holds the content under your existing redaction. guardrail_events gained policy_id, enforcement and evaluated_locally, so per-policy analytics work and a dryrun-suppressed block stays distinguishable from a rule genuinely authored as warn — see Local evaluation
  • Policy testing and the deny gatezespan policy test --against issues|last:7d|dataset:<id> backtests a policy over your project’s own recorded traffic and reports what it would have caught against what it would have broken. The false-positive count is broken down per rule, not just per policy, so a single noisy rule in a stack is identifiable rather than forcing you to abandon the whole policy — and because Zespan holds the trace, each false positive comes with the actual conversation rather than an estimated aggregate rate. Prompts and completions are separate samples, so a pre-phase rule is never scored against text the model produced and never sees in production. Promoting a policy to enforcement: deny now requires either a test result or an explicit --untested; the result is keyed to the policy’s exact content, so editing a rule invalidates it. Deliberately not a hard block — a platform engineer mid-incident has to be able to ship — but the default path makes you look at the false positives first. Every report carries the note that a backtest only catches what is in the corpus and cannot catch attack patterns that have not happened yet, which is why warn against live traffic remains the second line of defence — see Policy testing
  • Local policy evaluation in the SDK — a new PolicyBundleStore downloads a compiled policy bundle from GET /v1/policy-bundle and evaluates the rules that are pure functions (regex, secret_egress, model_governance, topic_boundary) in-process, instead of every guardrail check being a network round-trip as it is today. Failure semantics follow OPA’s bundle model: fail closed until the first-ever bundle activation (answering “allowed” from an empty bundle is indistinguishable from having no guardrails), fail open on every disconnect after (a control-plane blip must not become a fleet-wide false deny), staleness exposed as four separate timestamps rather than one boolean so “could not reach the control plane” stays distinguishable from “reached it and got something unusable”, and no half-applied state — a bundle is swapped in whole or not at all, so revision N keeps serving while N+1 is unavailable. The bundle declares what it does not carry, so a policy using any server-evaluated type makes the SDK call out rather than answer from a partial picture; whether an unevaluable rule calls out or is skipped is taken per-policy from spec.failOpen. Because the SDK cannot import server code, its evaluators are a second implementation — a parity test runs identical inputs through both on every build and fails if they ever disagree — see Local evaluation
  • Policy as code: adoption, review screen, and three new primitiveszespan policy pull generates policy files from the guardrails a project already has, so moving an existing project to policy-as-code no longer means hand-writing a file per rule. Each generated file pins its rule to the existing guardrail, so the plan straight afterwards reports no changes to make — only pending adoptions — and zespan policy apply --adopt takes each guardrail over in place, keeping its id so execution history and metrics survive the handover. Generation is deliberately not lossless and says so at the time: failOpen is assumed, and a non-default maxLatencyMs, a template link, a disabled rule, or a slug the format cannot express each raise a warning (the last is skipped rather than written as a file that would fail validation). A new Review changes screen on the Guardrails page renders the same change set as the CLI from the same endpoint, and is read-only — reviewing needs policy:read, applying still needs policy:apply. Three new rule types: model_governance (allow/deny model globs, requireProvider, data-residency regions — a model whose provider cannot be inferred fails rather than being guessed, and an allowRegions rule with no reported region fails rather than passing unchecked), secret_egress (a maintained credential pattern set covering AWS, GitHub incl. fine-grained PATs, Slack, Stripe, OpenAI, Anthropic, Google, SendGrid, Twilio, npm, PEM keys, JWTs and bearer tokens, with disablePatterns/additionalPatterns escape hatches), and schema_contract (full JSON Schema validation of output, not just “is it JSON”). Rules may now carry an explicit slug, which is what makes adoption a no-op. Backed by GET /v1/projects/:id/policies/pull and an allowAdopt flag on apply — see Policy as code, file reference, and zespan policy
  • Policy as code — guardrail policies can now be authored as YAML files in your repository and applied with a plan / apply loop, via a new zespan policy CLI subcommand (init, validate, plan, diff, apply). validate runs fully offline — no API key, no network call — so it is safe as a pre-commit hook, and it reports every problem in every file at once rather than one per run. Ownership is explicit: a guardrail is owned either by a policy file or by the dashboard, apply only ever updates or removes rows it owns (so a first apply from a partial directory cannot delete dashboard-authored guardrails), and code-owned guardrails render read-only in the dashboard with a Code-managed badge and a Detach action that hands ownership back. apply refuses in three cases rather than surprising you, each naming the flag that resolves it: a stale plan, a policy detached in the UI, and any removal (which needs --allow-remove, because removing a guardrail removes a control). A new spec.enforcement ladder — dryrunwarndeny — moves a whole policy’s posture in one line and compiles down onto the existing engine with no change to how checks are evaluated. Comparison runs over a canonical form, so reformatting a file, reordering keys or adding comments is not a diff. Every apply records each file’s SHA-256 content hash to policy.applied in the audit log. policy:apply is granted to owner and admin only. Policy files parse as YAML 1.2 core (so on/no stay strings), with anchors, aliases, merge keys, tags and multi-document files rejected by name and line. Backed by GET /v1/projects/:id/policies, POST .../policies/validate, .../plan, .../apply and .../:policyId/detach. Not in this release: policy pull (generating files from guardrails you already created in the dashboard), a dashboard review-changes screen, and policy testing against recorded traffic — see Policy as code, file reference, and zespan policy
  • zespan doctor — a new subcommand on the @zespan/cli binary (zespan doctor, alongside the existing zespan gate / zespan-gate) diagnoses SDK setup problems in one command: API key validity, API reachability, whether spans are actually arriving (GET /v1/projects/:id/ingest-health), your @zespan/sdk version against latest, PII redaction posture, and a heuristic scan for the most common integration mistake — a provider client (OpenAI, Anthropic, Mistral, Groq, Cohere, GoogleGenAI) constructed before zespan.init() runs, so it never gets patched. Configure via flags, ZESPAN_* environment variables, or a committed .zespan.yaml — neither the API key nor the API base URL is ever read from that file, so a committed config can’t redirect where your key gets sent. doctor also flags a --project/ZESPAN_PROJECT_ID that doesn’t match the project your API key belongs to, and doctor --help prints usage without making any network calls. --json emits { results, summary } for CI. A new GET /v1/sdk/whoami endpoint backs it, resolving project identity from an API key alone — see zespan doctor and CLI overview
  • Environments — every project now has real environment records (seeded dev/staging/prod, plus any custom slugs you add) instead of just a free-text tag: manage them from Settings → Environments, switch between them from a new project-header switcher that scopes Traces, Evaluations, Guardrails, Incidents, and metrics via a shared environment query parameter, and delete is blocked with a 409 naming exactly what’s still attached rather than orphaning guardrails, alerts, or incidents. Historical free-text values (production, development) alias onto prod/dev on both read and ingest; an inbound value that matches neither a slug nor an alias is never dropped, only flagged — see Environments
  • Changes — a new project-wide timeline page merges prompt deploys, agent lifecycle transitions, guardrail/evaluator/alert edits, and deploys reported from your own CI pipeline into a single chronological feed, filterable by time range and change kind. Report your own pipeline deploys with a new POST /v1/projects/:id/changes endpoint so they show up alongside everything else — see Changes
  • Changes around this incident — an incident’s detail page now shows a before/after split of every prompt deploy, agent lifecycle transition, and policy/evaluator/alert edit in the six hours on either side of when the incident started, each side labeled with its count so the split is legible at a glance. A partial-results banner appears if any underlying source didn’t respond in time — see Changes around this incident
  • Blast Radius — a dependency graph across prompts, agents, models, guardrail policies, evaluators, and alerts, distinguishing declared (configured) from observed (actually-called, from real trace data) edges. Surfaces as an advisory impact summary in the prompt release-confirmation dialog and as a delete-blocking check on evaluators — deleting an evaluator with dependents now requires explicit acknowledgement instead of silently breaking whatever depended on it. No standalone page or graph visualization yet; both are on the roadmap. GET /v1/projects/:id/blast-radius and GET /v1/projects/:id/graph back it — see Blast Radius
  • Outcome attribution — a new Value dashboard page shows which agents and models actually drive business results. Report a business outcome (a deflected ticket, an avoided refund, an SLA met) against any trace with zespan.getClient().outcome(...) (TypeScript) or POST /v1/ingest/outcomes directly (Python, or any backend) — reporting is designed to happen out-of-band, minutes or hours after the trace that produced the outcome has finished. Outcomes are summarized per-agent and per-model against real trace cost: total outcomes, success rate, attributed value, cost, cost-per-success, and value-per-dollar. Re-reporting the same outcome corrects it rather than double-counting it. GET /v1/projects/:id/outcomes/summary and GET /v1/projects/:id/outcomes/kinds back it — see Value
  • Compliance Evidence Packs — a new Compliance dashboard page generates audit-ready evidence documents from data Zespan already recorded: a per-agent Compliance Card, or a SOC 2 control evidence report mapped to CC6.1 (logical access), CC7.2 (monitoring), and CC8.1 (change management). Every document is hash-addressed (SHA-256 over the exact stored bytes) and re-verifiable — a new GET /v1/projects/:id/evidence-packs/:packId/verify re-hashes the stored document and re-resolves every cited record against live data, honestly separating what it actually re-checked (checkedRefs/evidenceStillPresent) from what it structurally can’t (unverifiableRefs, e.g. ClickHouse aggregates). A coverage preview shows which controls have evidence for your chosen period before you generate anything, and every document carries a disclaimer stating it reports observed evidence, not a certification of compliance. SOC 2 is the only framework mapped today — EU AI Act and ISO/IEC 42001 aren’t yet — and the SOC 2 mapping itself is reviewed internally, not yet by a licensed external auditor, which the document says on its face. Output is JSON or a printable HTML document; PDF export isn’t available yet (print-to-PDF from your browser is the interim path), and there’s no recurring/scheduled generation or email delivery in this release — generation is on-demand only, from the dashboard or POST /v1/projects/:id/evidence-packs — see Evidence Packs
  • Model Lifecycle — a daily scan matches the models you actually call against a curated, bundled catalogue of provider-announced deprecation and retirement dates, and raises a finding — call volume, cost, affected agents and prompts, all measured from your own traffic over the trailing 30 days — for any model retiring within 90 days that you’re still calling. Findings surface on a new Overview widget, a new Lifecycle column on the Models page, and a dedicated Model Lifecycle findings page. Dismissing a finding suppresses it at its current urgency band (90/30/7/0 days remaining) and it re-raises exactly once when the deadline tightens into a nearer band, never when a feed correction pushes it further out. Delivery reuses the existing alert-rule/webhook machinery under a new model-lifecycle rule type — a project needs an enabled rule of that type to get notified; the finding is recorded and visible either way. There is deliberately no fabricated quality delta or regression comparison against the named successor model — the only automated comparison is a measured cost-per-call difference on your own organic traffic, and the UI states outright that Zespan does not run your evaluations against the successor for you. Drift detection (catching a pinned model’s behavior silently changing over time, independent of any provider announcement) is not part of this release — see Model Lifecycle, Models, Alerts, and the feed reference
  • Organisation view for Policies — every project’s policy inventory, rolled up — a new Organisation view on the top-level Policies entry in the org admin sidebar answers “is this policy deployed everywhere it should be?” across the whole org instead of one project at a time: four rollup cards (policies, projects covered, occurrences at deny, untested-or-stale), grouped by policy id with a posture/owner/attention breakdown per group, and a Recent applies feed across every project. The Environment scope selector defaults to all environments, not production — Production and any single environment are explicit narrowings. Coverage renders as “present in 9 of 12 projects” and stays a fact, never a verdict: no standard yet exists in Zespan for a project to be measured against, so nothing here calls a project non-compliant or failing. Apply recency is bounded to a trailing 90-day window and the most recent 2,000 applies org-wide, reported back as applyRecencyTruncated/applyRecencyCap — a null last-applied time means no apply landed in that window, not that the policy was never applied. Backed by two new read endpoints, GET /v1/orgs/{orgId}/policies and GET /v1/orgs/{orgId}/policy-applies, both gated on policy:read — see Policies → Organisation view
Bug fixes:
  • ZespanPilot now asks before every write, not just high-risk ones — sixteen actions that change state were executing silently: snoozing or modifying an alert, acknowledging, updating, resolving or writing a postmortem on an incident, adding a guardrail keyword, running or scheduling an evaluation, creating a budget alert, setting a budget, updating project settings, inviting a teammate, setting an SDK concurrency limit, and the Setup Monitoring and Harden Project composites. They were rated low or medium risk, and only high and critical risk triggered a confirmation card. Every one of them now shows the card before it runs. Nothing that previously asked for confirmation stopped asking; the read actions (listing, diffing, and root-cause analysis) are unchanged and still run straight away — see Safety gates
  • Model Lifecycle deprecation notifications can now actually be turned on — a small settings card at the top of the Model Lifecycle page lets you opt in to email and/or webhook notifications for new or re-raised findings. Previously there was no way, in the dashboard or the API, to create the model-lifecycle alert rule the delivery worker looks for, so notifications never fired for any project even though findings were always fully recorded and visible — see Model Lifecycle → Getting notified
  • The OTel ingest endpoint now reads the real GenAI semantic-convention model attributes (gen_ai.request.model / gen_ai.response.model) — it previously only checked a non-standard gen_ai.model, so any spec-compliant instrumentation (OpenLLMetry, OpenInference, and most framework-native OTel exporters) sending traces straight to /v1/traces showed every span as model: unknown — see Send traces from any OTel-instrumented app
  • OpenRouter used via the openai client is no longer mislabeled openaiautopatch() in both SDKs now inspects the client’s base_url and correctly tags these calls provider: "openrouter", fixing provider-based cost and model lookups for anyone using OpenRouter through an OpenAI-compatible client instead of a dedicated OpenRouter SDK
  • wrapBedrock, wrapMistral, wrapGroq, wrapLiteLLM are now reachable via the zespan convenience object (TypeScript) — zespan.wrapMistral(...)-style usage shown on those integrations’ docs pages previously threw at runtime since only wrapOpenAI/wrapAnthropic/wrapGoogle/wrapGoogleGenAI/wrapOpenRouter were actually attached to it

Platform updates — Simulations CI parity

Released: August 2026
  • Closed-loop fix proposal — on an incident with a single identified causal prompt deploy, a new Propose Fix button generates a fix candidate with the AI Prompt Enhancer grounded in that incident’s own captured failing traces, tests it head-to-head against the current production prompt — both replays scored by your project’s own LLM-judge evaluator — using the same Simulations quality gate, and — only if the candidate beats production — routes it through ZespanPilot’s approval queue, showing the gate’s score deltas and regressions alongside the request. Approving lets the reviewer promote the fix to staging or production; rejecting leaves the draft untouched. The draft is created with no labels at all, so it is never reachable by a latest prompt lookup before approval. Only a higher-is-better judge is used for the comparison — a lower-is-better rubric like toxicity would invert the gate — falling back to a provisioned reference-free quality judge otherwise. Starting a run requires both incidents:manage and prompts:manage (owner/admin), plus at least one other owner/admin in the organization, since nobody can approve their own fix candidate. Nothing is ever auto-deployed — a human approval is always required — see Propose Fix
  • Convert to Scenario — a Convert to Scenario button on a trace’s detail page and a session’s detail page turns that one trace or conversation into a regression test in one click: a dedicated one-item dataset named after the source (with a timestamp), and a new scenario pointed at it with Reference Match set to Fuzzy match by default. A trace converts to a Prompt Target scenario, a session (inherently multi-turn) to a Conversation (multi-turn) scenario. It doesn’t auto-run — you land on the Simulations page with a success toast. The new scenario’s provider/model default to the project’s baseline rather than the original trace’s, with the original noted in the scenario’s description — see Convert to Scenario
  • Adaptive adversarial persona presets — the Persona section of a Conversation (multi-turn) scenario now offers four built-in presets (Prompt Injection Attacker, Escalating Angry Customer, Social Engineer, Confused Novice) that populate the existing Name/Goal/Tone/Expertise fields — still editable afterward — plus a tactic-ladder instruction that pushes the simulated user to vary its approach across turns. This works on every run with no evaluator required; when a real judge rubric is also attached, each turn’s judge verdict is additionally prepended to the target’s reply before the next turn, reinforcing the adaptive signal for free — see Adversarial persona presets
  • Real LLM-judge scoring for simulation runs — a simulation scenario’s attached evaluator now scores with a real judge call when its template has a system prompt: one call for a single-turn (Prompt/HTTP) target, or for a Conversation (multi-turn) target, one call per turn plus a final holistic call judging the whole transcript, with each turn’s judge cost and latency shown in the Conversation Transcript. An evaluator with no system prompt (e.g. one created via the quick New Evaluator dialog) still falls back to the existing metric-key/assertion scoring — no judge call, no LLM cost — see Creating a scenario
  • CI gate for simulation batch runsPOST /projects/{id}/simulations/batch-runs/{batchRunId}/gate gates a candidate batch run against an explicit baseline batch run, mirroring the prompt-version quality gate: three threshold signals (avg score drop, regressed items, pass-rate drop, all with defaults), a 202 { pending: true } response while the candidate is still running, and a pass/fail verdict persisted into the batch run’s experimentMetadata.gateResult — see CI gate for batch runs
  • referenceMatch scenario evaluation option — a scenario’s evaluation config can now request exact or fuzzy (≥80% similarity) string comparison against the dataset item’s expectedOutput, surfaced as a reference_match entry in the run’s assertion results and automatically skipped when the dataset item has no expectedOutput — see Creating a scenario
  • Gate Check UI — the CI gate for batch runs is now also runnable from the dashboard, not just the API/CI: a Gate Check card on a completed batch run’s detail page picks a baseline (defaulting to the most recent completed batch run on the same dataset), exposes the three threshold overrides behind a collapsed Advanced thresholds section, and shows the pass/fail verdict, exit reason, and a per-item regression table — persisted so it’s shown automatically on reload, with a Re-run option — see CI gate for batch runs
  • Scenario editing — scenarios can now be edited after creation from an Edit button on the Scenarios list, which reopens the same form pre-filled with the scenario’s current config — see Creating a scenario
  • HTTP Target headers & timeout — a scenario’s HTTP Target now supports custom request headers and a configurable request timeout (defaults to 30000ms) — see Creating a scenario
  • Conversation stop conditions — a Conversation (multi-turn) scenario can now also stop early on a regex match against the agent’s reply, or on its attached evaluator’s pass/fail verdict, alongside the existing max-turns limit — see Creating a scenario
  • Evaluator score direction — set whether a higher or lower judge score counts as “better” for an LLM-judge evaluator via the new Direction button on its row (Evaluators tab); defaults to higher-is-better, with lower-is-better for metrics like toxicity or hallucination rate — see Score direction

Platform updates — Evaluation trust & CI gating

Released: July 2026
  • Evaluations page redesign — the Evaluators tab now shows a color-coded type badge (LLM JUDGE/CLASSIFIER/METRIC CHECK/PATTERN DETECT) and a pass/warn/fail score-distribution bar per evaluator, backed by real per-score counts rather than the metric-level average used elsewhere on the page; the Trends density view’s sparkline now reflects the selected time window instead of a fixed lookback. The Runs tab’s Score and new Samples columns now show each run’s actual result instead of a placeholder, and its status badge gained an icon. The LLM-connection banner also shows a “connected” state naming the active provider once one is configured — see Reading evaluation results and Evaluation runs
  • Vector-DB tracing for Pinecone, Chroma, Weaviate, and Qdrant — calling a vector-DB client directly (no LangChain/LlamaIndex retriever in between) is now auto-traced by zespan.autopatch(), the same one-liner that already covers LLM providers: reads (query/search/near_vector) emit a retriever span with rag_contexts, writes (upsert/add/insert) emit an embedding span with metadata.vector_count — no code changes to your DB client calls. A new recordVectorSearch()/record_vector_search() manual helper covers pgvector and other raw-SQL vector stores, which have no client library to auto-patch — see Pinecone, Chroma, Weaviate, Qdrant, and Manual spans
  • Vector-DB read spans now include metadata.top_k and metadata.filter — every retriever span from the auto-patched Pinecone/Chroma/Qdrant/Weaviate clients now also carries the requested result count (metadata.top_k — always recorded for Pinecone, whose top_k is required; recorded when the caller passes one for Chroma/Qdrant/Weaviate, whose equivalent option is optional) and the query’s filter/where clause (metadata.filter, redacted and stringified, only when storePrompts/store_prompts is on). Both fields are only observable nested under metadata — the ingest pipeline persists the metadata object verbatim but drops other unrecognized top-level fields — see each provider’s integration page for the exact source field per client
  • Corrected built-in evaluatorsLatency SLA, Cost Budget, Loop Detection, and Error Recovery Rate now compute a real deterministic check against the trace’s own latency_ms/cost_usd/operation/status data instead of silently falling back to a generic LLM quality-judge score — see Performance & agent evaluators
  • Categorical/boolean scores now render correctly — a categorical or boolean evaluator’s result shows as the judge’s chosen label, or true/false, everywhere it appears (trace detail, per-trace score chip, evaluator tables) instead of a meaningless percentage bar — see Reading evaluation results
  • Unified evaluation score sources — scores attached from the SDK via span.setEvalScore() now appear alongside server-side judge scores in the Evaluations dashboard’s KPI tiles and metric trend list, not only in the raw per-trace view — see Attaching scores from the SDK
  • POST /evaluation-runs now actually scores — creating an evaluation run through the API now enqueues real scoring and the run list shows its aggregated score, instead of the run sitting at pending forever
  • zespan-gate CLI — a new dependency-free @zespan/cli package wraps the prompt quality gate for CI: poll for a pending run, fail fast with a linking snippet if nothing’s scored yet, and exit non-zero on a failed or misconfigured gate — see CI quality gate
  • Evaluation & dataset MCP tools — the hosted Zespan MCP server now exposes datasets, evaluators, evaluation runs, and trace verdicts to any connected coding agent (Claude Desktop, Cursor, Claude Code), alongside the existing trace/metrics and prompt-management tools — see Zespan MCP server
  • Issue, session, retroactive-eval, and cost MCP tools — the hosted Zespan MCP server now covers 35 tools, adding open Issues (list, detail with sample traces, resolve/dismiss, and cached markdown remediation suggestions), sessions (recent-session list, full rollup + turn-by-turn transcript, session-level eval scores), retroactive evaluation runs (create, list, status), and cost attribution by agent/tool/model/user/operation. Write tools enforce the same role permissions and plan limits as the dashboard — a personal API key can only do what its owner’s role allows — and remediation stays advice-only: Zespan never opens a PR or touches your source. See Zespan MCP server
  • HTTP Targets & HTTP-endpoint dataset runs — register an externally-hosted agent endpoint you don’t control or can’t instrument with the SDK (a deployed Bedrock/Glean agent, or any HTTP-reachable chatbot API), then run a dataset against it directly from “Run over dataset” — Zespan POSTs your hydrated request template to the endpoint (with retry, SSRF protection, and W3C traceparent propagation) and records the response as a tagged trace, no LLM connection required — see HTTP Targets and Running against an HTTP endpoint
  • Wider SSRF blocking for HTTP Targets — the address-range check applied to a registered HTTP Target URL (and re-applied to the hydrated URL before every call) now also rejects RFC 6598 carrier-grade NAT space 100.64.0.0/10 — which includes Tailscale’s default range — plus multicast and reserved space from 224.0.0.0 up. The full list of rejected ranges is now documented; there is no override, so an agent reachable only over a private or Tailscale address has to be exposed publicly or instrumented with the SDK instead — see Security: URL validation
  • Retroactive evaluation trigger UI — score historical traces without waiting for online eval to have been configured, from a new Retroactive Runs panel on the Evaluations page: pick any evaluator, a time range, and optional operation/model filters, then watch per-run progress live — see Retroactive evaluation
  • Annotation Queues — a new dashboard section for routing a filtered set of traces to a human reviewer: filter by time range (and, via the API, operation/model/session/verdict), work through pending items with a trace input/output review panel, and submit a pass/fail verdict, optional label, score, and notes. A categorical (label-only) annotation always requires an explicit Pass/Fail choice — there’s no way to submit one without picking a verdict. Annotations are written into the same evaluation_scores data as automated judge scores, tagged human_annotated, so they appear in every existing trend and cost-quality view alongside judge scores — see Annotation Queues
  • Session-level evaluation — a new scope: "session" option on evaluator templates judges an entire conversation’s transcript instead of one trace, so multi-turn quality problems (contradictions, lost context, topic drift) can be caught even when no single call looks wrong. “Session complete” is detected by quiescence — 30 minutes of no new activity by default, checked on a 10-minute scan — since the SDK has no explicit “close this session” call. Results appear as a Conversation: Pass/Fail · N evaluators badge on the session detail page, kept visually distinct from per-trace scores — see Session-level evaluation
  • Session-level scores no longer skew the trace-level Evaluations views — a scope: "session" score judges a whole conversation and belongs on the session detail page, but it was also being counted by the Evaluations page’s metric trends, traces/spans-evaluated tiles, and Retrieval tab. Those four aggregations are now trace-scoped, so a session evaluator can’t drag a per-trace metric average down, add a phantom trace to the evaluated count, or rank session among your worst retrieval operations — see Session-level evaluation
  • Sessions time-range filter — the Sessions list now defaults to the last 7 days instead of scanning full project history on every load, with a date-range picker to widen or narrow the window — see Filtering sessions
  • Issue detail page, resolve action, and remediation suggestions — Issues now open into a detail page with their full sample-trace list; a new Resolve action sits alongside Dismiss with Sentry-style semantics (resolved = fixed, dismissed = stop showing me this pattern — either can reopen on a fresh occurrence); and a Generate suggestion button runs root-cause analysis across representative sample traces and asks ZespanPilot for a cached markdown remediation suggestion — likely cause and one concrete next step, never an automatic PR or repo change — see Issues
  • Multi-turn conversation scenarios are now fully functional — Simulations’ Conversation (multi-turn) target, where a simulated user and your target agent alternate turns, now runs end-to-end. Conversation scenarios also gained structured persona configuration (name, goal, expertise level, and tone compose the simulated user’s system prompt, with the existing free-text Simulated User Prompt as a fallback), and a run’s detail view now shows the full turn-by-turn Conversation Transcript with per-turn latency and cost plus a total conversation cost from real per-model pricing — see Persona configuration for conversation scenarios and Reading run results
  • Conversation scenarios now honor their inner target’s prompt template — the Prompt template you configure for a Conversation (multi-turn) scenario’s inner target was previously discarded, so the target agent ran with no template and no system prompt. It’s now applied to every turn, with {{input}} resolving to that turn’s message, and a configured system prompt is rendered against the run’s variables just like a single-turn prompt target — see Scenario target types
  • Plan retention is now enforced on reads — traces, evaluation scores, guardrail events, and incident events older than your plan’s retention window (14 days on Free through 1 year on Scale) are excluded from every API response and dashboard view. Widening a date filter past the window no longer returns older data — see Data retention
  • OTLP trace ingest reaches parity with the native SDK path/v1/traces now enforces the same monthly event quota and 1 MB body limit as /v1/ingest, plus its own 512-span-per-request cap (matching the OpenTelemetry SDK/Collector’s own default max_export_batch_size, rather than reusing /v1/ingest’s 100-span cap, which was sized for the native SDK’s batching and would have silently dropped most spans from a default-configured OTel exporter), with the overflow reported through OTLP’s partialSuccess.rejectedSpans — see OpenTelemetry integration
  • OTLP spans now honor organization custom pricing — spans ingested over OTLP are priced through the same resolver as SDK spans (custom ModelPrice rows first, built-in table as fallback) instead of always using built-in rates. Orgs with negotiated or self-hosted model pricing will see corrected costs on OTLP traces
  • /v1/metrics and /v1/logs now return 501 instead of a misleading 202 — these OTLP signals were never stored; the endpoints previously accepted and discarded the payload while reporting success. They now fail honestly and non-retryably. Point only your trace exporter at Zespan — see OpenTelemetry integration
  • Per-trace sampling in both SDKs — a sampleRate/sample_rate below 1.0 now keeps or drops a whole trace, decided from the trace id, instead of rolling per event. Sampled traces are complete: no orphaned child spans, no wrong span counts, and the same verdict on every service a distributed trace passes through. This also fixes a bug where a trace arriving via OpenTelemetry (or from another service in a distributed trace) always sampled in at 100%, ignoring sampleRate entirely — if you use OTel with sampleRate below 1.0, upgrading will reduce your ingest volume down to your configured rate, which is the fix working as intended, not new data loss — see sampleRate
  • Corrected span counts across the dashboard — agent summaries, the AI Hub top-agents panel, top-cost and slow-trace tables, the ops expensive-traces panel, and session summaries now count distinct spans instead of raw rows, so their span and error totals match the trace detail view
  • Multi-trace root-cause investigation — ZespanPilot can now investigate a recurring Issue across its sample traces instead of one trace at a time, and reports whether the occurrences share a single root cause or are several distinct failure modes colliding on the same verdict/operation/error-code cluster key — so an Issue that is quietly two problems says so — see Automatic remediation suggestions
  • Automatic remediation suggestions — an Issue that recurs three or more times gets a remediation suggestion generated for it without anyone asking, ready on the Issue detail page with the time it was produced. Still text only: no pull request, no prompt edit, no config change — see Automatic remediation suggestions
  • Evaluator authoring from chat — describe what you want scored (“check whether replies stay on topic”) and ZespanPilot maps it to the right built-in metric and creates the evaluator, instead of pointing you at the dashboard — see What ZespanPilot can do
  • ZespanPilot conversations survive navigation — leaving the Pilot page and coming back resumes the same conversation with its transcript instead of silently starting a new one, and reopening a conversation from History now shows its messages — see Opening ZespanPilot
  • Export a dataset run comparison — the two-run comparison view on a dataset’s Runs tab now has Export CSV and Export HTML buttons, so a report can be saved, attached to a PR, or shared without needing dashboard access — see Comparing two runs

Platform updates — Agentic reliability & RAG evaluation

Released: July 2026
  • Enforcement-linked trace forensics — guardrail hits now appear inline in the trace flame graph as their own spans, colored by outcome, instead of a separate log. Promote any violation directly into a permanent guardrail rule with pre-filled tool/field/value — see Promoting a violation to a policy
  • Human approval gatesclient.awaitApproval() (TypeScript) / client.await_approval() (Python) blocks execution until an admin approves or rejects the call from a new Approvals inbox — a real human-in-the-loop primitive for your own agents’ high-risk tool calls
  • Near-miss capture & suggested policy rules — numeric guardrail checks that evaluate close to their threshold without firing are now logged; a background worker clusters recurring near-misses into suggested rules you can promote or dismiss
  • Verdict-based issue clustering — a new Issues page automatically groups recurring failed/degraded traces into a single Issue using the same deterministic verdict classifier shown on every trace, with occurrence counts and one-click promotion to a guardrail rule
  • Retroactive evaluations — score historical traces against an evaluator you didn’t have configured at the time, from a new panel on the Costs page
  • Cost-Quality Frontier — a new chart plots every model used on an operation by real cost vs. real quality, span-level joined so multi-model traces aren’t misattributed — see Cost-Quality Frontier
  • Regression testing from production failures — recurring Issues that hit 3+ occurrences are auto-captured into a “Production Failures” dataset your own CI can replay, with verdict-comparison scoring and an optional quality-gate signal — see Regression testing from production failures
  • Agent-to-agent trust ledger (Phase 1: intra-org) — a compliance badge (pass rate, violation count, last violation) now appears on delegation lines in the trace detail view and on the Agent Registry, pulled from anywhere in your org rather than only the current project
  • RAG quality evaluators — four new reference-free evaluator templates (RAG Faithfulness, Context Relevance, Context Utilization, Retrieval Hit Rate) score the retrieval step of a RAG pipeline directly, plus a fix so the existing Groundedness evaluator actually receives retrieved context instead of judging blind — see Evaluating RAG pipelines
  • RAG analytics — a new Retrieval tab on the Evaluations page trends RAG evaluator scores over time and ranks your worst-performing retrieval operations
  • Redesigned trace detail — every trace now opens into a plain-English health verdict, three evaluation lenses (final response / trajectory / per step), and a resizable span navigator + inspector, plus Flow (hierarchical/chronological node graph) and Flame (chronological/icicle) visualizations with full-screen, a Retrieval panel showing retrieved RAG chunks, an A2A task-lifecycle panel, a Session conversation view, and per-span deep-link permalinks — see Traces
  • Bedrock & Vertex LLM connections — bring-your-own-key now covers Amazon Bedrock (static access keys or STS assume-role, no long-lived secret stored) and Google Vertex AI (service-account JSON), usable by Playground, Evaluations, and Simulations just like the direct-provider connections — see LLM Connections

SDK updates — July 2026

Released: July 2026 · TypeScript (@zespan/sdk) and Python (zespan) New:
  • RAG / retrieval tracingrecordRetrieval() / record_retrieval() records a retriever span with the retrieved documents in one call; span.recordDocuments() / record_documents() and a documents argument on span.end() attach chunks to a span you’re timing. Accepts plain strings, objects, or framework nodes (LangChain Document, LlamaIndex NodeWithScore). Chunk text follows your storePrompts and redaction settings. The LangChain and LlamaIndex integrations now capture retrieved document content automatically. See Recording retrieved documents
Bug fixes:
  • Google wrapper no longer crashes generate_content (Python) — a TypeError on the token-usage fields (None where a number was expected) took down every call after the model had already responded; fixed
  • Google embedding cost is now computed — embedding calls that report only a total token count (e.g. gemini-embedding-2) previously showed $0; cost is now calculated from the correct input-token count in both SDKs
  • Manual span kind always recorded (TypeScript) — startSpan({ span_kind }) previously dropped the kind unless the span was created inside an agent context; it’s now always emitted, matching Python

Platform updates — July 2026

Released: July 2026
  • Prompt folders — organize prompts into folders, with a move-to-folder action and folder autocomplete
  • Prompt webhooks — fire a webhook on version created, label assigned, or version deleted, delivered to Slack or a signed, retrying HTTPS endpoint, with a test-delivery button
  • Dataset experiment runs — link your own pipeline’s results to a named run against a dataset via new SDK methods (getItems/get_items, createRun/create_run, run.link()), score runs with an evaluator, and compare two runs side by side
  • MCP prompt tools — the hosted MCP server now exposes prompt management (list, get, create, update tags, set label) as tools an AI assistant can call directly
  • Chat prompt message placeholders — declare a placeholder slot in a chat prompt and fill it with a caller-supplied message array at compile time (e.g. conversation history)
  • SDK prompt-fetch resiliencePromptClient.get() now serves a stale cached value or a caller-supplied fallback instead of throwing when the API is unreachable
  • Custom evaluation templates — author your own LLM-judge rubrics (numeric or categorical scoring), pin a specific judge model per template, and dry-run a template against a real trace before deploying it
  • Auto-evaluator sampling and filters — scope continuous evaluation to a sample rate and filters by model, operation, or status instead of judging every matching trace

SDK v1.0.1 — TypeScript

Released: June 2026 Bug fixes:
  • Fixed API key prefix validation warning (now expects zsp_ prefix)
  • Minor internal reliability improvements

SDK v1.0.0

Released: June 2026 Initial public release of the Zespan SDK (TypeScript and Python).

TypeScript SDK (@zespan/sdk)

  • zespan.init() singleton initialization
  • wrapOpenAI(), wrapAnthropic(), wrapGoogle() provider wrappers
  • wrapOpenRouter(), wrapBedrock(), wrapMistral(), wrapGroq(), wrapLiteLLM()
  • withZespanContext() — async context propagation
  • withAgent() — multi-agent tracing with plan, tool, and handoff spans
  • startSpan() — manual span API with eval score attachment
  • ZespanCallbackHandler for LangChain
  • wrapADKAgent() and wrapADKRunner() for Google ADK
  • PromptClient — fetch, compile, create, and manage versioned prompts
  • GuardrailBlockedError with phase and results properties
  • PII redaction with configurable key list
  • OpenTelemetry dual-export via enableOTel + otelEndpoint

Python SDK (zespan)

  • zespan.init() with autopatch for OpenAI, Anthropic, Gemini, Bedrock, Mistral, Groq
  • wrap_openai(), wrap_anthropic(), wrap_google() explicit wrappers
  • ZespanCallbackHandler for LangChain (sync + async)
  • ZespanADKTracer for Google ADK
  • with_agent() context manager for multi-agent tracing
  • PromptClient with same API as TypeScript version
  • @zespan.trace decorator for sync and async functions
  • FastAPI and Flask middleware
  • Python parity: all TypeScript features available in Python

Middleware v1.0.0

Released: June 2026
  • zespan-autogen — observability middleware for Microsoft AutoGen (agentchat + legacy pyautogen)
  • zespan-crewai — observability listener for CrewAI agents
  • zespan-fastapi — FastAPI middleware for automatic request/response tracing
  • zespan-flask — Flask middleware for automatic request/response tracing