Decision-first Python SDK for policy-aware agent action control
Project description
Xenovia SDK for Python
Decision-first control hook for agent actions.
Full documentation:
docs/SDK_REFERENCE.md(complete API and mode behavior)
Core Model
Xenovia decides. Your code executes.
- The SDK sends action intent to Xenovia for policy decisioning.
- The SDK returns a typed response with decision and trace context.
- Local execution stays in your environment, never in Xenovia.
Before vs After SDK
Before (proxy-only wiring, ad-hoc in each tool integration):
- Calls can still go through the proxy, but request/response shape is custom per tool.
- Block/failed/success handling is inconsistent across agents.
- Sequence tracking and result-to-decision mapping are harder to do reliably.
After (SDK-wrapped integration):
- Every call uses one contract:
capability + payload + session_id + identity + env. - Every outcome uses one response object:
status + decision + result + trace_id. - You can enforce or observe consistently and map call sequence + outcomes deterministically.
flowchart LR
subgraph BEFORE["Before SDK (manual wiring)"]
B1["Agent"] --> B2["Custom tool wrapper(s)"]
B2 --> B3["Xenovia Proxy decision"]
B3 --> B4["Local execution"]
B4 --> B5["Raw / inconsistent tool result handling"]
end
subgraph AFTER["After SDK (standardized control hook)"]
A1["Agent"] --> A2["Xenovia SDK execute/guard"]
A2 --> A3["Xenovia Proxy decision + trace_id"]
A3 --> A4{"mode"}
A4 -- "enforce + deny" --> A5["blocked response"]
A4 -- "allow or observe" --> A6["Local execution"]
A6 --> A7["Unified XenoviaResponse<br/>status + decision + result + trace_id"]
A7 --> A8["Sequence mapping + behavior controls"]
end
Execution Flow
flowchart TD
A["Agent / App"] --> B["Xenovia SDK (execute/guard)<br/>infers capability from module.function"]
B --> C["Xenovia Proxy<br/>policy decision + trace_id"]
C --> D{"Decision"}
D -- "deny" --> E["Blocked response<br/>status=blocked + reason + trace_id"]
D -- "allow" --> F["Local tool execution<br/>(your infra)"]
F --> G["Capture actual result/outcome"]
G --> H["Return unified response<br/>decision + result + trace_id"]
H --> I["Sequence mapping<br/>session_id + capability + call order"]
I --> J["Behavior controls<br/>detect/limit duplicate or multi-call patterns"]
Installation
pip install xenovia-sdk
For local development:
pip install -e ".[dev]"
Quick Start
Capability is inferred automatically from the calling module and function name — you do not need to name it.
from xenovia_sdk import Xenovia
xenovia = Xenovia(
api_key="xv_...",
endpoint="https://api.xenovia.io",
default_env="prod",
auto_session=True,
)
# capability inferred as "payments.transfer" when called from payments.py
decision = xenovia.execute({"service": "checkout", "version": "v2"})
if decision.is_blocked():
print("Blocked:", decision.decision.get("reason"))
elif decision.is_success():
print("Allowed:", decision.trace_id)
else:
print("Failed:", decision.error)
Guard Modes
guard() wraps existing tool functions with decision-aware control. Capability is inferred from the decorated function's module and name.
# capability inferred as "k8s.deploy" when defined in k8s.py
@xenovia.guard(mode="enforce")
def deploy(payload):
return kubectl_apply(payload)
Supported modes:
enforce(recommended): execute local function only when decision outcome isallow.observe: always execute local function, but still return Xenovia's decision for visibility.
Migration pattern:
- Start with
observe. - Review would-have-blocked events.
- Move sensitive capabilities to
enforce.
API Surface
Xenovia(
api_key: str,
endpoint: str = "https://api.xenovia.io",
default_env: str = "prod",
raise_on_block: bool = False,
timeout: int = 5,
auto_session: bool = False,
debug: bool = False,
identity_id: str | None = None,
identity_type: str = "agent",
unreachable_mode: str = "error",
allow_insecure_http: bool = False,
)
Primary methods:
execute(payload, capability=None, session_id=None, env=None) -> XenoviaResponseguard(capability=None, mode="enforce") -> decorator
Capability is inferred as module.function_name when not provided. It can always be overridden explicitly.
XenoviaResponse fields:
status:success|blocked|failedresult: local function output (if executed viaguard) or backend-provided resulterror: string error when presentdecision: decision payload (outcome,rule_id,reason, ...)trace_id: request trace identifier
Wire Contract
SDK request envelope:
{
"capability": "payments.transfer",
"payload": { "...": "..." },
"session_id": "sess_123",
"identity": { "type": "agent", "id": "agent_7" },
"env": "prod",
"sdk": { "version": "0.1.2", "language": "python" }
}
Typical response shape:
{
"status": "blocked",
"result": null,
"error": "policy_violation",
"decision": {
"outcome": "deny",
"rule_id": "SEQ_001",
"reason": "unsafe_sequence"
},
"trace_id": "xnv_123"
}
Security Defaults
The SDK ships with opinionated safety defaults:
- HTTPS endpoint enforcement by default (
allow_insecure_http=False). - If
allow_insecure_http=True, only localhost/loopback HTTP endpoints are permitted. - No secret or payload logging in normal operation.
- Explicit fail-mode for Xenovia outages:
unreachable_mode="error"(default)unreachable_mode="allow"(fail-open)unreachable_mode="block"(fail-closed)
- Structured identity metadata on every request.
- Timeout required and validated.
Production Configuration Example
import os
from xenovia_sdk import Xenovia
xenovia = Xenovia(
api_key=os.environ["XENOVIA_API_KEY"],
endpoint=os.environ.get("XENOVIA_ENDPOINT", "https://api.xenovia.io"),
default_env=os.environ.get("XENOVIA_ENV", "prod"),
identity_id=os.environ.get("XENOVIA_AGENT_ID", "payments-agent"),
auto_session=True,
raise_on_block=False,
unreachable_mode="block", # fail closed for sensitive environments
)
Development
Run tests:
python -m unittest discover -s tests -v
Publishing and Security Docs
- See
PUBLISHING.mdfor release workflow and publishing checklist. - See
SECURITY.mdfor vulnerability reporting and operational controls. - See
CHANGELOG.mdfor release history. - See
docs/SDK_REFERENCE.mdfor full mode matrix and behavior reference.
CI and Release Automation
.github/workflows/ci.ymlruns tests, builds the package, and checks metadata..github/workflows/publish.ymlis manual (workflow_dispatch) and supports separate targets:testpypiandpypi..github/dependabot.ymlkeeps dependencies and GitHub Actions updated weekly.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file xenovia_sdk-0.1.2.tar.gz.
File metadata
- Download URL: xenovia_sdk-0.1.2.tar.gz
- Upload date:
- Size: 20.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5161678d06258a92ff98c99d798ff81d3db5e734eaf52774fb768e738d7721a6
|
|
| MD5 |
38cd1195704dc9b462cb43b4fb33ceb0
|
|
| BLAKE2b-256 |
51da04cf686fe0024a84c69e2129d8618b76404f192f2fbace0a0c3d430697fa
|
File details
Details for the file xenovia_sdk-0.1.2-py3-none-any.whl.
File metadata
- Download URL: xenovia_sdk-0.1.2-py3-none-any.whl
- Upload date:
- Size: 9.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f67eee0cb5729837d67a7293762603abef3c9782e9ec12f9a6216a8ed4b6bd4b
|
|
| MD5 |
9f5f8e0a38de14f35ec47fecf896113f
|
|
| BLAKE2b-256 |
8f2a2bb189fc0a84c72808dd9eaea4f25bedf5c999baa198039f494fdd452d7c
|