pi-jev-guard

Jev validation guard for pi: on-demand tool + automatic provider gate. Dual backend: TypeSafe direct + OpenRouter.

Packages

Package details

extensionskill

Install pi-jev-guard from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:pi-jev-guard
Package
pi-jev-guard
Version
0.4.0
Published
Sep 22, 2026
Downloads
1,744/mo · 1,744/wk
Author
alucard_24
License
unknown
Types
extension, skill
Size
221.6 KB
Dependencies
4 dependencies · 3 peers
Pi manifest JSON
{
  "skills": [
    "./skills"
  ],
  "extensions": [
    "./extensions/index.ts"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

pi-jev-guard

Verifica le risposte con TypeSafe Jev dentro pi: un solo motore di validazione, due interfacce.

  • A richiesta (default): tool jev_validate, comando /jev check, skill jev-review. Zero verifiche implicite, zero costi se non lo usi.
  • Domande tipate: tool jev_ask per giudizi calibrati definiti dal modello (noul/choice/score) invece di prosa.
  • Automatica: gemello guarded <modello>__jev dentro lo stesso provider, che trattiene la risposta, la verifica con Jev e pubblica solo l'output approvato (con rigenerazione privata su block). Stessa auth del provider: nessun login extra, funziona anche con Codex/OAuth.
  • Giudice output (solo automatic, fail-open): dopo ogni bash cerca secret nel testo e classifica i fallimenti, allegando un consiglio al risultato.

Installazione

cd pi-jev-guard
npm install
mkdir -p ~/.pi/agent
cp config/jev-config.example.json ~/.pi/agent/jev-config.json
pi install "$PWD"

Chiavi (a seconda del backend Jev scelto):

export OPENROUTER_API_KEY="sk-or-v1-..."   # backend openrouter (default)
export TYPESAFE_API_KEY="..."              # backend typesafe diretto

Uso normale (invariato)

Le nuove sessioni partono in on-demand: i modelli normali restano selezionabili e nessuna verifica parte da sola. /jev mode automatic|on-demand salva la scelta nella sessione, non nella config globale. La scelta sopravvive a /reload, riavvio e /resume; fork e /tree seguono lo stato del ramo. Credenziali, policy e catalogo dei twin restano condivisi.

Migrazione: il vecchio mode in jev-config.json non controlla più l'estensione (resta compatibile con i consumer standalone). Nelle sessioni senza uno stato salvato, riattiva automatic una volta se desiderato. JEV_MODE=automatic può impostare il valore iniziale in headless; una scelta già salvata nella sessione ha precedenza.

/jev check ./src/example.ts        # verifica singola di un file
/jev status                        # stato completo
/jev help                          # tutti i sottocomandi

In chat, l'LLM può chiamare jev_validate per controlli mirati.

Uso protetto

/jev upstream deepseek deepseek-flash          # crea deepseek/deepseek-flash__jev
/jev upstream openai-codex gpt-5.6-terra       # aggiunge openai-codex/gpt-5.6-terra__jev
/jev twins                                    # elenca attivi e salvati
/jev untwin openai-codex gpt-5.6-terra         # rimuove uno solo
/jev mode automatic                           # seleziona il primario + attiva enforcement

Backend e chiavi

Due backend, stessa policy e stesse regole:

Backend Endpoint Variabile Modello Timeout
openrouter (default) OpenRouter OPENROUTER_API_KEY typesafe/jev-1.13 4000 ms
typesafe SDK ufficiale @typesafe-ai/sdkapi.typesafe.ai TYPESAFE_API_KEY jev-1.13.0 1500 ms
auto sceglie il backend che ha una chiave (entrambe → OpenRouter)
/jev backend                    # stato: backend, modello, chiavi rilevate
/jev backend typesafe           # cambia backend (salvato in jev-config.json)
/jev save-key typesafe          # salva la chiave del backend (file 600)
/jev save-key openrouter        # l'altra chiave resta dov'è
/jev key-file                   # dove viene cercata la chiave

Le chiavi stanno in file separati (jev-api-key.<backend>.txt accanto alla config), quindi i due backend possono coesistere. Precedenza: variabile d'ambiente → file del backend → file generico jev.apiKeyFile → percorso canonico. Vedi config/jev-config.example.json.

Più twin insieme: ogni /jev upstream aggiunge un modello guarded, senza rimuovere i precedenti. I twin vengono registrati anche all'avvio (i modelli dinamici come Codex sono letti dal models-store), quando gli originali sono disponibili. Il primario salvato è il target di auto-selezione e revert; un twin già selezionato nella sessione viene mantenuto al reload, anche se non è il primario.

Nessun login extra: i twin vivono nello stesso provider e riusano la credenziale già configurata (env, stored, OAuth/SSO come Codex business plan). I modelli originali restano intatti e selezionabili.

I twin salvati vengono registrati prima del ripristino del modello anche in on-demand: registrazione e attivazione del gate sono indipendenti. Il suffisso __jev è locale e non viene inviato al provider; anche la cronologia viene normalizzata sull'identità originale senza perdere firme o metadati di continuazione.

Torna normale con /jev mode on-demand, che seleziona anche il modello originale. In automatic, selezionare un modello non protetto lo fa ritornare subito al twin (enforcement a livello selezione). /jev upstream senza argomenti ripristina tutti i twin salvati, /jev untwin P M ne rimuove uno solo, /jev off li rimuove tutti (e salva on-demand nella sessione, selezionando l'originale prima di rimuovere il routing). /jev off conserva il catalogo salvato: al reload gli alias tornano disponibili, ma il gate rimane disattivato.

Se filtri i modelli con enabledModels, usa i glob invece delle voci esplicite, così i twin futuri non restano nascosti:

"enabledModels": ["openai-codex/*__jev", "deepseek/*__jev", "...altri pattern..."]

Dopo aver aggiunto un twin serve /reload (lo scope è risolto all'avvio della sessione). /jev upstream avvisa subito quando il twin appena creato è fuori dal picker. L'installazione punta alla cartella: dopo un aggiornamento del codice basta /reload.

Headless/CI: JEV_MODE=automatic JEV_AUTO_UPSTREAM=provider/model crea e attiva il guarded nelle sessioni senza uno stato Jev salvato.

Un hold [JEV — OUTPUT NON PUBBLICATO] interrompe intenzionalmente il turno: controlla /jev last. Non viene aggirato con un proseguimento automatico.

Verdetto incerto (review): il gate tenta prima una riparazione privata (automatic.repairOnReview, default true), poi applica la policy configurata: policy.onUncertain e policy.onUnavailable ("hold" default, oppure "pass"). Con "pass" il testo viene pubblicato con una nota di trasparenza sul verdetto.

Se un'altra estensione gestisce lo stesso provider (caso reale: pi-accounts con overlay auth su openai-codex), il gate NON ri-registra nulla: applica una patch in-place sull'oggetto provider live (stessa identità, stessa config del proprietario). Così i controlli di ownership altrui non vedono mai una "configuration changed" e non armano abort dei turni (This operation was aborted istantaneo). Quando il proprietario sostituisce l'oggetto, il nuovo viene patchato ex-novo agli hook (input, before_agent_start, turn_start, model_select, message_end); /jev off ripristina i metodi originali senza unregister. I twin restano visibili nel registry per le letture altrui (jev-overlay-reassert solo quando un oggetto nuovo viene patchato). In più, rete di sicurezza: se un id __jev sfugge comunque nel payload, il gate toglie il suffisso registrando jev-gate-bypass e avvisando in TUI, perché quel turno non è passato dalla verifica. Le conferme per comandi di rete sospetti attendono invece una decisione esplicita; la normale sequenza tool → risposta successiva continua senza un nuovo prompt.

Come decide

Jev risponde a domande noul (probabilità 0–1 di "sì, c'è la falla"):

Regola Gate automatic On-demand
sql_injection
unsafe_destructive_action
requirement_conflict — (requisiti chat troppo vaghi) ✓ (requisiti espliciti)

Soglie preliminari calibrate live (docs/calibration-2026-09-19.md): p ≤ 0.20 pass · p ≥ 0.90 block · in mezzo review (trattenuto) · errori/timeout → unavailable (trattenuto). Mai approvazione su input troncato: un pass parziale diventa review dichiarata. I candidati lunghi sono verificati a chunk (copertura completa); i requisiti oltre 16k trattengono con nota.

In automatic è attiva anche una tool policy deterministica (costo zero): blocca comandi distruttivi (rm -rf /, fork bomb, mkfs, dd su device…) e scritture su path sensibili (.env, .ssh/, *.pem, node_modules/). In on-demand non blocca nulla. Sempre in automatic, i comandi con tool di rete (curl, scp, ssh…) passano un check semantico Jev (destructive/exfiltration/beyond_scope/impact): su flag chiede conferma in TUI, in headless solo avviso (fail-open).

Sempre in automatic, il giudice output esamina i risultati bash (1 chiamata Jev per output nuovo, con cache 120s): se trova un secret avvisa e dice di riferirsi al valore per nome; se è un errore, allega il consiglio per la classe (transient → riprova, code_bug → fixa il codice…). Non blocca mai; /jev output mostra l'ultimo verdetto.

Sempre in automatic, il grounding (opt-in, default spento) confronta le affermazioni sulla sessione — "ho letto il file", "i test passano", "ho eseguito X" — con un registro evidenza costruito dai tool_result (comandi, test, letture, modifiche; redatto, max 30 voci, solo sessione corrente). Due domande Jev strette: contraddizione con l'evidenza e affermazione non supportata. Senza evidenza non si giudica: "senza evidenza" non significa "falso". Una modifica riuscita invalida i test precedenti (stale). Tre modalità (/jev grounding off|warn|repair|hold): warn pubblica con nota, repair rigenera una volta, hold trattiene solo le contraddizioni ad alta confidenza. Fail-open: se il verificatore non risponde, il testo passa senza nota. /jev last mostra anche l'esito grounding (warned/repaired/held).

Configurazione

~/.pi/agent/jev-config.json (o $JEV_CONFIG). Env: JEV_MODE, JEV_BACKEND (openrouter|typesafe|auto), JEV_MODEL, JEV_AUTO_UPSTREAM. Vedi config/jev-config.example.json per tutti i campi.

Chiave Jev (ordine: env vince sul file): OPENROUTER_API_KEY o TYPESAFE_API_KEY, altrimenti jev.apiKeyFile (es. "~/.pi/agent/jev-api-key.txt", ~/ espanso, solo prima riga, permessi 600 consigliati). /jev status mostra la sorgente (env:VAR, file:<path>, o il motivo se assente) — mai il valore.

Per salvare la chiave stando dentro pi (senza passarla come argomento, che resterebbe in history e transcript):

/jev save-key   # chiede la chiave via dialogo, la scrive a 600, ricarica

Nota: il dialogo TUI potrebbe fare echo nel terminale; per massima paranoia usa la shell: umask 077 && cat > ~/.pi/agent/jev-api-key.txt.

I verdetti identici sono riusati per 120s (reviewCache, mai gli unavailable): retry e batch paralleli costano una sola chiamata. Gli input oltre i limiti sono marcati …[N chars elided] nel testo inviato a Jev, mai troncati in silenzio.

Retry solo su transienti (jev.retryTransients, default 1 retry): 429/5xx ed errori di connessione ritentano con backoff, timeout e auth mai — un rate-limit non diventa più subito hold. /jev last mostra l'ultimo verdetto del gate (esito, tentativi, controllo con p, backend, tempo).

Sviluppo

npm run typecheck
npm run test:unit            # mock, nessun costo
npm run test:integration
RUN_JEV_LIVE=1 npm run bench:jev   # corpus live (costa poco)
echo '{"requirements":"...","candidate":"..."}' | npm run jev-check

Limiti onesti

  • pass = "supera i controlli configurati", non "corretto". Compilazione e test restano necessari.
  • Il gate copre solo il twin __jev selezionato; in automatic la selezione di altri modelli viene rifiutata con revert al twin. (/jev off ripristina il provider originale intatto.)
  • Soglie calibrate su 20 casi (docs/calibration-2026-09-19.md): buone per iniziare, da rivalutare con corpus più ampio.
  • Jev è un giudice probabilistico, non un verificatore formale.
  • Il grounding controlla solo affermazioni verificabili sulla sessione (comandi, file, test, modifiche): niente fact-check di fatti esterni, e con contesti lunghi l'evidenza oltre budget viene elisa.