Skip to content

Architecture

The problem this solves

A conventional agent loop looks like this:

transcript + every tool schema ──► generative model ──► reasoning tokens ──► a tool name

The output of all that work is one string from a known set. You paid for a full autoregressive pass, you re-sent forty tool descriptions so the model could compare them, and you waited two to seven seconds. The expensive, creative, open-ended machine was used as a switch statement.

That is not just wasteful — it is unobservable. When the agent does something strange, the only artefact is a transcript. There is no number anywhere saying how close the call was.

The split

                           ┌─────────── System One ───────────┐
  bounded state  ────────► │  action   : choice over tools     │
  (fresh projection,       │  goal_met : noul                  │  one call, 3 answers
   never the raw           │  risk     : choice low/med/high   │  no tokens generated
   transcript)             └───────────────┬──────────────────┘
                                           │
                        ┌──────────────────┴────────────────────┐
                        ▼                                       ▼
            decision is actionable                   decision is not actionable
         ┌──────────────┴──────────────┐          ┌─────────────┴─────────────┐
         ▼              ▼              ▼          ▼                           ▼
   static payload   force tool    forbid tools   delegate                  stop/ask
   (no model call)  + verify      + verify       (unconstrained,           (no model call)
                                                  exactly as before)

System One decides what. The generative model supplies how — the argument string, the prose — and only when asked. That boundary is the whole design, and it follows from one fact about the model: it is non-autoregressive, so it cannot write a bash command, compose a query, or draft a reply. It can only answer typed questions about state. Trying to make it do more is the main way this architecture is implemented badly.

Why the guards are in that order

Governor._dispatch runs seven checks. The order is load-bearing.

1. Interrupted? A decision takes a few hundred milliseconds, which is long enough for someone to hit Ctrl-C. Checking after the decision and before the effect costs nothing and is the last honest moment to notice.

2. Truncated view and a finish? Delegate. The one judgment that must never be made from a partial transcript is "the work is already done" — because the part that got dropped is exactly where the unfinished work would be. A tool route from partial evidence is fine; the next turn sees the result. A stop from partial evidence is how an agent confidently reports success on work it never did.

3. Confidently high risk? Stop and ask a human. This sits before the confidence gate on purpose. If it came after, an action judged risky but with low confidence would fall through to the generative model — i.e. the response to "this might be dangerous and I am not sure" would be to hand it to the component with fewer brakes. Risk is checked first so that uncertainty about risk never buys an escalation in capability.

4. Unconfident, or explicitly delegate? Hand it over, unconstrained. This is the designed path, not the failure path. It is supposed to be common. The fast path can afford to be strict precisely because falling off it is cheap and safe: the agent behaves exactly as it did before this package was installed.

5. clarify? Ask the user. No generative call — spending one to phrase a question is the cost this package exists to avoid.

6. finish? Only if goal_met clears its own, higher threshold. Two thresholds rather than one because the failure modes are asymmetric: a needless delegation costs a few cents, a premature stop costs the user's trust.

7. A tool? Either a pre-approved static payload (validated against the live schema, and only when risk is low), or the generative model writes the arguments under a hard constraint.

Enforcement is the load-bearing part

Everything above would be advice if step 7 ended at asking the provider nicely.

response = host.generate(force_tool="read_file", directive=...)
called = host.called_tools(response)
if called != ["read_file"]:
    raise JevError("The provider ignored the selected tool; execution blocked")

The host sets tool_choice={"type": "function", "function": {"name": ...}} and parallel_tool_calls=False. Then the result is checked. Providers ignore tool_choice — under load, on older models, through proxies and compatibility shims. Without the check, a provider that disregarded the constraint would have its own choice executed, and the decision layer would have been decorative in exactly the cases where it mattered.

The same check runs inverted for finish: a tool call after a no-tool decision also raises.

Distrusting the answer

A typed response is schema-valid, not coherent. Four properties are enforced in decision.py, and each one has a failure behind it:

Check What it catches
Not a bool isinstance(True, int) is true in Python, so a stray boolean reads as certainty
Distribution covers exactly the offered options, sums to 1 The question and the answer have drifted apart
The choice is its own argmax An internally inconsistent answer where neither half can be believed
confidence = min(stated, mass[winner]) Confidence summarising concentration more optimistically than the mass supports

None of this makes a judgment correct. It makes an incoherent one loud, which is the only guarantee a client library can honestly offer. confidence is distribution concentration — not the probability the action is right, and never permission to act.

The state projection

The governor re-asks on every step, so the size of what it sends is the entire cost model. state.py builds a fresh projection each time and throws it away:

{
  "goal": "the latest user message",
  "instructions": "system messages, concatenated once, in order",
  "recent_history": [ "newest turns that fit, oldest-first" ],
  "truncated": true
}

Two properties matter more than the shape.

The canonical transcript is never touched. Not truncated, not reordered, not rewritten. It is what the generative model and the user's own history depend on; a decision layer that edits it to make its own job cheaper has corrupted the record.

The bound is enforced on the serialized length, not the sum of field lengths. JSON escaping can multiply a string several times over — a transcript full of control characters overruns a naive character count badly, and the limit would silently stop holding.

And truncated is reported rather than hidden, because guard 2 depends on it.

What it costs

One System One call per step, plus the generative call when one is needed. It pays off when the model you are routing with is expensive and the routing decision is easy. It does not pay off if you are already routing with a cheap local model — see limits.md.

Governor.events records decision_ms, the resolved model id (e.g. jev-1.13.0, so a behaviour change is attributable after the fact), token usage, and which of the nine paths the turn took. Measure on your own traffic; do not trust the table in anyone's README, including this one.