VegaDūta Agent SDK for Python
Governance for AI agents in Python, as a dependency. Tool risk
classification, human approval, budgets, idempotent effects, content
guardrails, policy hooks, audit and telemetry, wired around the tools you
already have — plus the guarantees a security review, a compliance officer or
an incident post-mortem asks for: a kill switch, tiered autonomy, delegation
mandates that can only narrow, a tamper-evident (hash-chained, signable)
audit trail, durable SQLite stores, tool-shape pinning, governed memory,
cost attribution and policy replay. Framework-free — works on plain and
async functions — with adapters for LangChain, OpenAI-style tool calling
and an OpenTelemetry observer.
This is the Python sibling of the Java Agent SDK (sdk/java,
io.github.cd-lc:vegaduta-agent-*): same contracts, same vocabulary, same audit
format. It is a different thing from sdk/python (vegaduta_sdk), which is
the generated REST client that calls the VegaDūta platform API — this
library runs inside your own application. Apache-2.0. Python 3.10+.
Status:
0.1.0, not yet on PyPI. Install from source:pip install ./sdk/python-agent(orpip install -e ./sdk/python-agentfor development).
Why this exists
LangChain, Pydantic AI, LlamaIndex, the OpenAI Agents SDK and a hand-rolled tool loop all give you model calls and tool dispatch. None of them answers what a security review, a compliance officer or an incident post-mortem asks:
- which tools can this agent reach, and what can each one do to the world?
- who approved that refund, and what exactly was approved?
- how much did this run spend, and what stopped it?
- if the turn is retried, does the payment happen twice?
- did a web page the agent read try to re-program it?
- one audit record across every tool call, tagged with the OWASP agentic risk (ASI01–ASI10) it relates to.
The SDK answers those with one dependency-free package and one decorator, and does not require a server, a UI or a database to start (though it supports durable storage and pluggable sinks for all three).
60-second start
from vegaduta_agent import Governance, governed, RiskLabel, AsiRisk
@governed(risk=RiskLabel.HIGH, requires_approval=True, idempotent=True,
risks={AsiRisk.ASI02})
def refund(order_id: str, cents: int) -> str:
...
@governed(risk=RiskLabel.LOW, side_effecting=False, idempotent=True)
def order_status(order_id: str) -> str:
...
governance = Governance()
tools = governance.scan(__import__(__name__)) # or governance.wrap(refund)
Now, when the model calls refund:
- The dispatch gate and hooks evaluate the call;
CRITICALrisk andrequires_approval=Truerecord an approval request instead of running — the tool returns{"approvalRequired": true, "approvalId": ...}for the model to relay to the user. order_statusruns immediately; a repeat of the same call in the same run is served from the effect journal (the payment never happens twice).- A per-run budget (default: 50 tool calls) stops loops before they spend.
- Tool output is redacted for credential-looking text, and flagged when a fetched page reads like a prompt-injection attempt.
- Every outcome lands as one
key=valueaudit line on thevegaduta.agent.auditlogger and can drive metrics/OTelexecute_toolspans via observers.
Decide the approval from your own UI, a chat command or a ticket:
gate = governance.approval_gate
req = gate.pending(tenant_id="acme")[0]
gate.approve(req.id, "reviewer@corp") # runs refund(), journals the result
gate.reject(other.id, "cfo", "not this quarter")
The pieces
| Piece | Type | Default |
|---|---|---|
@governed |
decorator → ToolClassification |
MEDIUM, side-effecting |
| Dispatch gate | ToolDispatchGate protocol |
allow-all; policy lives in the decorator |
| Approval | ApprovalGate + ApprovalStore |
in-memory, 24 h TTL |
| Effect journal | EffectJournal |
in-memory; SqliteEffectJournal is stdlib-durable |
| Budget | BudgetProvider → Budget |
50 tool calls per run |
| Guardrails | ContentGuardrail at 4 stages |
secret redaction + injection flagging |
| Hooks | Hook at 9 HookPoints |
none; fail mode CLOSED |
| Audit | AuditSink |
key=value lines to vegaduta.agent.audit; SqliteAuditSink for durability |
| Observers | ToolCallObserver |
none; OtelToolCallObserver with [otel] |
| Mode | GovernanceMode |
ENFORCE; OBSERVE audits without stopping |
| Report | governance_report(run_id, events, approvals) |
Markdown/JSON: per-tool risk, outcomes, approval decisions, OWASP ASI coverage |
| Kill switch | HaltSwitch + HaltGate |
engages a halt on a tool, an agent or everything; halt_drill proves it works |
| Autonomy | AutonomyGate + AutonomyRegistry |
L1 observe → L4 act; L2 recommends, L3 needs approval on HIGH, CRITICAL always gated |
| Mandates | Mandate.derive, verify_mandate_chain, MandateGate |
delegation that can only narrow, digest-chained across hops |
| Evidence | HashChainedAuditSink, HmacSha256, verify_audit_chain, evidence_bundle |
tamper-evident audit with optional signature; JSON/Markdown export |
| Provenance | ProvenanceGate on Governance(provenance=...) |
pins each tool's promised surface; ENFORCE refuses a drifted description or schema |
| Memory | GovernedMemoryStore |
writes pass guardrails, carry provenance; tool-output text is poison-probed |
| Cost | CostLedger + CostObserver, showback |
per-run/agent/principal/tool attribution and a markdown report |
| Replay | policy_replay, ShadowGate, TraceRecorder, replay_trace |
measure a stricter policy before enforcing it; record and re-run tool traces |
| Risk map | risk_map(module) |
the scan-before-you-integrate report: unclassified tools, approval coverage, lethal-trifecta, ASI exposure, 0-100 score |
| Notify | slack_message(req), teams_message(req) |
approval payloads for chat review; the HTTP transport is your port |
| Reconcile | interrupted_keys(journal) |
effects that began but never recorded — the "did the payment run?" check |
| Approvals+ | FourEyesAuthorizer, allowed_deciders, sign_receipt/verify_receipt |
two-person rule and signed decision receipts |
| Async | Governance.wrap_async / tool.acall(...) |
same pipeline around async def tools |
LangChain
from vegaduta_agent.adapters.langchain import to_langchain_tools
lc_tools = to_langchain_tools(governance.scan(my_tools_module), context=ctx)
# pass to create_react_agent / bind_tools as usual
Install the extra with pip install './sdk/python-agent[langchain]' (quote it: zsh expands the
brackets; the name vegaduta-agent[langchain] works once the package is on PyPI). The pipeline is unchanged — the
adapter only translates the call surface, so governance behaves identically
across frameworks.
OpenAI-style tool calling
from vegaduta_agent.adapters.openai import openai_tools, dispatch_tool_call
tools = governance.scan(my_tools_module)
resp = client.chat.completions.create(model=..., tools=openai_tools(tools.values()))
for call in resp.choices[0].message.tool_calls or []:
messages.append({"role": "tool", "tool_call_id": call.id,
"content": dispatch_tool_call(call.model_dump(), tools, ctx)})
Dependency-free — it works with the OpenAI SDK, LiteLLM, Azure OpenAI,
Ollama's OpenAI mode or a hand-rolled loop. The governed result (including
approvalRequired JSON) is the tool message the model sees.
Durability and evidence
Everything is in-memory by default so the SDK starts with zero
infrastructure; the stdlib sqlite3 stores make approvals, audit and the
effect journal durable with one file:
from vegaduta_agent import (Governance, SqliteApprovalStore, SqliteAuditSink,
SqliteEffectJournal, HashChainedAuditSink, HmacSha256,
verify_audit_chain, evidence_bundle)
sink = SqliteAuditSink("agent.db")
chain = HashChainedAuditSink(sink, HmacSha256(key)) # tamper-evident + signed
g = Governance(approval_gate=ApprovalGate(SqliteApprovalStore("agent.db")),
journal=SqliteEffectJournal("agent.db"), audit_sink=chain)
...
report = verify_audit_chain(sink.events()) # intact or the break point
bundle = evidence_bundle(sink.events(run_id), run_id).to_markdown()
sign_receipt/verify_receipt do the same for an approval decision: a signed
record of who approved what, verifiable later. HMAC is in the box; plug in
Ed25519 from your crypto library when the audit consumer is untrusted.
Webhooks and other hooks
WebhookHook posts each chosen HookPoint to your endpoint — but the
transport is a port you supply (the SDK imports no HTTP client; the contract
test test_sdk_makes_no_http_call_to_core enforces it). Three lines of
urllib.request or your existing client is the adapter; a response of
{"block": "reason"} turns the endpoint into a policy service.
Observability
pip install './sdk/python-agent[otel]' (once on PyPI: vegaduta-agent[otel]) adds OtelToolCallObserver, emitting one
execute_tool span per call with gen_ai.* attributes — the same convention
the platform's core/chat module uses, so platform and embedded agents land
in one trace view.
Rules that are easy to miss
- Share the
RunContextacross a run's calls. Budgets and the effect journal are keyed byrun_id; a fresh context per call defeats both.tool(args, context=ctx), or bind one with the LangChain adapter. - Approval resumptions are in-memory by default. An approval that
outlives the process is recorded but cannot execute;
SqliteApprovalStoremakes the request durable (the resumption still must be re-issued — a callable cannot persist). - Undeclared tools are classified
MEDIUM+ side-effecting, never silently trusted. Declare honestly:idempotent=Trueon a tool that isn't is the one dangerous combination. - Failures raise, not return.
ToolDeniedException,BudgetExceededException,GuardrailViolationExceptionpropagate to break the tool loop; adapters translate them into model-readable errors. GovernanceMode.OBSERVEis the rollout path: full evaluation and audit, nothing blocked. Measure, then enforce.
Development
cd sdk/python-agent
python -m venv .venv && .venv/bin/pip install -e '.[dev]'
.venv/bin/python -m pytest tests/ -q
.venv/bin/ruff check vegaduta_agent tests
.venv/bin/ruff format --check vegaduta_agent tests # `ruff format vegaduta_agent tests` fixes it
Metadata
Release files for vegaduta-agent 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| vegaduta_agent-0.1.0.tar.gz | 80.0 kB | With provenance | Details for vegaduta_agent-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| vegaduta_agent-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for vegaduta_agent-0.1.0-py3-none-any.whl |
Total release size: 155.9 kB
Release files / vegaduta_agent-0.1.0.tar.gz
| Download URL | vegaduta_agent-0.1.0.tar.gz |
|---|---|
| Size | 80.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
61e763675dc05bf3f4d364f8a19d0f3f1fe7458233883f89622d81a7d84438b6
|
|
BLAKE2b-256 checksum How to use checksums |
cd3dc5c0f447c28d5a65e4193b7b91afd79a18819352ac6d3475659437259454
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.
Transparency logRelease files / vegaduta_agent-0.1.0-py3-none-any.whl
| Download URL | vegaduta_agent-0.1.0-py3-none-any.whl |
|---|---|
| Size | 75.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4b11c6e842700068c385fedac744caf82687bad5d36d38d636fc8964632cd5e6
|
|
BLAKE2b-256 checksum How to use checksums |
0d32312e8ffa7cd4a38cd418e8c09c236fb745501874f6405f5049abef83829d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.
Transparency logSecurity for vegaduta-agent 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for vegaduta_agent-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.