dsh-auto-review 0.13.8 → 0.14.0
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/CHANGELOG.md +9 -0
- package/README-es.md +28 -1
- package/README-hi.md +28 -1
- package/README-pt.md +28 -1
- package/README-zh.md +28 -1
- package/README.md +28 -1
- package/lib/eval-cli.js +75 -0
- package/lib/index.js +428 -5
- package/lib/mcp.js +75 -0
- package/lib/types/config.d.ts +108 -0
- package/lib/types/config.d.ts.map +1 -1
- package/lib/types/config.js +107 -0
- package/lib/types/config.js.map +1 -1
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +1 -0
- package/lib/types/index.js.map +1 -1
- package/lib/types/messages.d.ts +1 -0
- package/lib/types/messages.d.ts.map +1 -1
- package/lib/types/messages.js +2 -0
- package/lib/types/messages.js.map +1 -1
- package/lib/types/phocinae.d.ts +184 -0
- package/lib/types/phocinae.d.ts.map +1 -0
- package/lib/types/phocinae.js +391 -0
- package/lib/types/phocinae.js.map +1 -0
- package/lib/types/review.d.ts +25 -2
- package/lib/types/review.d.ts.map +1 -1
- package/lib/types/review.js +32 -3
- package/lib/types/review.js.map +1 -1
- package/lib/types/runtime.d.ts +7 -0
- package/lib/types/runtime.d.ts.map +1 -1
- package/lib/types/runtime.js +12 -2
- package/lib/types/runtime.js.map +1 -1
- package/package.json +2 -2
- package/src/config.ts +200 -0
- package/src/index.ts +1 -0
- package/src/messages.ts +3 -0
- package/src/phocinae.ts +458 -0
- package/src/review.ts +33 -3
- package/src/runtime.ts +16 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.14.0] - 2026-10-25
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **Support for the local non-generative `Phocinae-Largha-150M-v1` decision model as the reviewer.** `reviewerBackend: phocinae` — or just `reviewerModel: Phocinae-Largha-150M-v1`, which infers the backend — routes `ai`-policy reviews to a loopback `phocinae-server` (`POST /v1/systemone`, default `http://127.0.0.1:8155/v1/systemone`) instead of a generative subagent: no API key, no `subagents`/`llm` seam and no network. It is a **veto/triage layer, not a general-purpose approver**: only a `harmless` answer with `answer_confidence >= phocinae.minConfidence` (default 0.4) allows, `destructive` denies, and `risky` / an unsure `harmless` / any transport or protocol failure resolve through `fallbackPolicy` (default `rejected`; `delegate` hands them to the human chain). Reviews are shell commands only — a call without a `command` argument escalates without contacting the model.
|
|
8
|
+
- **The request wording is the measured one, not an improvised prompt.** State is exactly `The agent is about to run the shell command "<command>" in its workspace.`, and the harm options carry the descriptions the model was trained on as `criteria` (the HTTP contract has no `criteria` field). On 89 labelled commands (2026-10-25, CPU fp32, released weights) **0 of 53 destructive commands were allowed at 0.4** — 26 denied, 27 escalated — while 5 of 36 routine commands were allowed; the same measurement at 0.35 let two destructive Windows commands through. The other measured formulations (bare option names, a boolean question, `safe` / `must not run` wording) were rejected because they either collapse to deny-everything or clear destructive commands.
|
|
9
|
+
- **Privacy and egress.** Only the redacted shell command is sent to the decision service — the approval reason, transcript and workspace path are not — and the command passes the same key-level and inline-secret redaction as the generative prompt. `phocinae.allowRemoteEndpoint` must be set explicitly to send it off-host.
|
|
10
|
+
- **Observability and docs.** `/auto-review status` names the reviewer backend and model, and `autoReview/verdict` records `provider: phocinae` (instead of claiming a subagent ran). Full config documentation, an annotated example in `fixtures/config/config-full.yaml`, and the service setup instructions in all five READMEs.
|
|
11
|
+
|
|
3
12
|
## [0.13.8] - 2026-10-22
|
|
4
13
|
|
|
5
14
|
### Fixed
|
package/README-es.md
CHANGED
|
@@ -68,7 +68,7 @@ Cuando una acción cruza el límite del sandbox, un segundo modelo lee la eviden
|
|
|
68
68
|
| Harness | DeepSeek Harness `dsh-v0.2.1-rc.2` (verificado 2026-10-18): `engines.dsh >=0.2.1-0 <0.3.0`; las rutas de compatibilidad P0 tienen pruebas de regresión. |
|
|
69
69
|
| Node | `^22.19.0 \|\| >=24.0.0` |
|
|
70
70
|
| Plataformas | Todas (answerer de host; panel web opcional mediante la capacidad de proyección de sesión) |
|
|
71
|
-
| Modelo |
|
|
71
|
+
| Modelo | Cualquier modelo generativo (el revisor hereda la ruta del agente de sesión; `reviewerModel` la reemplaza), o el modelo de decisión local no generativo `Phocinae-Largha-150M-v1` (`reviewerBackend: phocinae`) |
|
|
72
72
|
|
|
73
73
|
## Qué obtienes
|
|
74
74
|
|
|
@@ -164,6 +164,13 @@ Todas las opciones son campos Schemastery `Config` (modificables desde cordis.ym
|
|
|
164
164
|
| `reviewerPreset` | *(none)* | Id del Agent Preset de solo lectura; obligatorio cuando el host solo expone `startActivation` (0.2.1+) |
|
|
165
165
|
| `reviewerModel` | *(heredado)* | Id del modelo revisor; si no se define, hereda la ruta del agente de sesión |
|
|
166
166
|
| `reviewerModelProvider` | *(heredado)* | Ruta del proveedor LLM para `reviewerModel`; si no se define, hereda el proveedor del agente de sesión |
|
|
167
|
+
| `reviewerBackend` | `agent` | Motor del revisor: `agent` (el revisor generativo) o `phocinae` (el servicio local de decisión no generativo). Sin definir se infiere de `reviewerModel`: un id `Phocinae…` selecciona `phocinae` |
|
|
168
|
+
| `phocinae.endpoint` | `http://127.0.0.1:8155/v1/systemone` | URL del servicio de decisión Phocinae; solo loopback salvo `phocinae.allowRemoteEndpoint` |
|
|
169
|
+
| `phocinae.model` | `Phocinae-Largha-150M-v1` | Nombre del modelo servido; un `reviewerModel` Phocinae lo selecciona automáticamente |
|
|
170
|
+
| `phocinae.timeoutMs` | `5000` | Plazo HTTP por solicitud (un intento, sin reintentos), acotado por `reviewerTimeoutMs` |
|
|
171
|
+
| `phocinae.minConfidence` | `0.4` | Piso de confianza calibrada para un `allow`; un allow dudoso pasa por `fallbackPolicy` en vez de conceder. Medido: 0/53 comandos destructivos permitidos con 0.4; con 0.35 pasaron dos |
|
|
172
|
+
| `phocinae.maxStateChars` | `4096` | Límite del estado de decisión renderizado; un estado truncado puede denegar pero nunca permitir |
|
|
173
|
+
| `phocinae.allowRemoteEndpoint` | `false` | Permitir un endpoint no loopback. El estado lleva el texto de la llamada revisada, así que es una decisión explícita de salida de datos |
|
|
167
174
|
| `reviewerTimeoutMs` | `60000` | Plazo del veredicto; al vencer se aplica la política de fallback |
|
|
168
175
|
| `reviewerTools` | `[read, glob, grep]` | Lista blanca de herramientas del hijo revisor (debe ser no vacía) |
|
|
169
176
|
| `fallbackPolicy` | `rejected` | Ante fallo del revisor: `rejected` (cierre en fallo) / `delegate` / `allow-once` |
|
|
@@ -182,6 +189,26 @@ Todas las opciones son campos Schemastery `Config` (modificables desde cordis.ym
|
|
|
182
189
|
| `language` | `en` | Idioma de la UI de la salida del comando `/auto-review` (`en` \| `zh`) |
|
|
183
190
|
| `allowUnmarkedAudit` | `false` | Fuerza la auditoría del registro de sesión en hosts que descartan el marcador `ignorable` (peligroso: los eventos sin marcar hacen las sesiones irrecuperables en otros hosts); por defecto se detecta y se degrada (adaptado el 2026-09-02, reverificado contra `0.1.5-rc.2` el 2026-09-11) |
|
|
184
191
|
|
|
192
|
+
### Revisor local con modelo de decisión (Phocinae, opcional)
|
|
193
|
+
|
|
194
|
+
`reviewerBackend: phocinae` sustituye el revisor generativo por el modelo de decisión local y no generativo **Phocinae-Largha-150M-v1**, servido por [`phocinae-server`](https://github.com/Phocinae/phocinae-server) (`POST /v1/systemone`). El modelo responde una pregunta tipada y nunca genera texto, así que una revisión **no necesita clave de API, ni la costura `subagents`/`llm`, ni red**: una pasada (≈21 ms en caliente con GPU, ≈1,5 s en CPU monohilo) devuelve una etiqueta de daño con confianza calibrada.
|
|
195
|
+
|
|
196
|
+
```yaml
|
|
197
|
+
config:
|
|
198
|
+
toolsPolicy: { overrides: { bash: ai } }
|
|
199
|
+
reviewerModel: Phocinae-Largha-150M-v1 # esto solo ya selecciona el backend Phocinae
|
|
200
|
+
phocinae:
|
|
201
|
+
endpoint: http://127.0.0.1:8155/v1/systemone
|
|
202
|
+
minConfidence: 0.4
|
|
203
|
+
fallbackPolicy: delegate # opcional: envía lo dudoso a la cadena humana
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**Es una capa de veto/triaje, no un aprobador general.** Solo un `harmless` con `answer_confidence >= phocinae.minConfidence` (por defecto 0.4) permite. `destructive` deniega; `risky`, un `harmless` dudoso y un servicio inalcanzable o malformado pasan por `fallbackPolicy` (por defecto `rejected`, cierre en fallo; `delegate` los envía a la cadena humana). El revisor solo pregunta por **comandos de shell**: un `write`/`edit` u otra llamada sin argumento `command` escala sin contactar al modelo.
|
|
207
|
+
|
|
208
|
+
El estado es exactamente `The agent is about to run the shell command "<command>" in its workspace.`, con opciones descritas (los `criteria` con los que se entrenó el modelo, renderizados en el texto de la opción porque el contrato HTTP no tiene campo `criteria`). Medido sobre 89 comandos etiquetados (53 destructivos, 36 rutinarios) (2026-10-25, CPU fp32, pesos publicados): **0 de 53 comandos destructivos permitidos con `0.4`** — 26 denegados y 27 escalados — y 5 de 36 comandos rutinarios permitidos; bajar el piso a 0.35 dejó pasar dos comandos destructivos de Windows. La razón de aprobación, la transcripción y la ruta del workspace **no** se envían, y el comando pasa por la misma redacción de claves y secretos en línea que el prompt generativo. El endpoint es solo loopback salvo `phocinae.allowRemoteEndpoint: true`. El backend se aplica solo a las revisiones de aprobación; la capa de revisión de `dsh-eval` sigue usando un modelo generativo.
|
|
209
|
+
|
|
210
|
+
Inicia el servicio una vez: `pip install phocinae-server` (o ejecuta el checkout), descarga los pesos desde la model card y ejecuta `PHOC_MODEL_DIR=<weights-dir> python -m phocinae.main` — escucha en `127.0.0.1:8155`; verifica con `curl http://127.0.0.1:8155/health`.
|
|
211
|
+
|
|
185
212
|
Ejemplo (forma completa anotada: `fixtures/config/config-full.yaml`):
|
|
186
213
|
|
|
187
214
|
```yaml
|
package/README-hi.md
CHANGED
|
@@ -68,7 +68,7 @@ DeepSeek Harness के लिए द्वितीय-मॉडल AI अन
|
|
|
68
68
|
| हारनेस | DeepSeek Harness `dsh-v0.2.1-rc.2` (2026-10-18 सत्यापित): `engines.dsh >=0.2.1-0 <0.3.0`; P0 संगतता पथ के रिग्रेशन परीक्षण हैं। |
|
|
69
69
|
| Node | `^22.19.0 \|\| >=24.0.0` |
|
|
70
70
|
| प्लेटफ़ॉर्म | सभी (होस्ट answerer; सत्र-प्रोजेक्शन क्षमता के ज़रिए वैकल्पिक वेब समीक्षा पैनल) |
|
|
71
|
-
| मॉडल | कोई भी (समीक्षक सत्र एजेंट का मार्ग विरासत में लेता है; `reviewerModel` उसे बदल देता है) |
|
|
71
|
+
| मॉडल | कोई भी जनरेटिव मॉडल (समीक्षक सत्र एजेंट का मार्ग विरासत में लेता है; `reviewerModel` उसे बदल देता है), या स्थानीय गैर-जनरेटिव `Phocinae-Largha-150M-v1` निर्णय मॉडल (`reviewerBackend: phocinae`) |
|
|
72
72
|
|
|
73
73
|
## आपको क्या मिलता है
|
|
74
74
|
|
|
@@ -164,6 +164,13 @@ No recent denial #2 in this session (0 recorded).
|
|
|
164
164
|
| `reviewerPreset` | *(none)* | केवल-पढ़ने वाला Agent Preset id; जब होस्ट केवल `startActivation` (0.2.1+) देता है तब आवश्यक |
|
|
165
165
|
| `reviewerModel` | *(विरासत)* | समीक्षक मॉडल id; खाली होने पर सत्र एजेंट का मार्ग लेता है |
|
|
166
166
|
| `reviewerModelProvider` | *(विरासत)* | `reviewerModel` के लिए LLM प्रदाता मार्ग; खाली होने पर सत्र एजेंट का प्रदाता लेता है |
|
|
167
|
+
| `reviewerBackend` | `agent` | समीक्षक इंजन: `agent` (जनरेटिव समीक्षक) या `phocinae` (स्थानीय गैर-जनरेटिव निर्णय सेवा)। खाली होने पर `reviewerModel` से अनुमान: `Phocinae…` id `phocinae` चुनता है |
|
|
168
|
+
| `phocinae.endpoint` | `http://127.0.0.1:8155/v1/systemone` | Phocinae निर्णय-सेवा URL; `phocinae.allowRemoteEndpoint` के बिना केवल loopback |
|
|
169
|
+
| `phocinae.model` | `Phocinae-Largha-150M-v1` | परोसा गया मॉडल नाम; Phocinae `reviewerModel` इसे स्वतः चुनता है |
|
|
170
|
+
| `phocinae.timeoutMs` | `5000` | प्रति-अनुरोध HTTP समय-सीमा (एक प्रयास, कोई retry नहीं), `reviewerTimeoutMs` से सीमित |
|
|
171
|
+
| `phocinae.minConfidence` | `0.4` | `allow` के लिए कैलिब्रेटेड कॉन्फ़िडेंस की न्यूनतम सीमा; अनिश्चित allow अनुमति देने के बजाय `fallbackPolicy` से जाता है। मापा गया: 0.4 पर 53 में से 0 विनाशकारी कमांड अनुमत; 0.35 पर दो निकल गए |
|
|
172
|
+
| `phocinae.maxStateChars` | `4096` | रेंडर किए गए निर्णय-स्थिति की सीमा; कटी हुई स्थिति अस्वीकार कर सकती है पर कभी अनुमति नहीं दे सकती |
|
|
173
|
+
| `phocinae.allowRemoteEndpoint` | `false` | गैर-loopback endpoint की अनुमति। स्थिति में समीक्षित कॉल का टेक्स्ट होता है, इसलिए यह स्पष्ट डेटा-निर्यात निर्णय है |
|
|
167
174
|
| `reviewerTimeoutMs` | `60000` | निर्णय की समय-सीमा; समाप्ति पर fallback नीति लागू होती है |
|
|
168
175
|
| `reviewerTools` | `[read, glob, grep]` | समीक्षक चाइल्ड की टूल अनुमति-सूची (गैर-रिक्त होनी चाहिए) |
|
|
169
176
|
| `fallbackPolicy` | `rejected` | समीक्षक विफलता: `rejected` (विफल-बंद) / `delegate` / `allow-once` |
|
|
@@ -182,6 +189,26 @@ No recent denial #2 in this session (0 recorded).
|
|
|
182
189
|
| `language` | `en` | `/auto-review` कमांड आउटपुट की UI भाषा (`en` \| `zh`) |
|
|
183
190
|
| `allowUnmarkedAudit` | `false` | `ignorable` मार्कर छोड़ने वाले होस्ट पर सत्र-लॉग ऑडिट बलपूर्वक चालू करें (खतरनाक: बिना मार्कर वाली घटनाएँ अन्य होस्ट पर सत्र अप्राप्य बनाती हैं); डिफ़ॉल्ट पहचान-और-डिग्रेड है (2026-09-02 को अनुकूलित, 2026-09-11 को `0.1.5-rc.2` के विरुद्ध पुनः सत्यापित) |
|
|
184
191
|
|
|
192
|
+
### स्थानीय निर्णय-मॉडल समीक्षक (Phocinae, वैकल्पिक)
|
|
193
|
+
|
|
194
|
+
`reviewerBackend: phocinae` जनरेटिव समीक्षक की जगह स्थानीय, गैर-जनरेटिव **Phocinae-Largha-150M-v1** निर्णय मॉडल लेता है, जो [`phocinae-server`](https://github.com/Phocinae/phocinae-server) द्वारा परोसा जाता है (`POST /v1/systemone`)। मॉडल एक टाइप्ड प्रश्न का उत्तर देता है और कभी टेक्स्ट नहीं बनाता, इसलिए समीक्षा के लिए **API कुंजी, `subagents`/`llm` सीम, या नेटवर्क की ज़रूरत नहीं**: एक फ़ॉरवर्ड (GPU पर गर्म ≈21 ms, CPU सिंगल-थ्रेड ≈1.5 s) कैलिब्रेटेड कॉन्फ़िडेंस के साथ हानि-लेबल लौटाता है।
|
|
195
|
+
|
|
196
|
+
```yaml
|
|
197
|
+
config:
|
|
198
|
+
toolsPolicy: { overrides: { bash: ai } }
|
|
199
|
+
reviewerModel: Phocinae-Largha-150M-v1 # केवल यही Phocinae बैकएंड चुनता है
|
|
200
|
+
phocinae:
|
|
201
|
+
endpoint: http://127.0.0.1:8155/v1/systemone
|
|
202
|
+
minConfidence: 0.4
|
|
203
|
+
fallbackPolicy: delegate # वैकल्पिक: संदिग्ध अनुरोध मानव श्रृंखला को
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**यह एक वीटो/ट्रायाज पर्दा है, सामान्य अनुमोदक नहीं।** केवल `harmless` और `answer_confidence >= phocinae.minConfidence` (डिफ़ॉल्ट 0.4) अनुमति देता है। `destructive` अस्वीकार करता है; `risky`, अनिश्चित `harmless`, और अनुपलब्ध या विकृत सेवा `fallbackPolicy` से जाते हैं (डिफ़ॉल्ट विफल-बंद `rejected`; `delegate` उन्हें मानव श्रृंखला को भेजता है)। समीक्षक केवल **shell कमांड** के बारे में पूछता है: `write`/`edit` या `command` तर्क के बिना कोई भी कॉल मॉडल से पूछे बिना एस्केलेट होता है।
|
|
207
|
+
|
|
208
|
+
स्थिति पाठ ठीक `The agent is about to run the shell command "<command>" in its workspace.` है, विकल्प विवरण के साथ (मॉडल के प्रशिक्षण `criteria` विकल्प पाठ में रेंडर किए गए, क्योंकि HTTP अनुबंध में `criteria` फ़ील्ड नहीं है)। 89 लेबल वाले कमांड (53 विनाशकारी, 36 नियमित) पर मापा गया (2026-10-25, CPU fp32, प्रकाशित वेट्स): **`0.4` पर 53 में से 0 विनाशकारी कमांड अनुमत** — 26 अस्वीकृत और 27 एस्केलेट — जबकि 36 में से 5 नियमित कमांड अनुमत; सीमा 0.35 करने पर दो Windows विनाशकारी कमांड निकल गए। अनुमोदन कारण, ट्रांसक्रिप्ट और वर्कस्पेस पथ **नहीं** भेजे जाते, और कमांड जनरेटिव प्रॉम्प्ट जैसे ही कुंजी/इनलाइन-सीक्रेट रिडैक्शन से गुज़रता है। एंडपॉइंट केवल loopback है जब तक `phocinae.allowRemoteEndpoint: true` न हो। यह बैकएंड केवल अनुमोदन समीक्षाओं पर लागू होता है; `dsh-eval` की समीक्षा परत अभी भी जनरेटिव मॉडल वापस करती है।
|
|
209
|
+
|
|
210
|
+
सेवा एक बार शुरू करें: `pip install phocinae-server` (या चेकआउट चलाएँ), मॉडल कार्ड से वेट्स डाउनलोड करें, फिर `PHOC_MODEL_DIR=<weights-dir> python -m phocinae.main` — यह `127.0.0.1:8155` पर सुनता है; `curl http://127.0.0.1:8155/health` से जाँचें।
|
|
211
|
+
|
|
185
212
|
उदाहरण (टिप्पणी-युक्त पूर्ण रूप: `fixtures/config/config-full.yaml`):
|
|
186
213
|
|
|
187
214
|
```yaml
|
package/README-pt.md
CHANGED
|
@@ -60,7 +60,7 @@ Quando uma ação cruza a fronteira do sandbox, um segundo modelo lê as evidên
|
|
|
60
60
|
| Harness | DeepSeek Harness `dsh-v0.2.1-rc.2` (verificado 2026-10-18): `engines.dsh >=0.2.1-0 <0.3.0`; os caminhos de compatibilidade P0 têm testes de regressão. |
|
|
61
61
|
| Node | `^22.19.0 \|\| >=24.0.0` |
|
|
62
62
|
| Plataformas | Todas (answerer de host; painel web opcional via capacidade de projeção de sessão) |
|
|
63
|
-
| Modelo | Qualquer (o revisor herda a rota do agente da sessão; `reviewerModel` sobrescreve) |
|
|
63
|
+
| Modelo | Qualquer modelo generativo (o revisor herda a rota do agente da sessão; `reviewerModel` sobrescreve), ou o modelo de decisão local não generativo `Phocinae-Largha-150M-v1` (`reviewerBackend: phocinae`) |
|
|
64
64
|
|
|
65
65
|
## O que você recebe
|
|
66
66
|
|
|
@@ -164,6 +164,13 @@ Todos os ajustes são campos Schemastery `Config` (alteráveis no cordis.yml). U
|
|
|
164
164
|
| `reviewerPreset` | *(none)* | Id do Agent Preset somente leitura; obrigatório quando o host expõe apenas `startActivation` (0.2.1+) |
|
|
165
165
|
| `reviewerModel` | *(herdado)* | Id do modelo revisor; se vazio, herda a rota do agente da sessão |
|
|
166
166
|
| `reviewerModelProvider` | *(herdado)* | Rota do provedor LLM para `reviewerModel`; se vazio, herda o provedor do agente da sessão |
|
|
167
|
+
| `reviewerBackend` | `agent` | Motor do revisor: `agent` (o revisor generativo) ou `phocinae` (o serviço local de decisão não generativo). Sem definir, é inferido de `reviewerModel`: um id `Phocinae…` seleciona `phocinae` |
|
|
168
|
+
| `phocinae.endpoint` | `http://127.0.0.1:8155/v1/systemone` | URL do serviço de decisão Phocinae; somente loopback salvo `phocinae.allowRemoteEndpoint` |
|
|
169
|
+
| `phocinae.model` | `Phocinae-Largha-150M-v1` | Nome do modelo servido; um `reviewerModel` Phocinae o seleciona automaticamente |
|
|
170
|
+
| `phocinae.timeoutMs` | `5000` | Prazo HTTP por requisição (uma tentativa, sem retry), limitado por `reviewerTimeoutMs` |
|
|
171
|
+
| `phocinae.minConfidence` | `0.4` | Piso de confiança calibrada para um `allow`; um allow inseguro passa pelo `fallbackPolicy` em vez de conceder. Medido: 0/53 comandos destrutivos permitidos com 0.4; com 0.35 dois passaram |
|
|
172
|
+
| `phocinae.maxStateChars` | `4096` | Limite do estado de decisão renderizado; um estado truncado pode negar mas nunca permitir |
|
|
173
|
+
| `phocinae.allowRemoteEndpoint` | `false` | Permitir endpoint não loopback. O estado carrega o texto da chamada revisada, então é uma decisão explícita de saída de dados |
|
|
167
174
|
| `reviewerTimeoutMs` | `60000` | Prazo do veredito; ao expirar, aplica-se a política de fallback |
|
|
168
175
|
| `reviewerTools` | `[read, glob, grep]` | Lista de permissões de ferramentas do filho revisor (deve ser não vazia) |
|
|
169
176
|
| `fallbackPolicy` | `rejected` | Falha do revisor: `rejected` (falha fechada) / `delegate` / `allow-once` |
|
|
@@ -182,6 +189,26 @@ Todos os ajustes são campos Schemastery `Config` (alteráveis no cordis.yml). U
|
|
|
182
189
|
| `language` | `en` | Idioma da UI da saída do comando `/auto-review` (`en` \| `zh`) |
|
|
183
190
|
| `allowUnmarkedAudit` | `false` | Força a auditoria do registro de sessão em hosts que descartam o marcador `ignorable` (perigoso: eventos sem marcador tornam sessões irrecuperáveis em outros hosts); o padrão é detectar e degradar (adaptado em 2026-09-02, reverificado contra `0.1.5-rc.2` em 2026-09-11) |
|
|
184
191
|
|
|
192
|
+
### Revisor local com modelo de decisão (Phocinae, opcional)
|
|
193
|
+
|
|
194
|
+
`reviewerBackend: phocinae` troca o revisor generativo pelo modelo de decisão local e não generativo **Phocinae-Largha-150M-v1**, servido pelo [`phocinae-server`](https://github.com/Phocinae/phocinae-server) (`POST /v1/systemone`). O modelo responde uma pergunta tipada e nunca gera texto, então uma revisão **não precisa de chave de API, nem da costura `subagents`/`llm`, nem de rede**: um forward (≈21 ms a quente em GPU, ≈1,5 s em CPU single-thread) devolve um rótulo de dano com confiança calibrada.
|
|
195
|
+
|
|
196
|
+
```yaml
|
|
197
|
+
config:
|
|
198
|
+
toolsPolicy: { overrides: { bash: ai } }
|
|
199
|
+
reviewerModel: Phocinae-Largha-150M-v1 # só isto já seleciona o backend Phocinae
|
|
200
|
+
phocinae:
|
|
201
|
+
endpoint: http://127.0.0.1:8155/v1/systemone
|
|
202
|
+
minConfidence: 0.4
|
|
203
|
+
fallbackPolicy: delegate # opcional: envia o duvidoso à cadeia humana
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**É uma camada de veto/triagem, não um aprovador geral.** Só um `harmless` com `answer_confidence >= phocinae.minConfidence` (padrão 0.4) permite. `destructive` nega; `risky`, um `harmless` inseguro e um serviço inalcançável ou malformado passam pelo `fallbackPolicy` (padrão `rejected`, fecha em falha; `delegate` os envia à cadeia humana). O revisor só pergunta sobre **comandos de shell**: um `write`/`edit` ou outra chamada sem argumento `command` escala sem contatar o modelo.
|
|
207
|
+
|
|
208
|
+
O estado é exatamente `The agent is about to run the shell command "<command>" in its workspace.`, com opções descritas (os `criteria` com que o modelo foi treinado, renderizados no texto da opção porque o contrato HTTP não tem campo `criteria`). Medido em 89 comandos rotulados (53 destrutivos, 36 rotineiros) (2026-10-25, CPU fp32, pesos publicados): **0 de 53 comandos destrutivos permitidos com `0.4`** — 26 negados e 27 escalados — e 5 de 36 comandos rotineiros permitidos; baixar o piso para 0.35 deixou passar dois comandos destrutivos do Windows. O motivo da aprovação, a transcrição e o caminho do workspace **não** são enviados, e o comando passa pela mesma redação de chaves e segredos inline do prompt generativo. O endpoint é somente loopback salvo `phocinae.allowRemoteEndpoint: true`. O backend vale apenas para revisões de aprovação; a camada de revisão do `dsh-eval` continua usando um modelo generativo.
|
|
209
|
+
|
|
210
|
+
Inicie o serviço uma vez: `pip install phocinae-server` (ou rode o checkout), baixe os pesos no model card e execute `PHOC_MODEL_DIR=<weights-dir> python -m phocinae.main` — ele escuta em `127.0.0.1:8155`; verifique com `curl http://127.0.0.1:8155/health`.
|
|
211
|
+
|
|
185
212
|
Exemplo (forma completa anotada: `fixtures/config/config-full.yaml`):
|
|
186
213
|
|
|
187
214
|
```yaml
|
package/README-zh.md
CHANGED
|
@@ -60,7 +60,7 @@ DeepSeek Harness 的第二模型 AI 审批 —— 一个只读审查子代理在
|
|
|
60
60
|
| 宿主 | DeepSeek Harness `dsh-v0.2.1-rc.2`(2026-10-18 验证):`engines.dsh >=0.2.1-0 <0.3.0`;P0 兼容路径已有回归测试。 |
|
|
61
61
|
| Node | `^22.19.0 \|\| >=24.0.0` |
|
|
62
62
|
| 平台 | 全部(宿主 answerer;可选 Web 审查面板,依赖会话投影能力) |
|
|
63
|
-
| 模型 |
|
|
63
|
+
| 模型 | 任意生成式模型(审查器默认继承会话代理的路由;`reviewerModel` 可覆盖),或本机非生成式的 `Phocinae-Largha-150M-v1` 决策模型(`reviewerBackend: phocinae`) |
|
|
64
64
|
|
|
65
65
|
## 你能获得什么
|
|
66
66
|
|
|
@@ -164,6 +164,13 @@ No recent denial #2 in this session (0 recorded).
|
|
|
164
164
|
| `reviewerPreset` | *(none)* | 只读 Agent Preset id;宿主只提供 `startActivation`(0.2.1+)时必填 |
|
|
165
165
|
| `reviewerModel` | *(继承)* | 审查器模型 id;不设置则继承会话代理的路由 |
|
|
166
166
|
| `reviewerModelProvider` | *(继承)* | `reviewerModel` 的 LLM provider 路由;不设置则继承会话代理的 provider |
|
|
167
|
+
| `reviewerBackend` | `agent` | 审查引擎:`agent`(生成式审查器)或 `phocinae`(本机非生成式决策服务)。不设置时由 `reviewerModel` 推断:`Phocinae…` 模型 id 会选择 `phocinae` |
|
|
168
|
+
| `phocinae.endpoint` | `http://127.0.0.1:8155/v1/systemone` | Phocinae 决策服务地址;除设置 `phocinae.allowRemoteEndpoint` 外仅允许 loopback |
|
|
169
|
+
| `phocinae.model` | `Phocinae-Largha-150M-v1` | 服务端模型名;`reviewerModel` 为 Phocinae 模型时自动采用 |
|
|
170
|
+
| `phocinae.timeoutMs` | `5000` | 单次 HTTP 截止时间(只尝试一次,不重试),受 `reviewerTimeoutMs` 约束 |
|
|
171
|
+
| `phocinae.minConfidence` | `0.4` | 放行所需的校准置信度下限;置信度不足的 allow 会走 `fallbackPolicy`,不会放行。实测:0.4 下 53 条破坏性命令 0 条被放行,0.35 时有两条通过 |
|
|
172
|
+
| `phocinae.maxStateChars` | `4096` | 决策状态渲染上限;被截断的状态可以拒绝,但永远不会放行 |
|
|
173
|
+
| `phocinae.allowRemoteEndpoint` | `false` | 允许非 loopback 端点。状态里含被审调用的文本,所以这是一次显式的数据出境决定 |
|
|
167
174
|
| `reviewerTimeoutMs` | `60000` | 裁决截止时间;超时后应用 fallback 策略 |
|
|
168
175
|
| `reviewerTools` | `[read, glob, grep]` | 审查器子代理的工具白名单(必须非空) |
|
|
169
176
|
| `fallbackPolicy` | `rejected` | 审查器失败时的策略:`rejected`(失败关闭)/ `delegate` / `allow-once` |
|
|
@@ -182,6 +189,26 @@ No recent denial #2 in this session (0 recorded).
|
|
|
182
189
|
| `language` | `en` | `/auto-review` 命令输出的界面语言(`en` \| `zh`) |
|
|
183
190
|
| `allowUnmarkedAudit` | `false` | 强制在丢弃 `ignorable` 标记的宿主上写入会话审计(危险:未标记事件会让会话在其他宿主上无法恢复);默认自动探测并降级(2026-09-02 适配,2026-09-11 针对 `0.1.5-rc.2` 复核) |
|
|
184
191
|
|
|
192
|
+
### 本地决策模型审查器(Phocinae,可选)
|
|
193
|
+
|
|
194
|
+
`reviewerBackend: phocinae` 会把生成式审查器换成由 [`phocinae-server`](https://github.com/Phocinae/phocinae-server) 提供的本机非生成式 **Phocinae-Largha-150M-v1** 决策模型(`POST /v1/systemone`)。模型只回答一个类型化问题,不生成任何 token,因此审查**不需要 API key、不需要 `subagents`/`llm` 接缝、也不需要网络**:一次前向(GPU 热态约 21 ms,CPU 单线程约 1.5 s)返回带校准置信度的危害标签。
|
|
195
|
+
|
|
196
|
+
```yaml
|
|
197
|
+
config:
|
|
198
|
+
toolsPolicy: { overrides: { bash: ai } }
|
|
199
|
+
reviewerModel: Phocinae-Largha-150M-v1 # 仅这一项即可选中 Phocinae 后端
|
|
200
|
+
phocinae:
|
|
201
|
+
endpoint: http://127.0.0.1:8155/v1/systemone
|
|
202
|
+
minConfidence: 0.4
|
|
203
|
+
fallbackPolicy: delegate # 可选:把 risky/不确定的请求交给人类链
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**它是否决/分级层,不是通用放行者。** 只有 `harmless` 且 `answer_confidence >= phocinae.minConfidence`(默认 0.4)才会放行。`destructive` 直接拒绝;`risky`、置信度不足的 `harmless`、服务不可达或响应不合法都走 `fallbackPolicy`(默认失败关闭的 `rejected`;设为 `delegate` 则交给人类链)。审查只针对 **shell 命令**:`write`/`edit` 等没有 `command` 参数的调用会直接升级,不会询问模型。
|
|
207
|
+
|
|
208
|
+
状态文本就是 `The agent is about to run the shell command "<command>" in its workspace.`,选项带描述(把模型训练时的 `criteria` 渲染进选项文本,因为 HTTP 契约没有 `criteria` 字段)。在 89 条带标签命令(53 条破坏性、36 条常规)上实测(2026-10-25,CPU fp32,发布权重):**`0.4` 下 53 条破坏性命令 0 条被放行**——26 条拒绝、27 条升级——36 条常规命令放行 5 条;把下限降到 0.35 会有两条 Windows 破坏性命令通过。审批理由、转录和工作区路径**不会**发送;命令本身要经过与生成式提示词相同的键名级与内联密钥脱敏。端点默认仅允许 loopback,除非设置 `phocinae.allowRemoteEndpoint: true`。该后端只作用于审批审查;`dsh-eval` 的审查层仍使用生成式模型。
|
|
209
|
+
|
|
210
|
+
启动服务:`pip install phocinae-server`(或直接运行仓库),从模型卡下载权重,然后 `PHOC_MODEL_DIR=<weights-dir> python -m phocinae.main`——它监听 `127.0.0.1:8155`;用 `curl http://127.0.0.1:8155/health` 验证。
|
|
211
|
+
|
|
185
212
|
示例(完整注释形式见 `fixtures/config/config-full.yaml`):
|
|
186
213
|
|
|
187
214
|
```yaml
|
package/README.md
CHANGED
|
@@ -70,7 +70,7 @@ When an action crosses the sandbox boundary, a second model reads the evidence a
|
|
|
70
70
|
| Harness | DeepSeek Harness `dsh-v0.2.1-rc.2` (verified 2026-10-18): `engines.dsh >=0.2.1-0 <0.3.0`; the P0 compatibility paths have regression tests. |
|
|
71
71
|
| Node | `^22.19.0 \|\| >=24.0.0` |
|
|
72
72
|
| Platforms | All (host answerer; optional Web review panel via the session-projection capability) |
|
|
73
|
-
| Model | Any (the reviewer inherits the session agent's route; `reviewerModel` overrides) |
|
|
73
|
+
| Model | Any generative model (the reviewer inherits the session agent's route; `reviewerModel` overrides), or the local non-generative `Phocinae-Largha-150M-v1` decision model (`reviewerBackend: phocinae`) |
|
|
74
74
|
|
|
75
75
|
## What you get
|
|
76
76
|
|
|
@@ -166,6 +166,13 @@ All tunables are Schemastery `Config` fields (changeable from cordis.yml). An id
|
|
|
166
166
|
| `reviewerPreset` | *(none)* | Read-only Agent Preset id; required when the host exposes `startActivation` (0.2.1+) instead of `toolFilter` |
|
|
167
167
|
| `reviewerModel` | *(inherit)* | Reviewer model id; unset inherits the session agent's route |
|
|
168
168
|
| `reviewerModelProvider` | *(inherit)* | LLM provider route for `reviewerModel`; unset inherits the session agent's provider |
|
|
169
|
+
| `reviewerBackend` | `agent` | Reviewer engine: `agent` (the generative reviewer) or `phocinae` (the local non-generative decision service). Unset infers it from `reviewerModel`: a `Phocinae…` id selects `phocinae` |
|
|
170
|
+
| `phocinae.endpoint` | `http://127.0.0.1:8155/v1/systemone` | Phocinae decision-service URL; loopback only unless `phocinae.allowRemoteEndpoint` is set |
|
|
171
|
+
| `phocinae.model` | `Phocinae-Largha-150M-v1` | Served model name; a Phocinae `reviewerModel` selects it automatically |
|
|
172
|
+
| `phocinae.timeoutMs` | `5000` | Per-request HTTP deadline (one attempt, no retry), bounded by `reviewerTimeoutMs` |
|
|
173
|
+
| `phocinae.minConfidence` | `0.4` | Calibrated-confidence floor for an `allow`; an unsure allow resolves through `fallbackPolicy` instead of granting. Measured: 0/53 destructive commands allowed at 0.4, while 0.35 let two through |
|
|
174
|
+
| `phocinae.maxStateChars` | `4096` | Cap on the rendered decision state; a truncated state can deny but never allow |
|
|
175
|
+
| `phocinae.allowRemoteEndpoint` | `false` | Permit a non-loopback endpoint. The state carries the reviewed call's text, so this is an explicit egress decision |
|
|
169
176
|
| `reviewerTimeoutMs` | `60000` | Verdict deadline; on expiry the fallback policy applies |
|
|
170
177
|
| `reviewerTools` | `[read, glob, grep]` | The reviewer child's tool allow-list (must be non-empty) |
|
|
171
178
|
| `fallbackPolicy` | `rejected` | Reviewer failure: `rejected` (fail closed) / `delegate` / `allow-once` |
|
|
@@ -184,6 +191,26 @@ All tunables are Schemastery `Config` fields (changeable from cordis.yml). An id
|
|
|
184
191
|
| `language` | `en` | UI language of the `/auto-review` command output (`en` \| `zh`) |
|
|
185
192
|
| `allowUnmarkedAudit` | `false` | Force session-log audit on hosts that drop the `ignorable` marker or fail-closed on unknown event types (host `0.1.2-rc.1`+) (dangerous: unmarked events make sessions unresumable elsewhere); default is detect-and-degrade (adapted 2026-09-02, re-verified against `0.1.5-rc.2` on 2026-09-11): the session envelope keeps its ignorable field for stored-log read compatibility only - Session.append still cannot stamp it, so audit-gate behavior is unchanged. |
|
|
186
193
|
|
|
194
|
+
### Local decision-model reviewer (Phocinae, optional)
|
|
195
|
+
|
|
196
|
+
`reviewerBackend: phocinae` swaps the generative reviewer for the local, non-generative **Phocinae-Largha-150M-v1** decision model served by [`phocinae-server`](https://github.com/Phocinae/phocinae-server) (`POST /v1/systemone`). The model answers one typed question and never generates text, so a review needs **no API key, no `subagents`/`llm` seam, and no network**: one forward pass (≈21 ms warm on a GPU, ≈1.5 s single-threaded on CPU) returns a harm label with a calibrated confidence.
|
|
197
|
+
|
|
198
|
+
```yaml
|
|
199
|
+
config:
|
|
200
|
+
toolsPolicy: { overrides: { bash: ai } }
|
|
201
|
+
reviewerModel: Phocinae-Largha-150M-v1 # this alone selects the Phocinae backend
|
|
202
|
+
phocinae:
|
|
203
|
+
endpoint: http://127.0.0.1:8155/v1/systemone
|
|
204
|
+
minConfidence: 0.4
|
|
205
|
+
fallbackPolicy: delegate # optional: hand risky/unsure requests to the human chain
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
**It is a veto/triage layer, not a general-purpose approver.** Only a `harmless` answer with `answer_confidence >= phocinae.minConfidence` allows. `destructive` denies; `risky`, an unsure `harmless`, and an unreachable or malformed service resolve through `fallbackPolicy` (default fail-closed `rejected`; set `delegate` to send them to the human chain). The reviewer only asks about **shell commands**: a `write`/`edit` or any other call without a `command` argument escalates without contacting the model.
|
|
209
|
+
|
|
210
|
+
The state is exactly `The agent is about to run the shell command "<command>" in its workspace.` with described options (the model's trained `criteria` rendered into the option text, because the HTTP contract has no `criteria` field). Measured on 89 labelled commands (2026-10-25, CPU fp32, released weights): **0 of 53 destructive commands were allowed at `0.4`** — 26 were denied and 27 escalated — while 5 of 36 routine commands were allowed; lowering the floor to 0.35 let two destructive Windows commands through. The approval reason, transcript and workspace path are **not** sent, and the command passes the same key-level and inline-secret redaction as the generative prompt. The endpoint is loopback-only unless `phocinae.allowRemoteEndpoint: true`. The backend applies to approval reviews only; `dsh-eval`'s review layer still uses a generative model.
|
|
211
|
+
|
|
212
|
+
Start the service once: `pip install phocinae-server` (or run the checkout), download the weights from the model card, then `PHOC_MODEL_DIR=<weights-dir> python -m phocinae.main` — it listens on `127.0.0.1:8155`; verify with `curl http://127.0.0.1:8155/health`.
|
|
213
|
+
|
|
187
214
|
Example (annotated full form: `fixtures/config/config-full.yaml`):
|
|
188
215
|
|
|
189
216
|
```yaml
|
package/lib/eval-cli.js
CHANGED
|
@@ -6,6 +6,23 @@ import { tmpdir } from "node:os";
|
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { boot, installFailLoud, loadEnv, resolveConfigPath } from "@deepseek-ai/dsh-app-boot";
|
|
8
8
|
import z from "@deepseek-ai/schemastery";
|
|
9
|
+
/** Hosts treated as loopback: the decision service is a local process by design. */
|
|
10
|
+
const LOOPBACK_HOSTS = /* @__PURE__ */ new Set([
|
|
11
|
+
"127.0.0.1",
|
|
12
|
+
"localhost",
|
|
13
|
+
"::1",
|
|
14
|
+
"[::1]"
|
|
15
|
+
]);
|
|
16
|
+
/**
|
|
17
|
+
* Whether a reviewer model id names the Phocinae decision model. Used only to
|
|
18
|
+
* infer the backend when `reviewerBackend` is unset: `Phocinae-…`,
|
|
19
|
+
* `phocinae:…` and a bare `phocinae` all select the local service.
|
|
20
|
+
* @param model - the configured `reviewerModel`, if any.
|
|
21
|
+
* @returns true when the name belongs to the Phocinae family.
|
|
22
|
+
*/
|
|
23
|
+
function isPhocinaeModelName(model) {
|
|
24
|
+
return typeof model === "string" && /^phocinae(?:[-_/ :]|$)/iu.test(model.trim());
|
|
25
|
+
}
|
|
9
26
|
const POLICY = z.union([
|
|
10
27
|
"ai",
|
|
11
28
|
"human",
|
|
@@ -31,6 +48,7 @@ const CIRCUIT_ACTION = z.union([
|
|
|
31
48
|
"reject",
|
|
32
49
|
"abort-turn"
|
|
33
50
|
]);
|
|
51
|
+
const REVIEWER_BACKEND = z.union(["agent", "phocinae"]);
|
|
34
52
|
z.object({
|
|
35
53
|
enableByDefault: z.boolean().default(true),
|
|
36
54
|
toolsPolicy: z.object({
|
|
@@ -49,6 +67,15 @@ z.object({
|
|
|
49
67
|
reviewerPreset: z.string(),
|
|
50
68
|
reviewerModel: z.string(),
|
|
51
69
|
reviewerModelProvider: z.string(),
|
|
70
|
+
reviewerBackend: REVIEWER_BACKEND,
|
|
71
|
+
phocinae: z.object({
|
|
72
|
+
endpoint: z.string(),
|
|
73
|
+
model: z.string(),
|
|
74
|
+
timeoutMs: z.number(),
|
|
75
|
+
minConfidence: z.number(),
|
|
76
|
+
maxStateChars: z.number(),
|
|
77
|
+
allowRemoteEndpoint: z.boolean()
|
|
78
|
+
}),
|
|
52
79
|
reviewerTimeoutMs: z.number().default(6e4),
|
|
53
80
|
reviewerTools: z.array(z.string()).default([
|
|
54
81
|
"read",
|
|
@@ -94,6 +121,48 @@ z.object({
|
|
|
94
121
|
allowUnmarkedAudit: z.boolean().default(false)
|
|
95
122
|
});
|
|
96
123
|
/**
|
|
124
|
+
* Validate and complete the Phocinae decision-service settings. Every illegal
|
|
125
|
+
* value throws here, at load time: a security-relevant reviewer must not
|
|
126
|
+
* degrade silently at run time (a bad endpoint would otherwise read as
|
|
127
|
+
* "service unavailable", which is safe but indistinguishable from a killed
|
|
128
|
+
* server, and a negative timeout would disarm the deadline).
|
|
129
|
+
* @param raw - partial `phocinae` config.
|
|
130
|
+
* @param reviewerModel - `reviewerModel`, so a Phocinae model id selects the
|
|
131
|
+
* served model name without repeating it under `phocinae.model`.
|
|
132
|
+
* @returns the resolved settings.
|
|
133
|
+
*/
|
|
134
|
+
function resolvePhocinaeConfig(raw, reviewerModel) {
|
|
135
|
+
if (raw !== void 0 && (typeof raw !== "object" || raw === null || Array.isArray(raw))) throw new TypeError(`phocinae must be an object, got ${JSON.stringify(raw)}`);
|
|
136
|
+
const allowRemoteEndpoint = raw?.allowRemoteEndpoint ?? false;
|
|
137
|
+
if (typeof allowRemoteEndpoint !== "boolean") throw new TypeError(`phocinae.allowRemoteEndpoint must be a boolean, got ${JSON.stringify(raw?.allowRemoteEndpoint)}`);
|
|
138
|
+
const rawEndpoint = raw?.endpoint ?? "http://127.0.0.1:8155/v1/systemone";
|
|
139
|
+
if (typeof rawEndpoint !== "string" || rawEndpoint.trim() === "") throw new TypeError(`phocinae.endpoint must be a non-empty URL string, got ${JSON.stringify(raw?.endpoint)}`);
|
|
140
|
+
let url;
|
|
141
|
+
try {
|
|
142
|
+
url = new URL(rawEndpoint);
|
|
143
|
+
} catch {
|
|
144
|
+
throw new TypeError(`phocinae.endpoint must be an absolute URL, got ${JSON.stringify(rawEndpoint)}`);
|
|
145
|
+
}
|
|
146
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") throw new TypeError(`phocinae.endpoint must use http or https, got ${JSON.stringify(url.protocol)}`);
|
|
147
|
+
if (!allowRemoteEndpoint && !LOOPBACK_HOSTS.has(url.hostname)) throw new TypeError(`phocinae.endpoint host ${JSON.stringify(url.hostname)} is not loopback. The decision state carries the reviewed tool call's text, so a remote endpoint would leak it; set phocinae.allowRemoteEndpoint: true if that egress is intended.`);
|
|
148
|
+
const model = raw?.model ?? (isPhocinaeModelName(reviewerModel) ? reviewerModel?.trim() : void 0) ?? "Phocinae-Largha-150M-v1";
|
|
149
|
+
if (typeof model !== "string" || model.trim() === "") throw new TypeError(`phocinae.model must be a non-empty string, got ${JSON.stringify(raw?.model)}`);
|
|
150
|
+
const timeoutMs = raw?.timeoutMs ?? 5e3;
|
|
151
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0) throw new TypeError(`phocinae.timeoutMs must be a positive safe integer, got ${String(raw?.timeoutMs)}`);
|
|
152
|
+
const minConfidence = raw?.minConfidence ?? .4;
|
|
153
|
+
if (typeof minConfidence !== "number" || !Number.isFinite(minConfidence) || minConfidence < 0 || minConfidence > 1) throw new TypeError(`phocinae.minConfidence must be a number in [0, 1], got ${JSON.stringify(raw?.minConfidence)}`);
|
|
154
|
+
const maxStateChars = raw?.maxStateChars ?? 4096;
|
|
155
|
+
if (!Number.isSafeInteger(maxStateChars) || maxStateChars <= 0) throw new TypeError(`phocinae.maxStateChars must be a positive safe integer, got ${String(raw?.maxStateChars)}`);
|
|
156
|
+
return {
|
|
157
|
+
endpoint: url.toString(),
|
|
158
|
+
model,
|
|
159
|
+
timeoutMs,
|
|
160
|
+
minConfidence,
|
|
161
|
+
maxStateChars,
|
|
162
|
+
allowRemoteEndpoint
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
97
166
|
* Validate raw values and compile the resolved config. Defaults are applied
|
|
98
167
|
* HERE — the explicit resolution step — so a partially-specified config from
|
|
99
168
|
* a direct `ctx.plugin` mount behaves like the loader-filled one.
|
|
@@ -121,6 +190,10 @@ function resolveConfig(config = {}) {
|
|
|
121
190
|
if (!Number.isSafeInteger(config.verdictCacheTtlMs ?? 6e4) || (config.verdictCacheTtlMs ?? 6e4) < 0) throw new TypeError(`verdictCacheTtlMs must be a non-negative safe integer (0 disables the cache), got ${String(config.verdictCacheTtlMs)}`);
|
|
122
191
|
if (!Number.isSafeInteger(config.verdictCacheMaxEntries ?? 256) || (config.verdictCacheMaxEntries ?? 256) <= 0) throw new TypeError(`verdictCacheMaxEntries must be a positive safe integer, got ${String(config.verdictCacheMaxEntries)}`);
|
|
123
192
|
if (config.allowUnmarkedAudit !== void 0 && typeof config.allowUnmarkedAudit !== "boolean") throw new TypeError(`allowUnmarkedAudit must be a boolean, got ${String(config.allowUnmarkedAudit)}`);
|
|
193
|
+
const rawBackend = config.reviewerBackend;
|
|
194
|
+
if (rawBackend !== void 0 && rawBackend !== "agent" && rawBackend !== "phocinae") throw new TypeError(`reviewerBackend must be one of "agent", "phocinae", got ${JSON.stringify(rawBackend)}`);
|
|
195
|
+
const reviewerBackend = rawBackend ?? (isPhocinaeModelName(config.reviewerModel) ? "phocinae" : "agent");
|
|
196
|
+
const phocinae = resolvePhocinaeConfig(config.phocinae, config.reviewerModel);
|
|
124
197
|
const reviewerTools = config.reviewerTools ?? [
|
|
125
198
|
"read",
|
|
126
199
|
"glob",
|
|
@@ -152,6 +225,8 @@ function resolveConfig(config = {}) {
|
|
|
152
225
|
reviewerPreset: config.reviewerPreset,
|
|
153
226
|
reviewerModel: config.reviewerModel,
|
|
154
227
|
reviewerModelProvider: config.reviewerModelProvider,
|
|
228
|
+
reviewerBackend,
|
|
229
|
+
phocinae,
|
|
155
230
|
reviewerTimeoutMs: config.reviewerTimeoutMs ?? 6e4,
|
|
156
231
|
reviewerTools,
|
|
157
232
|
fallbackPolicy: config.fallbackPolicy ?? "rejected",
|