Skip to content

andreas-altamirano/wave-engine

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

wave-engine

Continuous 1–5 risk scoring for AI agent actions.

An open-source alternative to binary allow/deny authorization for AI agents. Most agent governance today is "yes or no." Real production agents need more nuance: most actions should auto-execute, some should hold for human review, a few should be blocked outright. wave-engine gives you that as a small Python library with no runtime dependencies.

Wave 1  Routine        → auto-execute, light logging
Wave 2  Logged         → auto-execute, full audit trail
Wave 3  Evaluated      → auto-execute, contextual logging
Wave 4  Review         → hold for human approval
Wave 5  Critical       → hold, escalate, full audit

License: Apache 2.0 Python 3.9+


Why this exists

If you're building an AI agent that takes real actions — sends emails, calls APIs, moves money, writes to production systems — you eventually need to answer: should this specific action actually happen right now?

The common answers all have problems:

  • Hard-coded if blocks in agent code — unmaintainable as policies grow, impossible to audit.
  • Binary allow/deny authorizers — every ambiguous case falls into the wrong bucket. Either you block too much (agents become useless) or too little (incidents happen).
  • Full-platform governance suites (Microsoft Agent 365, vendor-specific consoles) — too heavy for most teams, locked to one ecosystem.

wave-engine sits in the middle. A small, focused library that scores each action on a continuous scale and routes it to one of three outcomes: auto, review, or block. Bring your own policy. Plug it into any agent framework.


Install

pip install wave-engine                # core only, no dependencies
pip install "wave-engine[yaml]"         # adds YAML policy loader

Python 3.9+. No required runtime dependencies for the core library.


Quick start

from wave_engine import (
    Action, WaveEvaluator, Rule,
    match_system, match_action, match_context_gt, match_all,
)

# Define a policy.
evaluator = WaveEvaluator(rules=[
    Rule(
        name="high_value_refund",
        matcher=match_all(
            match_system("stripe"),
            match_action("refund"),
            match_context_gt("amount", 100),
        ),
        score=30,
        min_wave=4,                          # force at least Wave 4 (review)
        reason="Refund over $100",
    ),
    Rule(
        name="billing_touch",
        matcher=match_system("stripe"),
        score=10,
        reason="Billing system touched",
    ),
])

# Evaluate an action the agent wants to take.
decision = evaluator.evaluate(Action(
    system="stripe",
    action="refund",
    destination="cus_12345",
    context={"amount": 500, "env": "prod"},
    actor_id="support-agent-v1",
))

print(decision.wave)        # Wave.REVIEW (4)
print(decision.outcome)     # "review" — hold for human approval
print(decision.reasoning)   # "Refund over $100; Billing system touched"

# Then in your agent code:
if decision.is_auto:
    run(action)
elif decision.is_held:
    hold_for_human(action, decision.reasoning)
else:
    reject(action, decision.reasoning)

YAML policies (no Python required)

Compliance and security teams can write policy without touching agent code.

# policies/support_agent.yaml
rules:
  - name: high_value_refund
    match:
      system: stripe
      action: refund
      context:
        amount: { gt: 100 }
    score: 30
    min_wave: 4
    reason: "Refund over $100"

  - name: external_email_with_attachment
    match:
      system: gmail
      action: send
      destination_regex: "@(?!acme\\.com$)"
      context:
        has_attachment: true
    score: 35
    reason: "External recipient + attachment"
from wave_engine import WaveEvaluator, load_rules_from_yaml

evaluator = WaveEvaluator(rules=load_rules_from_yaml("policies/support_agent.yaml"))

Editing the YAML updates the policy. Restart the agent. No code change required.


How scoring works

Each rule contributes to a cumulative score when it matches the action. The total score maps to a Wave tier:

Score Wave Outcome (default)
< 20 1 Routine auto
20–39 2 Logged auto
40–59 3 Evaluated auto
60–79 4 Review hold for human
≥ 80 5 Critical hold + escalate

Rules can also:

  • min_wave: N — force the action to at least Wave N regardless of score. Useful for "anything touching production billing is at least Wave 4."
  • force_outcome: "block" — reject the action outright. Useful for destructive operations.

Thresholds and outcome mapping are configurable; the defaults above are what wave-engine ships with.


Matchers

The library ships with composable matchers so you don't have to write lambdas:

match_system("stripe", "stripe.*")               # glob patterns
match_action("refund", "delete_*")
match_destination_regex(r"@(?!acme\.com$)")      # any external email
match_context("amount", lambda v: v > 100)       # arbitrary predicate
match_context_equals("env", "prod")
match_context_gt("amount", 100)
match_all(match_system("stripe"), match_context_gt("amount", 100))
match_any(match_system("aws"), match_system("gcp"))

Or write your own — a matcher is just Callable[[Action], bool].


Integration patterns

As a gate in your agent's tool-call loop

def run_agent_action(action_dict):
    action = Action(**action_dict)
    decision = evaluator.evaluate(action)

    if decision.is_blocked:
        return {"status": "rejected", "reason": decision.reasoning}
    if decision.is_held:
        approval_id = queue_for_human_review(action, decision)
        return {"status": "pending", "approval_id": approval_id}

    return execute_real_action(action)

As a sidecar over an MCP server

Pair wave-engine with an MCP proxy (such as mcp-governance-proxy) so any Claude Desktop, Cursor, or Cline agent gets governance applied automatically — no agent code changes.

As a policy compiler target

Build your own DSL or natural-language compiler that emits Rule objects. The library is the runtime; you bring the authoring layer.


What this library is not

  • Not a credential vault. Use Vault, AWS KMS, age, or sops.
  • Not an audit log store. Persist Decision.to_dict() to whatever you already use.
  • Not an enforcement runtime. It tells you what to do; you execute, hold, or reject.
  • Not a content filter for model outputs. Use Guardrails AI, NeMo Guardrails, or Llama Firewall for that.

wave-engine is the scoring primitive. Compose it with the rest of your stack.


Examples

Run them:

git clone https://github.com/andreas-altamirano/wave-engine
cd wave-engine
pip install -e ".[yaml]"
python examples/01_support_agent.py
python examples/02_yaml_policy.py

Tests

pip install -e ".[dev]"
pytest -q

License

Apache 2.0. See LICENSE.


Acknowledgments

The Wave scoring primitive was originally developed inside Surfit, an AI agent governance platform. This library is the extracted, generalized version of that scoring engine — released so other teams can build governance into their own agents without committing to a full platform.

Companion libraries:

About

Continuous 1-5 risk scoring for AI agent actions. Open-source alternative to binary allow/deny authorization.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages