Govern an agent

Route every tool call an agent makes through the same EVE governance composition root, so each action is checked before it executes. This guide uses the generic adapter, which is the validated (SUPPORTED) path.

Prerequisites

  • The EVE SDK installed (see install.md).
  • The embedded service facade for a local run (from core.eve_sdk import EVE), or a hosted endpoint for the client.
  • Synthetic tools only (recording, no real side effects).

Installation

pip install ./eve_coreguard-0.2.2-py3-none-any.whl
# or run from the EVE repo for the embedded generic adapter

Runnable code

from core.eve_sdk import EVE, verify_evidence
from core.eve_sdk.adapters import GenericAdapter, GovernedToolBlocked

IDENT = {"tenant_id": "acme", "principal_id": "agent-1", "session_id": "sess-1"}
_executions = []

def create_loan(**kwargs):
    """Synthetic recording tool."""
    _executions.append(kwargs)
    return {"loan_id": "L-1001", "status": "created"}

eve = EVE(policy="lending_v1", mode="embedded")
adapter = GenericAdapter(eve)
guarded = adapter.wrap(create_loan, tool="loan_approval", identity=IDENT)

# Permitted call executes:
out = guarded(amount=1000, _context={"credit_score": 760, "debt_to_income": 0.15, "employment_verified": True})
print("[allow]", out, "executions:", len(_executions))

# Prohibited call is blocked before the tool runs:
try:
    guarded(amount=500000, _context={"credit_score": 480, "debt_to_income": 0.9, "employment_verified": False})
except GovernedToolBlocked as blocked:
    print("[block]", blocked.result.action, blocked.result.reason_codes, "executions:", len(_executions))

Register the same wrapping for each tool your agent can call, using one adapter bound to one EVE instance.

Expected result

  • The permitted call executes and len(_executions) becomes 1.
  • The prohibited call raises GovernedToolBlocked and len(_executions) stays 1 — zero tool side effects for the blocked action.

Evidence output

Each wrapped call yields a GovernanceResult with a signed certificate:

GovernanceResult{action: BLOCK, reason_codes: [...], decision_id: "...", certificate: {...}}

The blocked result's certificate records that the tool never ran.

Verification

v = verify_evidence("decision", blocked.result.certificate, expected_tenant="acme")
print(v["valid"], v["reason"])

Failure example

A direct call to the underlying create_loan(...) bypasses the wrapper — the wrapper enforces only calls routed through it:

create_loan(amount=999999)  # NOT governed — direct bypass

This is a known, documented property of wrapper-enforced adapters. For enforcement that a direct call cannot bypass, place governance at a gateway or sidecar boundary (see deploy-as-a-sidecar.md).

Production considerations

  • Use one EVE instance and one adapter per agent so all tools share the same policy and evidence chain.
  • Wrapper-enforced adapters are convenient but bypassable by direct underlying-tool calls; hard enforcement requires a gateway or sidecar.
  • Carry identity from your authenticated context, not from user-controlled input.

Limitations

  • Only the generic adapter is SUPPORTED. Adapters for OpenAI Agents, Claude Agent SDK, LangChain, LangGraph, CrewAI, and Vercel AI SDK are structurally implemented but experimental and not validated against pinned live framework versions.
  • Wrapper-enforced adapters are bypassable by a direct call to the underlying tool. Claude Code hooks are cooperative, not an unbypassable boundary. Vercel is server-only.
  • Monitoring is observational; enforcement decisions are made by CoreGuard, sequence, and budget controls.

Next step

Add per-window behavior monitoring with detect-behavioral-anomalies.md, or block dangerous multi-step chains with block-a-prohibited-sequence.md.

Part of the EVE AI Core control plane Deterministic AI Governance Control Plane → Policy decisions that return the same result for the same input every time, before execution.