Skip to main content

Jev Reactor

An open-source runtime for real-time typed decisions. It uses Jev for parallel semantic judgments and ordinary code for policy, control flow, and side effects.

pip install jev-reactor                         # add [mcp] for the MCP gateway (below)
jev-reactor init                                # copies examples, fixtures and packs here
jev-reactor run examples/tool_loop.py --mock    # offline: recorded answers, no key, no network

export TYPESAFE_API_KEY=...                     # then, against live Jev:
jev-reactor run examples/tool_loop.py
Decision: skip
Reason: redundant_tool_call
Redundancy probability: 0.91
Choice confidence: 0.86

That is the output of examples/basic_decision.py --mock. In mock mode the answers are hand-written fixtures, not Jev output; run it live to see what Jev says. Jev returns typed values but can still make semantic mistakes. Nothing here is a guarantee that a decision is correct.

What it is

A software event arrives (an agent proposes a tool call, a context item ages, a turn ends). Jev Reactor turns it into a compact, redacted state, asks Jev several small typed questions in one request, and hands the answers to a deterministic Python policy. The policy returns an immutable action. The Reactor never executes anything; your application does, and only for the actions you choose.

event -> bounded state -> parallel Jev questions -> typed answers -> policy -> action -> host
         (redacted)       (one request)             (probabilities)   (plain Python)

Jev judges, code decides, adapters execute. It is not a chatbot, an agent framework, or a safety guarantee, and it does not replace Jev.

Use it from Python

# runnable
import asyncio

from jev_reactor import MockProvider, Reactor, ToolLoop, ToolSpec

tools = [
    ToolSpec(
        name="search_invoices", risk="read", idempotent=True, requires_permission="invoices:read"
    ),
    ToolSpec(name="send_email", risk="irreversible", requires_permission="email:send"),
]
loop = ToolLoop(tools)  # a question pack (what Jev is asked) + a policy (what happens)

# Recorded answers stand in for Jev. Swap in TypeSafeProvider() (needs TYPESAFE_API_KEY).
provider = MockProvider(
    {
        "relevant": 0.96,
        "should_call": 0.94,
        "redundant": 0.04,
        "task_complete": 0.03,
        "needs_user_input": 0.02,
        "injection_suspected": 0.01,
        "next_action": ("call_tool", 0.91),
    }
)


async def main() -> None:
    async with Reactor(provider, pack=loop.pack, policy=loop.policy) as reactor:
        event = reactor.new_event(
            "tool_call_proposed",
            {"recent_calls": []},
            goal="Find the status of invoice INV-2041",
            proposed_action={"tool": "search_invoices", "arguments": {"invoice_id": "INV-2041"}},
            metadata={"permissions": ["invoices:read"]},
        )
        decision = await reactor.decide(event)
        print(decision.action, decision.reason_codes)  # -> allow ['relevant_and_useful']

        # A hard rule decides this before Jev is asked; no answer can override it.
        mail = reactor.new_event(
            "tool_call_proposed",
            {},
            goal="Tell the customer",
            proposed_action={"tool": "send_email", "arguments": {"to": "a@b.c"}},
            metadata={"permissions": ["email:send"]},
        )
        held = await reactor.decide(mail)
        print(held.action, held.reason_codes, held.provider_status)
        # -> review ['irreversible_needs_approval'] not_called


asyncio.run(main())

MCP gateway: decide every tool call before it runs

jev-reactor-mcp is a real MCP server that fronts your existing MCP servers. Every tools/call goes through the allowlist, your hard rules, Jev and your policy first; a held-back call never reaches the downstream server, and the agent gets a plain reason it can act on.

pip install "jev-reactor[mcp]"
jev-reactor-mcp init --demo -o gateway.yaml   # fronts a safe fake invoice server, no API key
jev-reactor-mcp serve gateway.yaml            # stdio: use it as a server in Claude, Cursor, ...
  • Only tools you list are exposed; permissions, amount limits and the goal come from your config, never from tool arguments.
  • Irreversible actions need the human's approval, asked through MCP elicitation and bound to the exact call. A client that cannot be asked gets a held-back result, not a silent pass.
  • Tool descriptions that read like instructions to the model are withheld.
  • Serves stdio, or Streamable HTTP on loopback (a remote bind needs a bearer token).

The demo uses a scripted stand-in for Jev, so it shows the mechanics and says nothing about what Jev would decide. Setup for Claude Desktop, Claude Code and Cursor, the config reference and the limits are in docs/mcp.md.

The three question types

Type Asks Returns Notes
Noul Is this true? noul in 0..1 No separate confidence: the number is the answer and the certainty.
Choice Which of these options? choice, probabilities, confidence Up to 255 options. Always include an abstain option (other, unclear).
Score Which level? score, probabilities, confidence 2 to 10 ordered levels, numbered from 0. Describe situations, not degrees.

Ask many narrow questions about one state in one request; they run in parallel. Combine the answers in code. See docs/question-packs.md.

What is different about how this uses Jev

Jev's own documentation and the community's measurements shaped several choices that differ from the obvious approach. The full list, with sources, is in docs/design-notes.md. The short version:

  • Your hard rules run first and never depend on Jev: allowlist, permissions, user denials, amount limits, approval for irreversible actions, exact-duplicate calls. Jev's docs say adversarial text in state "can move the answer".
  • Two-sided thresholds. Between "confident no" and "confident yes" is uncertain, and uncertain goes to review instead of being forced to a coin flip.
  • Answers are not assumed to agree. A Noul and a Choice answer different questions, so disagreement is escalated, not resolved silently.
  • Fail safe, by risk. When Jev is unavailable, reads fall back to your default handling, writes go to review, and irreversible actions are blocked. Nothing defaults to allow.
  • When in doubt, keep it (context retention): pins are decided in code, discarding needs several signals to agree, and Jev never rewrites text.
  • Replay without Jev. Every decision is recorded (a digest of the redacted state, not the state); jev-reactor replay recomputes policy over the recorded answers and shows what a new threshold set would change.

Command line

jev-reactor init                       scaffold examples, fixtures and packs here
jev-reactor run <file.py> [--mock]     run an example
jev-reactor replay <runs.jsonl> [--policy strict] [--dry-run]
jev-reactor report <runs.jsonl> [--json]
jev-reactor validate-pack <pack.yaml>  lint a question pack
jev-reactor inspect <file>             pretty-print a fixture, event, pack or run
jev-reactor label <runs.jsonl> <event-id> --expected allow
jev-reactor export-pack tool-loop      a built-in pack as YAML, to start your own
jev-reactor bench [--live]             measure latency on your machine
jev-reactor mcp serve|check|init       the MCP gateway (same as jev-reactor-mcp; needs [mcp])

There is no telemetry and nothing is uploaded. A test checks that no core module can open a network connection.

Security and data: read this before using it on real data

  • Redaction (secret-shaped keys and credential patterns) runs before every provider call and every write. It is best effort: keep secrets out of state, and allowlist the fields you send.
  • By default a record stores a digest of the redacted state. No goal text and no raw provider payload are written unless you opt in.
  • State you send is processed by TypeSafe. Read their Data Processing Agreement and Privacy Policy first; zero data retention is offered for enterprise customers.
  • Recorded logs contain TypeSafe Output. Do not use them to train or distill a model: TypeSafe's Master Customer Agreement (section 2.3(b)) prohibits it.
  • This is a decision gate, not a sandbox. Stack OS-level isolation underneath. See SECURITY.md.

Measuring performance

This README makes no latency, accuracy, or cost claims for Jev, and the sample data in examples/replay.jsonl is hand-written (model mock-fixture), not measured. To measure your own setup:

jev-reactor bench            # policy and Reactor overhead on this machine, no network
jev-reactor bench --live     # a small number of real requests through your network and account

The engineering target for local policy evaluation is p95 under 5 ms; bench prints whether your machine meets it. Nothing about real-time behavior against Jev is asserted until you run --live. Thresholds in every pack are initial defaults, not calibrated values; calibrate them on your own labelled traces (docs/policy-writing.md).

Documentation

  • Architecture: lifecycle, failure semantics, concurrency, persistence
  • Providers: the TypeSafe adapter, the mock, writing your own
  • Question packs: writing questions Jev answers well, and the linter
  • Policy writing: bands, confidence, rule chains, calibration
  • MCP gateway: setup for Claude and Cursor, config reference, security model
  • Deployment: modes, deadlines, rate limits, privacy
  • Design notes: what the docs and community changed, with sources

Status and roadmap

Version 0.2.0 adds the MCP gateway to the local runtime and developer CLI. The API may change before 1.0. Live Jev has not been exercised by this project's automated tests or CI (they run offline against recorded and scripted answers); run jev-reactor bench --live and the opt-in live test with your own key before depending on it.

Next Not planned
Advisory MCP decision tools, downstream resources and prompts in the gateway, TypeScript runtime, LangGraph middleware, OpenTelemetry spans, provider fallback chains, speculative prefetch Anything that trains or distills a model on Jev output (prohibited by TypeSafe's terms), a hosted dashboard in this repository

License

Apache-2.0. See LICENSE, NOTICE and docs/dependency-notices.md. Jev and TypeSafe are TypeSafe AI's products; this project is independent and not affiliated with or endorsed by TypeSafe AI.

Release files for jev-reactor 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for jev-reactor 0.2.0
File Size Uploaded
jev_reactor-0.2.0.tar.gz 180.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for jev-reactor 0.2.0
File Interpreter ABI Platform
jev_reactor-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 312.5 kB

Release files / jev_reactor-0.2.0.tar.gz

Download URL jev_reactor-0.2.0.tar.gz
Size 180.2 kB
Tags Source
SHA-256 checksum
How to use checksums
58e932d947f36b2660431c86499fcca473ead31924de1c1431f153f12ec4d1af
BLAKE2b-256 checksum
How to use checksums
266b71f2d90e3ec92d4ad5c508ba7382a7d68ef7546f4a54f06bd3eb362596b2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / jev_reactor-0.2.0-py3-none-any.whl

Download URL jev_reactor-0.2.0-py3-none-any.whl
Size 132.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a6799bcfe3c4fdb608b2f7fb3d799095b491ce79810c0fe7806755365793219b
BLAKE2b-256 checksum
How to use checksums
c9fe68f48986659fe8218e3ce5e325e380f5748e39e043ad6b7c62d4fb8de3c0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page