pi-jev-guard 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -45,6 +45,9 @@ In chat, l'LLM può chiamare `jev_validate` per controlli mirati.
45
45
 
46
46
  Nessun login extra: il twin vive nello stesso provider e riusa la credenziale già configurata (env, stored, OAuth/SSO come Codex business plan). I modelli originali restano intatti e selezionabili.
47
47
 
48
+ In `automatic` il twin viene registrato già all'avvio (prima del ripristino del
49
+ modello di sessione), così la sessione può riaprire direttamente su `__jev`.
50
+
48
51
  Torna normale con `/model` (modello normale) + `/jev mode on-demand`.
49
52
  In `automatic`, selezionare un modello non protetto lo fa ritornare
50
53
  subito al twin (enforcement a livello selezione).
@@ -1,8 +1,7 @@
1
1
  import type { ExtensionAPI, ExtensionContext, ModelRegistry } from "@earendil-works/pi-coding-agent";
2
2
  import type { Model, Provider } from "@earendil-works/pi-ai";
3
3
  import type { Api } from "@earendil-works/pi-ai";
4
- import { builtinProviders } from "@earendil-works/pi-ai/providers/all";
5
- import { registerJevCommand, type UpstreamResult } from "../src/commands.ts";
4
+ import { builtinProviders } from "@earendil-works/pi-ai/providers/all";import { registerJevCommand, type UpstreamResult } from "../src/commands.ts";
6
5
  import { registerJevAskTool } from "../src/ask.ts";
7
6
  import {
8
7
  configSnapshot,
@@ -22,8 +21,8 @@ import {
22
21
  twinIdFor,
23
22
  } from "../src/automatic/overlay.ts";
24
23
  import type { GateVerdictReport } from "../src/automatic/guardian.ts";
25
- import { DEFAULT_GATE_RULES } from "../src/reviewer.ts";
26
- import { elideWithMarker } from "../src/reviewer.ts";
24
+ import { readStoreModels } from "../src/store-models.ts";
25
+ import { DEFAULT_GATE_RULES, elideWithMarker } from "../src/reviewer.ts";
27
26
  import {
28
27
  TOOL_POLICY_REVISION,
29
28
  checkFilePath,
@@ -331,12 +330,16 @@ export default function (pi: ExtensionAPI) {
331
330
  // provider non lo è e fallirebbe a load (solo installazioni npm).
332
331
  const base = builtinProviders().find((p) => p.id === seedTarget?.provider);
333
332
  if (!base) throw new Error(`provider ${seedTarget.provider} not in builtins`);
334
- // Il catalogo builtin può non avere il modello salvato (es. versioni
335
- // più vecchie senza deepseek-flash): uniamo la definizione statica.
336
- const extra =
337
- seedTarget.provider === "deepseek" && seedTarget.model === "deepseek-flash"
333
+ // Il catalogo builtin può non avere il modello salvato: provider
334
+ // dinamici (es. openai-codex) tengono la lista nel models-store,
335
+ // altri (deepseek-flash su versioni vecchie) in una definizione statica.
336
+ const storeModels = readStoreModels(seedTarget.provider);
337
+ const extra = [
338
+ ...(seedTarget.provider === "deepseek" && seedTarget.model === "deepseek-flash"
338
339
  ? [STATIC_DEEPSEEK_FLASH]
339
- : [];
340
+ : []),
341
+ ...storeModels,
342
+ ];
340
343
  const seedUpstream: Provider = {
341
344
  ...base,
342
345
  getModels: () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-jev-guard",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "type": "module",
5
5
  "description": "Jev validation guard for pi: on-demand tool + automatic provider gate. Dual backend: TypeSafe direct + OpenRouter.",
6
6
  "keywords": [
@@ -46,5 +46,11 @@
46
46
  "typebox": "1.3.34",
47
47
  "typescript": "5.9.3",
48
48
  "vitest": "5.0.1"
49
+ },
50
+ "allowScripts": {
51
+ "@google/genai@1.52.0": true,
52
+ "@openrouter/sdk@1.3.1": true,
53
+ "protobufjs@7.6.6": true,
54
+ "esbuild@0.28.2": true
49
55
  }
50
56
  }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Lettura read-only dei modelli dinamici dal models-store di pi.
3
+ *
4
+ * Alcuni provider (es. openai-codex) non hanno modelli nel catalogo statico:
5
+ * la lista arriva da `<agentDir>/models-store.json` e viene popolata al
6
+ * refresh con le credenziali. Senza questo, il seed all'avvio non troverebbe
7
+ * il modello e il twin non esisterebbe al momento del ripristino sessione.
8
+ *
9
+ * Solo lettura, best-effort: file assente/corrotto → nessun modello.
10
+ */
11
+ import { readFileSync } from "node:fs";
12
+ import { join } from "node:path";
13
+ import { getAgentDir } from "@earendil-works/pi-coding-agent";
14
+ import type { Api, Model } from "@earendil-works/pi-ai";
15
+
16
+ export function modelsStorePath(agentDir?: string): string {
17
+ return join(agentDir ?? getAgentDir(), "models-store.json");
18
+ }
19
+
20
+ function isUsableModel(value: unknown, providerId: string): value is Model<Api> {
21
+ if (typeof value !== "object" || value === null) return false;
22
+ const m = value as Record<string, unknown>;
23
+ return (
24
+ typeof m.id === "string" &&
25
+ m.id.length > 0 &&
26
+ typeof m.api === "string" &&
27
+ m.api.length > 0 &&
28
+ (m.provider === undefined || m.provider === providerId)
29
+ );
30
+ }
31
+
32
+ /** Modelli salvati nello store per un provider, normalizzati sull'id richiesto. */
33
+ export function readStoreModels(providerId: string, agentDir?: string): Model<Api>[] {
34
+ try {
35
+ const raw = JSON.parse(readFileSync(modelsStorePath(agentDir), "utf8")) as Record<
36
+ string,
37
+ { models?: unknown }
38
+ >;
39
+ const entry = raw?.[providerId];
40
+ const models = Array.isArray(entry?.models) ? entry.models : [];
41
+ return models
42
+ .filter((m) => isUsableModel(m, providerId))
43
+ .map((m) => ({ ...(m as Model<Api>), provider: providerId }));
44
+ } catch {
45
+ return [];
46
+ }
47
+ }