All notable changes to execlave-sdk (Python) will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Production audit remediation: enforcement bypass visibility, strict schema validation, and approval certificate verification evidence.
on_enforcement_bypassedconfig parameter — register a callback that fires on every ungoverned execution (network error, server error, circuit breaker open, fail-open allow). Each event dict carriesreason,agentId,timestamp, and optionalstatus/message, so the ungoverned window is observable even when the governance plane is down.- Approval certificate verification evidence in compliance reports —
verified_at_executionandverified_atfields on certificate inventory items, plus summary counts (verified vs unverified). alert_on_unverified_executionper-policy setting (default on) raises anapproval.execution_unverifiedaudit event and notification when an approved action's certificate is never verified within the grace window.- Server-Timing header on the enforce endpoint —
enforce;dur=<ms>for client-side latency attribution.
- Strict schema validation on all 78 request schemas — unknown fields now
produce a
400rather than being silently dropped. - Semantic classifier fast-path was unreachable for
action_type: 'general'(the default from enforce). Fixed: confident-safe exit now covers all action types that lack dangerous context, reducing median classify time from 5.5s to ~300ms. - XSS sanitizer no longer corrupts CEL expressions — markup patterns now
only apply when
<is present in the value. custompolicy type rejected — was creatable but inert; now returns a validation error.- Injection detector regex semantics — added bounded regex execution to prevent ReDoS on ambiguous patterns.
- Policy type count corrected from 20 to 19 across codebase and documentation.
- Trace schema field aliases accepted for backwards compatibility.
- Cost budget reconciliation: advisory enforcement uses discriminator field; FinOps aggregates default to production environment only.
Enforcement-reliability hardening and expanded injection coverage, plus new APIs for tool-output scanning and multi-turn evaluation.
enforce_policy()now rejects unknown fields. The server enforcement endpoint is now strict: a request carrying a field it does not recognize (for example, sending a tool name astoolNameinstead of the documentedtoolslist) returns a400instead of being silently accepted. If you relied on an unrecognized field being ignored, switch to the documented parameter names.
enforce_tool_output(agent_id, tool_name, output, ...)— synchronously scan a tool's output before feeding it back to the model. RaisesPolicyBlockedErrorwhen a block-modetool_output_scanpolicy fires, so a poisoned or PII-laden tool result (indirect prompt injection) can be stopped before the agent consumes it.conversation_historyparameter onenforce_policy()— pass recent turns so injection scanning runs over the combined conversation, catching an attack split across multiple turns (a "crescendo") that no single turn reveals.tool_outputsparameter onenforce_policy().
enforcement_on_outage="fail_closed"now blocks on the first failure. It previously allowed the first calls of an outage through (the guarantee was gated on an internal circuit breaker that only trips after several failures) and re-leaked calls periodically during a sustained outage. It now raisesEnforcementUnavailableErroron every enforcement failure, as documented.- Server errors (5xx) now honor
enforcement_on_outage. A502/503— the common shape of an outage behind a proxy — previously bypassed the setting and raised a generic error. It now routes through the same fail-open/fail-closed path as a network failure. Fail-open allows carry asourcemarker (fail_open_network_error/fail_open_server_error). - The kill switch is no longer bypassed by the local policy cache. A paused
agent could keep receiving cached
allowdecisions for up to the cache TTL;enforce_policy()now skips the cache while the agent is paused and the cache is flushed the moment a pause is observed. - The LangChain and OpenAI Agents integrations now enforce on their primary
code path. The LangChain callback handler previously enforced only on chain
start, so a direct
llm.invoke()was traced but never gated; it now enforces at the LLM boundary (de-duplicated against an already-enforced parent run). The OpenAI Agents tracing processor now enforces the agent's own input, not only tool calls.
Behavior change to note: after upgrading, block-mode policies will apply to LangChain direct-invoke calls and OpenAI-Agents agent input that were previously ungoverned. This is the intended behavior; review your policies if you relied on those paths being unenforced.
- Certificate binding no longer stops at a hand-picked field list. The
OpenAI Chat adapter sealed only
{"model": ..., "messages": ...}into the approval certificate's action-binding digest —tools,tool_choice,temperature, andresponse_formatcould drift from what a human approved without ever invalidating the certificate. It now seals the entire request kwargs via a newseal_full_request()helper (exclusion-list, not inclusion-list: every field is covered by default, a field is only dropped by explicit, reviewed exclusion). seal_for_metadatano longer collapses an entire payload to a placeholder over one bad field. Previously, a single non-serializable value anywhere in a metadata dict (a circular reference, a callable, a custom class instance) caused the whole sealed value to fall back to{"unserializable": True}— identical at issuance and at re-execution, so certificate verification silently became a no-op for every other field in that payload. Sanitization is now per-field: only the offending field is replaced with an explicit[unserializable:...]marker.- CrewAI's tool-call enforcement previously called
exe.enforce_policydirectly with nometadataat all — arequire_approvalcertificate for a CrewAI tool call bound to nothing but astr()'d, unbounded input string, not the full tool input. Migrated toenforce_policy_boundwith the full tool input sealed, in line with every other adapter. - Added a structural contract test that fails the build if any adapter passes
a hand-built, multi-field dict literal to
seal_metadata/seal_metadata_entryinstead of a single already-complete value orseal_full_request(...)— closing off the exact pattern that caused the OpenAI Chat gap from recurring in a future adapter.
- Optional HMAC request signing. New
sign_requestsclient argument (defaultFalse). When enabled, every request body is signed with HMAC-SHA256 keyed by the API key and sent withX-Execlave-TimestampandX-Execlave-Signature(sha256=<hex>) headers. Implemented as arequestssession auth callable, so it covers every call from a single point and signs the exact serialized body bytes (${timestamp}.${body}) the server verifies. Defense-in-depth on top of TLS + API-key auth; opt-in and fully backward-compatible (unsigned requests are unaffected when the server has not made signing mandatory).
1.3.0 - 2026-06-03
- Agent identity stamping. New
stamp_identityclient argument (defaultFalse). When enabled, the client issues and caches a short-lived RS256exe_agt_credential per agent and attaches it to each trace on ingest (agentCredentialfield), so the platform can cryptographically stamp who produced the trace. Best-effort and non-breaking: if a credential cannot be issued, traces are still sent unstamped — stamping never blocks or drops ingest. - MCP tool-integrity surfaces. Optional, backwards-compatible additions for
MCP tool-supply-chain governance:
tool_descriptor(server, tool, descriptor, description=None)— computes the stable SHA-256 descriptor hash (canonical, key-order independent) used to pin and diff a tool.report_tool_baseline(agent_id, descriptors, reason="manual")— pins the approved set of(server, tool, descriptorHash)tuples for an agent; re-pin withreason="baseline_update"after a reviewed tool update.enforce_policy()accepts an optionaltool_descriptorsargument that is diffed against the agent's pinned baseline at runtime.- New
ToolIntegrityError(subclass ofPolicyBlockedError) is raised when an enforcement is denied by atool_integritypolicy. Callers that do not use these fields are unaffected.
- No telemetry. The SDK does not phone home, emit anonymous usage events, or fetch remote configuration. Every network call goes to the Execlave backend URL configured by the caller.
1.2.1 - 2026-05-29
- AI Agent Management Platform (AMP) surfaces. Optional, backwards-compatible
additions for the governance features:
register_agent()accepts an optionalautonomy_level(observe|advise|act_with_approval|autonomous) that maps the agent onto a tiered-governance template.- New
report_agent_metadata()method that records a version snapshot in the agent registry (version_label/git_commit/deployed_at/notes/activate) — call it from a deploy pipeline to build version history for diff/rollback. - Available on both
ExeclaveClientandAsyncExeclaveClient. Callers that do not set these fields are unaffected.
1.2.0 - 2026-05-28
- Framework auto-instrumentation modules (opt-in via
execlave.integrations, each with its own install extra): LangChain ([langchain]), OpenAI Agents SDK ([openai-agents]), CrewAI ([crewai]), LlamaIndex ([llamaindex]), Model Context Protocol ([mcp]), OpenAI Chat Completions ([openai]), and AutoGen ([autogen]). Each routes the framework's tool calls / completions through policy enforcement and trace ingestion without changing the host app's call sites.
1.1.5 - 2026-05-05
ValidatorDeniedError(extendsPolicyBlockedError) for programmatic handling of denials originating from a Custom Validator (BYOV). Thefrom_violations()factory returns aValidatorDeniedErrorwhen any violation is validator-sourced and a plainPolicyBlockedErrorotherwise, so existingexceptsites keep working.
http://api.execlave.comis normalized tohttps://api.execlave.comso POST-based calls are not downgraded to GET by an HTTP-to-HTTPS redirect.enforce_policy()now sends the client environment by default, matchingregister_agent()and avoiding accidental production-policy enforcement from development SDK clients.
1.1.4 - 2026-05-05
register_agent()now handles agent responses wrapped as{ "data": [...] }by selecting the matchingagentIdinstead of passing the list intoAgent. Malformed list responses now raiseExeclaveErrorwith a clear response-shape message instead of surfacingAttributeError: 'list' object has no attribute 'get'.
1.0.0 — 2026-04
- Initial public release of
execlave-sdkon PyPI. ExeclaveClientclass withenforce(),ingest_trace(), andregister_agent()methods.- Async variants via
AsyncExeclaveClient(httpx-based). - Type hints throughout the public API, verified with
mypy --strict. - PEP 621
pyproject.tomlwith a hatchling build backend. - Python 3.10+ supported; tested against 3.10, 3.11, and 3.12.
- Support for API keys via the
exe_/exe_test_prefix.
- TLS certificate verification is always enabled. Callers who need to
target a self-signed local environment must set
verify=Falseon the client explicitly and are warned on construction. - The SDK refuses to accept API keys that do not match the
exe_*prefix, preventing accidental use of unrelated credentials.