Skip to content
Open source · Edictum

Check the call, not the answer.

An agent can refuse in text and still make the call. When it decides to call a tool, Edictum checks that call against your YAML rules before the tool runs, inside your own process, and blocks it or sends it to an approval handler you plug in.

License
MIT
Python
0.19.0
TypeScript
0.5.0
How it works

Between the decision and the tool.

Edictum runs inside the agent process. It checks each tool call against the loaded rules before the tool runs, with no model in the loop and no service to call. Every check ends in one of three outcomes.
Where the check sits.
  1. The model decides to call a tool.
  2. Edictum receives the tool call and its arguments.
  3. Edictum checks the call against your rules, inside the agent process, before the tool runs.
  4. Allow: No rule stopped the call. The tool runs.
  5. Block: The tool does not run. The caller gets the rule's message.
  6. Ask: Your approval handler decides. No handler, or no answer in time: blocked.
What it does

What you get.

01

Block or ask before the tool runs.

Rules can block a call or send it to an approval handler you plug in. With no handler configured, Edictum blocks the call. If the handler does not answer in time, Edictum blocks it by default.
02

Malformed policy fails at load.

Loading a ruleset fails on syntax errors, unknown rule types, unknown operators and invalid regexes. A reload that fails keeps the previous rules.
03

Observe before you enforce.

Observe mode lets calls run and records what would have been blocked, so you can read what a ruleset would do before it enforces anything.
04

Checks emit audit events.

Checks produce structured audit events that you route to your own sink. The default sink keeps them in memory. Nothing persists until you add one.
05

Runs in your process.

Deterministic code, no model in the loop, no service to call. Python and TypeScript SDKs, MIT licensed.
Rulesets

Rules are YAML you keep with the code.

A rule names a tool, a condition on its arguments and an action. The ruleset below blocks any read_file call whose path contains .env, and sends shell commands matching rm or sudo to the approval handler.

The file ran unchanged with both SDKs. Both programs printed Read of .env blocked.

rules.yaml
apiVersion: edictum/v1
kind: Ruleset
metadata:
  name: demo
defaults:
  mode: enforce
rules:
  - id: block-dotenv
    type: pre
    tool: read_file
    when:
      args.path: { contains: '.env' }
    then:
      action: block
      message: 'Read of {args.path} blocked.'

  - id: ask-shell
    type: pre
    tool: shell_exec
    when:
      args.command: { matches: '\b(rm|sudo)\b' }
    then:
      action: ask
      message: 'Needs approval: {args.command}'
      timeout: 120
      timeout_action: block
Python
import asyncio
from edictum import Edictum, EdictumDenied

guard = Edictum.from_yaml("rules.yaml")

def read_file(path):
    return open(path).read()

async def main():
    try:
        await guard.run("read_file", {"path": ".env"}, read_file)
    except EdictumDenied as e:
        print(e.reason)  # Read of .env blocked.

asyncio.run(main())
TypeScript
import { readFileSync } from 'node:fs'
import { Edictum, EdictumDenied } from '@edictum/core'

const guard = Edictum.fromYaml('rules.yaml')
const readFile = (args: Record<string, unknown>) => readFileSync(String(args.path), 'utf8')

try {
  await guard.run('read_file', { path: '.env' }, readFile)
} catch (e) {
  if (e instanceof EdictumDenied) console.log(e.reason) // Read of .env blocked.
}

A shell_exec call that matches the second rule goes to the approval handler. With none configured, both SDKs block it and report that approval is required.

YAML reference
Install

Install it where the agent runs.

Two SDKs, each with adapters for the agent frameworks it supports. Install the framework yourself. The adapter connects Edictum to it.

Python

Python 3.11+ · 0.19.0, published 2026-08-11

Python 3.11+
pip install "edictum[yaml]"

Adapters in the package

  • LangChain
  • OpenAI Agents SDK
  • CrewAI
  • Google ADK
  • Agno
  • Semantic Kernel
  • Nanobot

TypeScript

Node 22+ · 0.5.0, published 2026-04-15

Node 22+
pnpm add @edictum/core

Adapters as packages

Each adapter package lists its framework as a peer dependency.

Research

Why the call, not the text.

Cartagena and Teixeira, arXiv:2602.16943, February 2026. Across six frontier models, six regulated domains and 17,420 datapoints, the finding is in the title: text safety does not transfer to tool-call safety.

It is the research behind Edictum. A model that declines in prose can still make the call, so Edictum checks the call.

Read the paper