> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zespan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Changelog

> Release history for the Zespan platform and SDKs.

## SDK updates — August 2026

**Released:** August 2026 · TypeScript (`@zespan/sdk`) and Python (`zespan`)

**New:**

* **Cohere** — `wrapCohere()` (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](/sdk/integrations/cohere)
* **Vectra** — `ZespanVectraCallbackHandler` (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](/sdk/integrations/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](/sdk/otel-integration#send-traces-from-any-otel-instrumented-app-no-zespan-sdk)
* **9 new per-framework/gateway OTel integration pages** — [Ollama](/sdk/integrations/ollama), [DSPy](/sdk/integrations/dspy), [OpenAI Agents SDK](/sdk/integrations/openai-agents-sdk), [Strands Agents](/sdk/integrations/strands-agents), [Mastra](/sdk/integrations/mastra), [Agno](/sdk/integrations/agno), and a new **Gateways** category covering [LiteLLM Proxy](/sdk/integrations/litellm-proxy), [Kong AI Gateway](/sdk/integrations/kong-ai-gateway), and [Cloudflare AI Gateway](/sdk/integrations/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:**

* **Policy testing and the deny gate** — `zespan 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](/policies/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](/policies/local-evaluation)
* **Policy as code: adoption, review screen, and three new primitives** — `zespan 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](/policies/as-code), [file reference](/policies/file-reference), and [zespan policy](/cli/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 — `dryrun` → `warn` → `deny` — 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](/policies/as-code), [file reference](/policies/file-reference), and [zespan policy](/cli/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](/cli/doctor) and [CLI overview](/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](/dashboard/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](/dashboard/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](/dashboard/incidents#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](/dashboard/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](/dashboard/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](/compliance/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](/dashboard/model-lifecycle), [Models](/dashboard/models), [Alerts](/dashboard/alerts#model-lifecycle-alerts), and the [feed reference](/reference/model-lifecycle-feed)

**Bug fixes:**

* **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](/dashboard/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](/sdk/otel-integration#send-traces-from-any-otel-instrumented-app-no-zespan-sdk)
* **OpenRouter used via the `openai` client is no longer mislabeled `openai`** — `autopatch()` 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](/dashboard/incidents#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](/dashboard/simulations#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](/dashboard/simulations#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](/dashboard/simulations#creating-a-scenario)
* **CI gate for simulation batch runs** — `POST /projects/{id}/simulations/batch-runs/{batchRunId}/gate` gates a candidate batch run against an explicit baseline batch run, mirroring the [prompt-version quality gate](/dashboard/prompts#the-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](/dashboard/simulations#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](/dashboard/simulations#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](/dashboard/simulations#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](/dashboard/simulations#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](/dashboard/simulations#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](/dashboard/simulations#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](/dashboard/evaluations#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](/dashboard/evaluations#reading-evaluation-results) and [Evaluation runs](/dashboard/evaluations#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](/sdk/integrations/pinecone), [Chroma](/sdk/integrations/chroma), [Weaviate](/sdk/integrations/weaviate), [Qdrant](/sdk/integrations/qdrant), and [Manual spans](/sdk/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 evaluators** — **Latency 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](/dashboard/evaluations#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](/dashboard/evaluations#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](/dashboard/evaluations#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](/sdk/cli)
* **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](/guides/zespan-mcp#evaluation-dataset-tools)
* **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](/guides/zespan-mcp#issue-tools)
* **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](/dashboard/http-targets) and [Running against an HTTP endpoint](/dashboard/datasets#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](/dashboard/http-targets#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](/dashboard/evaluations#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](/dashboard/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](/dashboard/sessions#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](/dashboard/sessions#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](/dashboard/sessions#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](/dashboard/issues#issue-detail-page)
* **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](/dashboard/simulations#persona-configuration-for-conversation-scenarios) and [Reading run results](/dashboard/simulations#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](/dashboard/simulations#core-concepts)
* **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](/platform/security#configurable-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](/sdk/otel-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](/sdk/otel-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`](/quickstart#sdk-initialization-options)
* **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](/dashboard/issues) 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](/dashboard/issues#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](/dashboard/issues#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](/dashboard/zespanpilot#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](/dashboard/zespanpilot#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](/dashboard/datasets#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](/dashboard/guardrails#promoting-a-violation-to-a-policy)
* **Human approval gates** — `client.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](/dashboard/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](/dashboard/costs#retroactive-evaluations) 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](/dashboard/costs#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](/dashboard/datasets#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](/dashboard/agent-registry#compliance-history), 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](/dashboard/evaluations#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](/dashboard/traces#trace-detail)
* **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](/platform/llm-connections#connection-types)

***

## SDK updates — July 2026

**Released:** July 2026 · TypeScript (`@zespan/sdk`) and Python (`zespan`)

**New:**

* **RAG / retrieval tracing** — `recordRetrieval()` / `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](/sdk/manual-spans#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 resilience** — `PromptClient.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).

<CardGroup cols={2}>
  <Card title="TypeScript SDK (@zespan/sdk)" icon="code">
    * `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`
  </Card>

  <Card title="Python SDK (zespan)" icon="code">
    * `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
  </Card>
</CardGroup>

***

## 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
