Today we are proud to release version 1.0 of kyvvu-engine, together with 1.0 of the Python client, the command line, the baseline policies, the templates, and the Claude Code, LangChain and CrewAI integrations. We have been running the engine with clients in finance, insurance, healthcare and government for most of this year, and after a lot of stress testing, a penetration test, and more rewrites than I like to admit, we decided it was time to call it a full version. To us a 1.0 means that the public interface and the 25 built-in rules are frozen for the 1.x series (for the time being, but we make an effort to keep things stable), and that what the engine promises is written down in one place, the engine contract.

I want to use the occasion to explain why I think this engine, a small library that evaluates rules before an agent acts, is the part of the stack that matters most. It is the thing we started Kyvvu for.

Harnesses are converging on hooks

An AI agent is a model inside a harness: the code that turns the model’s output into tool calls, file writes, HTTP requests and messages. The model proposes; the harness executes. So the harness is the only place where an action exists as an action, before it has happened, and it is the only place where it can be stopped.

When we started, we had to argue this point in almost every conversation. Over the last year the harnesses themselves have started to make it for us. Claude Code has a PreToolUse hook that runs before every tool call and can deny it. The OWASP Agent Control Standard defines control points at which a harness hands a proposed action to an external evaluator and waits for a decision. Microsoft’s Copilot Studio calls a security webhook before an action runs. LangChain and CrewAI have had callbacks and event buses for a while, and a callback invoked before a tool call is the same thing under another name. Each of these is a pre-action hook: the harness pauses, describes what it is about to do, and asks.

I am glad about this, and not only because it is the design we have advocated since day one. It changes the question. Once every harness asks before it acts, the interesting part is no longer where to intercept. It is what the harness sends, and what answers it.

A shared vocabulary for actions

The first problem is that every harness describes its actions differently. A Claude Code Bash call, a LangChain tool invocation and a CrewAI task output are three shapes for things that, from a policy’s point of view, are often the same thing: the agent is about to execute code, or read a resource, or send a message outside.

The Agent Action Grammar (AAG) is our open answer to that, and it is the part of our work I am personally most attached to. It describes an action as a kind and a verb plus properties. There are twelve kinds. Four mark the boundaries of a task (task.start, task.end, task.error, task.idle) and eight describe what an agent does inside one: step.model, step.resource, step.exec, step.message, step.credential, step.self, step.gate and step.unknown. The verbs are GET, POST, PATCH and DELETE. A Claude Code Bash call becomes a step.exec; a tool that fetches a web page becomes a step.resource GET with a target; an approval becomes a step.gate with a decision.

An integration is then a mapping from the harness’s own events onto AAG, kept in a template file that anyone can read and test. In 1.0 those templates live in their own package, kyvvu-templates, and the engine itself accepts only AAG. The consequence is that a policy is written once, in terms of kinds and verbs, and applies to an agent built with LangChain, a CrewAI crew or a Claude Code session alike. It also means you can check what an integration covers by reading its template, rather than taking our word for it.

A reference monitor, not a model

The second problem is what answers the hook. There are two options. One is to ask a model: give a second model the proposed action and the context, and let it judge. The other is a reference monitor, the decades-old idea from operating systems: a small, always-invoked, tamper-resistant component that checks every access against a policy and returns a decision.

The Kyvvu engine is the second kind. It evaluates each AAG action against a set of policies and the history of the task, and returns allow, warn or block with the outcome of every policy behind it. No model is involved in the decision. The same inputs give the same verdict on any machine at any time, which the contract states and which you can replay. Time-based rules read the timestamp the action carries, not a wall clock. If no policy set can be loaded, the engine fails closed. It runs inside the agent’s process, or beside it as kyvvu serve for harnesses that are not written in Python, and evaluation takes well under a millisecond.

Both options have their place. A model can judge things a rule cannot express, and a rule can guarantee things a model cannot. We chose the rule side because the guarantees are what an organisation needs when it has to state what its agents will not do, and because we wanted something we could reason about ourselves.

Rules over the whole task

The part I care most about is the third one. A per-call check answers “may this agent run a shell command?”. The questions that matter in practice are different: may it run a shell command after it has just read a secret file? May it send an e-mail after it has fetched content from outside our domain, without a fresh approval in between? These are statements about a sequence, and the engine keeps the ordered history of the task so that policies can refer to it. We call these policies on paths, and as far as I know no other runtime check offers them.

A few examples from the manifests that ship with 1.0 make this concrete. The first is from the OWASP baseline every agent gets at registration:

- name: "Code execution requires a preceding gate"
  enforcement_point: step_execution
  rule_type: step_requires_gate
  params:
    target_kinds: ["step.exec"]
  severity: critical

A step.exec is blocked unless a completed step.gate with decision pass appears earlier in the same task. The gate can be a human approval or a guardrail; the rule only cares that it happened, and that it happened before.

The second is the taint rule, also in the baseline. In prose: after a step.resource GET whose target is marked as external, any outbound message, code execution or write is blocked unless it is directly preceded by a gate. A single approval at the start of the task does not count, because the external content arrived after it. This is the pattern behind the indirect prompt-injection incidents of the last year, where instructions hidden in a retrieved document redirect the agent, and it cannot be expressed without the history.

The third is from the Claude Code Safety manifest, which coding agents get instead of the OWASP set:

- name: "No exec after secret read"
  enforcement_point: step_execution
  rule_type: tainted_path_block
  params:
    taint_kind: step.resource
    taint_verb: GET
    taint_property_filter:
      data.classification: secret
    target_kinds: ["step.exec"]
  severity: critical

Reading .env is allowed. Running a shell command is allowed. Running a shell command after reading .env, in the same session, is not. The same manifest bounds the number of executions per task and the number of consecutive ones, which stops a loop before it burns through a budget.

Each of these rules is one entry in a YAML file, and each is a statement that a per-call permission system cannot make. We use them ourselves, every day, on our own coding agents.

What 1.0 fixes

A 1.0 is a promise about what will not change. Concretely:

  • The public interface is exactly what kyvvu_engine exports, pinned by a test. Within 1.x a release may add to it and may not break it.
  • The 25 built-in rules and their parameters are frozen. A renamed rule or parameter is a 2.0.
  • The contract states determinism, the task lifecycle, what evaluation returns, and what happens to every event you send: what is refused before it is stored, what is always recorded, and how delivery to a sink can fail and how you observe it.
  • The trace of a task is hashed by the client and verified by the platform on arrival, so a stored record is an event the agent sent, in the order it sent it, with its covered fields unchanged. The contract also states what the chain does not cover, and that it is unsigned.
  • Policy sets are unsigned in 1.0. The engine enforces the set it is delivered; the contract names who could change that set undetected.

The packages are now split along the lines above. kyvvu-engine is the evaluator and the platform client. kyvvu-templates holds the mapping from harness events to AAG. kyvvu-baseline holds the fifteen baseline policies. kyvvu is the Python client for agents you write yourself. kyvvu-cli is the kyvvu command, and the Claude Code, LangChain and CrewAI integrations are packages of their own. Everything except the engine is Apache 2.0; the engine is BSL 1.1, free for development, testing and evaluation.

Try it

uv tool install kyvvu-cli
kyvvu try

kyvvu try runs a four-step demo agent against the baseline, offline, with no account: it warns on an unmarked model output and blocks an ungated code execution.

kyvvu try 1.0.0: four steps, one warn, one block, and the same step allowed after a gate

The documentation is new with this release and has a section on the engine, including the contract, the settings and the HTTP API of kyvvu serve. Issues and questions go to github.com/Kyvvu/issues.

The engine does what a reference monitor does and no more. It governs the actions a harness submits; an action the harness does not submit is not governed, which is why we keep recommending a sandbox underneath it. Within that boundary, I think 1.0 is the first component that lets an organisation write down, in one file, how its agents may behave over the course of a task, and have that enforced before each action runs, whichever model and whichever framework is behind the agent. It took us ten months to get here, and I am very happy with where we landed.