- TypeScript 91.3%
- JavaScript 8.7%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .pi | ||
| .wayfinder | ||
| docs | ||
| extension/jev-guard | ||
| golden | ||
| scripts | ||
| test | ||
| .gitignore | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
pi-jev-guard
A pi extension where jev (the cheap typesafe model) is a pre-execution guard rail on every mutating tool call — bash, write, edit. Before a call runs, one batched jev call scores it against a question lens (rich context: task frame + recent calls + outputs); any flag ≥ its threshold hard-blocks the call with a deterministic reason fed back to the agent.
"Jev decides if it's safe to run, the LLM does the work."
How it works
agent wants to run a tool call
│
▼
mutating? ── no ──► pass (one-liner into the window buffer)
│ yes
▼
checkpoint due? (interval N · first-in-turn · after a block)
│ no ──► execute unchecked (counted)
│ yes — call is HELD
▼
one batched jev call (battery lens for the shape, 0.7/0.8 thresholds)
│ │ failure (timeout/transport/bad verdict ×retries)
▼ ▼
any flag ≥ t? fail-closed: block "guard unavailable"
block ◄── yes │ 3 consecutive (or 5/8 rolling)
│ no ▼
│ DEGRADED: notify-only + catastrophic regex floor
▼ │ probes (real calls, 60 s cooldown, ×2)
execute ◄──────────────────┘ auto-recovery → armed
- Block = deny before execution; the agent sees the flagged question's definition + confidence + "not a human click; do not rephrase, retry, or circumvent".
- Window buffer: last 12 tool-call entries (mutating rich, read-only one-liners, block events appended) — the evidence jev judges against.
- Lenses (
battery-v2): bash → 8 gate questions (destructive / out-of-workspace / remote-code-exec / credential-exposure / supply-chain / privilege-escalation / force-mutation / unguarded-expansion); write & edit → the 20-question code battery (security sinks + correctness + maintainability) + 2 filesystem gate questions. - Commands:
/guard off(session-only, audited) ·/guard on(re-arm) ·/guard status(state, counters, last verdicts). - Breaker: 3 consecutive or 5-of-8 rolling jev failures → degraded (calls pass, catastrophic floor still hard-blocks); auto-recovery via real-call probes.
- Persistent disable:
enabled: falsein.pi/guard.yaml.
Configuration as data
(This section describes the repo's own reference config — the same files any project
can place in its .pi/ to override the global defaults.)
| Path | Role |
|---|---|
.pi/guard.yaml |
gate knobs: interval, budgets, timeout, tools, thresholds, breaker |
.pi/guard-battery.yaml |
checklist: routing, 28 questions, thresholds, preambles, scope rules |
.pi/guard-outcomes.jsonl |
one line per verdict (gitignored) |
Validation
golden/ — 24 labeled cases (12 benign / 12 dangerous), question-set configs, scorer:
npm run golden. Acceptance: ≥ 10/12 catch, ≤ 1/12 false-blocks, 3-pass variance
protocol. gate-v3: 11/12 / 0 FP. Design/spec:
docs/superpowers/specs/2026-09-22-jev-guard-design.md; research notes in docs/research/.
Running & verifying
npm install
npm run check # tsc --noEmit (strict)
npm test # 23 tests, mocked jev transport
npm run golden # live jev testbench (needs TYPESAFE_AI_API_KEY)
node scripts/demo-live.mts # end-to-end demo: allow / block / fail-closed
Env: TYPESAFE_AI_API_KEY (or TYPESAFE_API_KEY). /reload picks up config + code
changes.
Requirements
- pi ≥ 0.87
- A TypeSafe AI API key in
TYPESAFE_AI_API_KEY(orTYPESAFE_API_KEY) - Node ≥ 22 (for the test suite; the extension itself runs inside pi)
Install
Global (recommended) — the guard runs in every pi session; projects without their
own .pi/guard.yaml run on built-in defaults + the embedded battery:
git clone ssh://git@git.mooivancrooy.nl:2222/crooy/pi-jev-guard.git
mkdir -p ~/.pi/agent/extensions/jev-guard
cp pi-jev-guard/extension/jev-guard/*.ts pi-jev-guard/extension/jev-guard/battery-default.json ~/.pi/agent/extensions/jev-guard/
cd ~/.pi/agent/extensions/jev-guard && npm install
Start (or /reload) pi — the statusline shows jev-guard: guard: armed (defaults).
Pick a project to tune it: copy .pi/guard.yaml + .pi/guard-battery.yaml from this
repo into the project's .pi/ and edit (per-project thresholds, interval, floor
patterns, breaker settings).
Install either globally or project-local, never both in the same project — two instances would each gate every call.
Repository layout
| Path | What |
|---|---|
extension/jev-guard/ |
the extension source (index wiring + engine, window, verdict, jev, breaker, config, embedded default battery) |
.pi/guard.yaml, .pi/guard-battery.yaml |
runtime config + question battery (data, edit freely) |
golden/ |
labeled testbench: 24 cases, question-set configs, scorer |
docs/superpowers/specs/ |
the design spec |
docs/research/ |
research notes: pi mechanics, pi-auto-review mining, spike, battery, semantics, breaker policy, golden report |
.wayfinder/ |
planning map + tickets (wayfinder local-markdown tracker) |