dsh-auto-review 0.5.3 → 0.6.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.
Files changed (68) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.es.md +42 -3
  3. package/README.hi.md +42 -3
  4. package/README.md +44 -4
  5. package/README.pt.md +42 -3
  6. package/README.zh.md +42 -3
  7. package/lib/client.js +1 -1
  8. package/lib/client.js.map +1 -1
  9. package/lib/eval-cli.js +1 -1
  10. package/lib/eval.js +2 -2
  11. package/lib/index.js +29 -7
  12. package/lib/{report-DERBXGXW.js → report-NzSUyIog.js} +435 -17
  13. package/lib/types/audit.d.ts +3 -2
  14. package/lib/types/audit.d.ts.map +1 -1
  15. package/lib/types/audit.js +7 -4
  16. package/lib/types/audit.js.map +1 -1
  17. package/lib/types/client/index.js +1 -1
  18. package/lib/types/client/index.js.map +1 -1
  19. package/lib/types/eval/assert.d.ts +3 -1
  20. package/lib/types/eval/assert.d.ts.map +1 -1
  21. package/lib/types/eval/assert.js +159 -1
  22. package/lib/types/eval/assert.js.map +1 -1
  23. package/lib/types/eval/diff.d.ts +41 -0
  24. package/lib/types/eval/diff.d.ts.map +1 -0
  25. package/lib/types/eval/diff.js +136 -0
  26. package/lib/types/eval/diff.js.map +1 -0
  27. package/lib/types/eval/dsl.d.ts +57 -0
  28. package/lib/types/eval/dsl.d.ts.map +1 -1
  29. package/lib/types/eval/dsl.js +40 -0
  30. package/lib/types/eval/dsl.js.map +1 -1
  31. package/lib/types/eval/index.d.ts +2 -0
  32. package/lib/types/eval/index.d.ts.map +1 -1
  33. package/lib/types/eval/index.js +2 -0
  34. package/lib/types/eval/index.js.map +1 -1
  35. package/lib/types/eval/report.d.ts.map +1 -1
  36. package/lib/types/eval/report.js +5 -0
  37. package/lib/types/eval/report.js.map +1 -1
  38. package/lib/types/eval/runner.d.ts +11 -0
  39. package/lib/types/eval/runner.d.ts.map +1 -1
  40. package/lib/types/eval/runner.js +40 -4
  41. package/lib/types/eval/runner.js.map +1 -1
  42. package/lib/types/eval/stress.d.ts +45 -0
  43. package/lib/types/eval/stress.d.ts.map +1 -0
  44. package/lib/types/eval/stress.js +68 -0
  45. package/lib/types/eval/stress.js.map +1 -0
  46. package/lib/types/eval/trace.d.ts +19 -9
  47. package/lib/types/eval/trace.d.ts.map +1 -1
  48. package/lib/types/eval/trace.js +43 -9
  49. package/lib/types/eval/trace.js.map +1 -1
  50. package/lib/types/projection-types.d.ts +24 -0
  51. package/lib/types/projection-types.d.ts.map +1 -1
  52. package/lib/types/projection.d.ts +49 -18
  53. package/lib/types/projection.d.ts.map +1 -1
  54. package/lib/types/projection.js +21 -2
  55. package/lib/types/projection.js.map +1 -1
  56. package/package.json +34 -34
  57. package/src/audit.ts +7 -4
  58. package/src/client/index.ts +2 -2
  59. package/src/eval/assert.ts +158 -1
  60. package/src/eval/diff.ts +151 -0
  61. package/src/eval/dsl.ts +61 -0
  62. package/src/eval/index.ts +2 -0
  63. package/src/eval/report.ts +5 -0
  64. package/src/eval/runner.ts +39 -3
  65. package/src/eval/stress.ts +89 -0
  66. package/src/eval/trace.ts +73 -0
  67. package/src/projection-types.ts +25 -0
  68. package/src/projection.ts +33 -21
package/CHANGELOG.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  All notable changes to `dsh-auto-review` are documented here. The repo is pre-release; versions follow the DeepSeek Harness `0.1.0-rc.x` target runtime and bump on every behavior change.
4
4
 
5
+ ## [0.6.0] — 2026-08-23
6
+
7
+ ### Added
8
+
9
+ - **dsh-eval assertion engine gains three structured assertion families** (completing the evaluation-platform direction):
10
+ - **Prompt regression** (`expect.prompt`): the rendered system prompt must match a committed `baseline` (inline or via `baselineFrom` file); any drift is reported as a **side-by-side diff** in the report, with `allowedChanges` regexes to whitelist intended edits. The baseline file is resolved before any agent runs, and a missing/unreadable file fails the suite loudly.
11
+ - **Stress metrics** (`expect.stress`): gates P99 step latency (`maxP99Ms`), worst time-to-first-token (`maxTtftMs`), and aggregate token generation speed (`minTokensPerSecond`), folded from per-step timing records (`step/start` → first/last `assistant/chunk` → `step/end`).
12
+ - **Bias radar** (`expect.bias`): per-category regex counts over the final output (`categories`), hard `forbid` patterns, and `maxHits`/`maxCategoryHits` caps — the fairness (Bias-Radar) assertion.
13
+ - Per-step timing is now collected into `trace.steps`, and the assertion engine reports optional multi-line `detail` (the prompt diff, the bias radar) which the Markdown report renders as a code block.
14
+ - Five-language READMEs document the assertion families with a combined `prompt`/`stress`/`bias` example, a GitHub Action CI snippet, and the contrast with `openai/codex-research`.
15
+
16
+ ## [0.5.4] — 2026-08-22
17
+
18
+ ### Changed
19
+
20
+ - Runtime `dependencies` and `devDependencies` pin the exact `0.1.1-rc.2` line and `dshWorkshop.dshVersions` lists `0.1.1-rc.2`; the peer specs stay on the `>=0.1.0-rc.8 <0.2.0` range, which already covers `0.1.1-rc.2`.
21
+ - The session-projection unit migrated to the rc2 `ProjectionDefinition` contract (`stateSchema` + `wire: { viewSchema, view }` + the required `stateVersion`), and the `autoReview` key now declares both the client `SessionProjectionMap` and the host `SessionProjectionStateMap` entries.
22
+ - The browser panel calls the rc2 `remote.commands.execute` shape (empty image list for the text-only `/auto-review` command).
23
+ - `scripts/check-host-versions.mjs` now recognizes the `0.1.1-rc.N` line family; `pnpm-workspace.yaml` excludes the whole `@deepseek-ai/*` scope from `minimumReleaseAge`; the compat workflow pins the rc2 CLI and `dsh-base`/`dsh-headless` bundles.
24
+
5
25
  ## [0.5.3] — 2026-08-21
6
26
 
7
27
  ### Changed
package/README.es.md CHANGED
@@ -24,7 +24,7 @@
24
24
 
25
25
  | Superficie | Estado |
26
26
  |---|---|
27
- | Harness | DeepSeek Harness `0.1.0-rc.8` (peers fijados a `0.1.0-rc.8`) |
27
+ | Harness | DeepSeek Harness `0.1.1-rc.2` (dependencias fijadas a `0.1.1-rc.2`; peers `>=0.1.0-rc.8 <0.2.0`) |
28
28
  | Node | `^22.19.0 \|\| >=24.0.0` |
29
29
  | Plataformas | Todas (answerer de host; panel web opcional mediante la capacidad de proyección de sesión) |
30
30
  | Modelo | Cualquiera (el revisor hereda la ruta del agente de sesión; `reviewerModel` la reemplaza) |
@@ -210,13 +210,52 @@ Ejecútalo (una clave de API de DeepSeek debe estar en el entorno):
210
210
  dsh-eval eval/cases --model deepseek-v4-flash --timeout-ms 240000 --out .eval-reports
211
211
  ```
212
212
 
213
- Puerta de CI: el proceso sale con 0 solo cuando todos los casos de todas las suites han pasado — colócalo en un paso de GitHub Action y las evaluaciones que fallen harán fallar la compilación. Cada caso deja un JSONL de sesión reproducible y un JSON de rastro junto a `report.md`/`report.json`; los resultados de las aserciones, el uso de tokens y el veredicto de revisión se escriben todos en los archivos de informe.
213
+ ### Familias de aserciones
214
+
215
+ El bloque `expect` admite seis familias de aserciones; cada aserción se evalúa de forma independiente e informa su propio aprobado/fallo con valores esperados/reales, de modo que un caso que falla se explica a sí mismo sin volver a ejecutarlo.
216
+
217
+ | Familia | Claves DSL | Qué verifica |
218
+ |---|---|---|
219
+ | Rastro de herramientas | `toolCalls`, `toolCallsExact`, `noToolCalls`, `results` | secuencia ordenada de llamadas (subsecuencia con saltos), secuencia exacta de nombres, resultado por herramienta (`isError`/`contains`/`regex`) |
220
+ | Salida y presupuesto | `output`, `turnEnds`, `maxTokens` | subcadena/regex de la salida final, resultado del turno, presupuesto de tokens |
221
+ | Regresión de prompt | `prompt` | el prompt de sistema renderizado debe coincidir con un `baseline` comprometido (o un archivo `baselineFrom`); cualquier deriva se informa como un **diff lado a lado**, con regex `allowedChanges` para permitir ediciones intencionales |
222
+ | Métricas de estrés | `stress` | latencia de paso P99 (`maxP99Ms`), peor tiempo al primer token (`maxTtftMs`), velocidad agregada de generación de tokens (`minTokensPerSecond`) |
223
+ | Equidad | `bias` | radar de sesgo sobre la salida final: conteos por categoría con regex (`categories`), patrones `forbid` duros, topes `maxHits`/`maxCategoryHits` |
224
+ | Revisión de segundo modelo | `review` | un veredicto aprobado/fallo complementario del subagente revisor (capa separada, la misma costura que el revisor de aprobación) |
225
+
226
+ ```yaml
227
+ - id: regression-gate
228
+ input: Answer in one sentence.
229
+ expect:
230
+ prompt:
231
+ baseline: "You are a helpful software engineer assistant."
232
+ allowedChanges: ["copyright-year"]
233
+ stress:
234
+ maxP99Ms: 8000
235
+ maxTtftMs: 3000
236
+ minTokensPerSecond: 20
237
+ bias:
238
+ categories: { gender: ["[Hh]e is (un)?stable"] }
239
+ forbid: ["[Ss]crew that"]
240
+ maxCategoryHits: 0
241
+ ```
242
+
243
+ Puerta de CI: el proceso sale con 0 solo cuando todos los casos de todas las suites han pasado — las evaluaciones que fallen hacen fallar la compilación. Cada caso deja un JSONL de sesión reproducible y un JSON de rastro junto a `report.md`/`report.json`; los resultados de las aserciones (incluido el diff lado a lado del prompt), el uso de tokens, las métricas de estrés/equidad y el veredicto de revisión se escriben todos en los archivos de informe.
244
+
245
+ ```yaml
246
+ - name: dsh-eval
247
+ run: npx dsh-eval eval/cases --model deepseek-v4-flash --timeout-ms 240000 --out .eval-reports
248
+ env:
249
+ DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
250
+ ```
251
+
252
+ `dsh-eval` difiere de [openai/codex-research](https://github.com/openai/codex-research): codex-research puntúa trayectorias de agentes para comparación de investigación; `dsh-eval` es un arnés declarativo de regresión aprobado/fallo — casos YAML, aserciones estructuradas de rastro/prompt/estrés/equidad, una revisión opcional de segundo modelo y un código de salida de CI — para gatear cualquier agente DSH, no para ranking de investigación.
214
253
 
215
254
  ## Permisos y datos
216
255
 
217
256
  - **Permisos**: el manifiesto del workshop declara `session:append`, `approval:answer`, `subagent:spawn`, `command:register` y `tools:observe`.
218
257
  - **Datos**: nada se guarda en disco; el búfer circular de informes está en memoria y acotado. Sin peticiones de red propias.
219
- - **Registro de sesión**: los eventos `autoReview/*` llevan identidad del revisor, veredicto, razón, riesgo y duración — añadidos con el marcador de sobre `ignorable: true` para que cualquier compilación cargue el registro. Los hosts cuyo `Session.append` es anterior al marcador (todas las líneas rc publicadas hasta `0.1.0-rc.8` — ninguna versión lo estampa aún) se detectan antes del primer append (precomprobación de la versión del peer y luego sondeo del sobre devuelto) y la auditoría se degrada a un espejo en memoria con comentarios sin marcador, de modo que las sesiones siguen siendo cargables en todas partes.
258
+ - **Registro de sesión**: los eventos `autoReview/*` llevan identidad del revisor, veredicto, razón, riesgo y duración — añadidos con el marcador de sobre `ignorable: true` para que cualquier compilación cargue el registro. Los hosts cuyo `Session.append` es anterior al marcador (todas las líneas rc publicadas hasta `0.1.1-rc.2` — ninguna versión lo estampa aún) se detectan antes del primer append (precomprobación de la versión del peer y luego sondeo del sobre devuelto) y la auditoría se degrada a un espejo en memoria con comentarios sin marcador, de modo que las sesiones siguen siendo cargables en todas partes.
220
259
 
221
260
  ## Límites de seguridad
222
261
 
package/README.hi.md CHANGED
@@ -24,7 +24,7 @@
24
24
 
25
25
  | सतह | स्थिति |
26
26
  |---|---|
27
- | Harness | DeepSeek Harness `0.1.0-rc.8` (peers `0.1.0-rc.8` पर पिन किए गए) |
27
+ | Harness | DeepSeek Harness `0.1.1-rc.2` (dependencies `0.1.1-rc.2` पर पिन की गईं; peers `>=0.1.0-rc.8 <0.2.0`) |
28
28
  | Node | `^22.19.0 \|\| >=24.0.0` |
29
29
  | प्लेटफ़ॉर्म | सभी (होस्ट answerer; सत्र-प्रोजेक्शन क्षमता के ज़रिए वैकल्पिक वेब समीक्षा पैनल) |
30
30
  | मॉडल | कोई भी (समीक्षक सत्र एजेंट का मार्ग विरासत में लेता है; `reviewerModel` उसे बदल देता है) |
@@ -210,13 +210,52 @@ suite:
210
210
  dsh-eval eval/cases --model deepseek-v4-flash --timeout-ms 240000 --out .eval-reports
211
211
  ```
212
212
 
213
- CI द्वार: प्रक्रिया केवल तभी 0 से निकलती है जब हर सुइट का हर केस पास हो — इसे GitHub Action चरण में डालें और असफल मूल्यांकन बिल्ड को असफल कर देते हैं। हर केस `report.md`/`report.json` के पास एक पुनः-चलाने योग्य सत्र JSONL और एक ट्रेस JSON छोड़ता है; दावा परिणाम, टोकन उपयोग और समीक्षा निर्णय सभी रिपोर्ट फ़ाइलों में लिखे जाते हैं।
213
+ ### दावा परिवार
214
+
215
+ `expect` ब्लॉक छह दावा परिवारों का समर्थन करता है; हर दावा स्वतंत्र रूप से मूल्यांकित होता है और अपने पास/फेल को अपेक्षित/वास्तविक मानों के साथ रिपोर्ट करता है, इसलिए विफल केस बिना दोबारा चलाए खुद को समझा देता है।
216
+
217
+ | परिवार | DSL कुंजियाँ | यह क्या जाँचता है |
218
+ |---|---|---|
219
+ | टूल ट्रेस | `toolCalls`, `toolCallsExact`, `noToolCalls`, `results` | क्रमबद्ध टूल-कॉल अनुक्रम (छूट के साथ उप-अनुक्रम), सटीक नाम अनुक्रम, प्रति-टूल परिणाम (`isError`/`contains`/`regex`) |
220
+ | आउटपुट और बजट | `output`, `turnEnds`, `maxTokens` | अंतिम आउटपुट का सबस्ट्रिंग/रेगेक्स, टर्न परिणाम, टोकन बजट |
221
+ | प्रॉम्प्ट रिग्रेशन | `prompt` | रेंडर किया गया सिस्टम प्रॉम्प्ट एक प्रतिबद्ध `baseline` (या `baselineFrom` फ़ाइल) से मेल खाना चाहिए; कोई भी विचलन **side-by-side diff** के रूप में रिपोर्ट होता है, जिसमें इच्छित संपादनों की अनुमति के लिए `allowedChanges` रेगेक्स होते हैं |
222
+ | स्ट्रेस मेट्रिक्स | `stress` | P99 स्टेप लेटेंसी (`maxP99Ms`), सबसे खराब पहला-टोकन समय (`maxTtftMs`), कुल टोकन निर्माण गति (`minTokensPerSecond`) |
223
+ | निष्पक्षता | `bias` | अंतिम आउटपुट पर पूर्वाग्रह रडार: प्रति-श्रेणी रेगेक्स गणना (`categories`), कठोर `forbid` पैटर्न, `maxHits`/`maxCategoryHits` सीमाएँ |
224
+ | द्वितीय-मॉडल समीक्षा | `review` | समीक्षक उप-एजेंट से एक पूरक पास/फेल निर्णय (अलग परत, अनुमोदन समीक्षक जैसी ही सीम) |
225
+
226
+ ```yaml
227
+ - id: regression-gate
228
+ input: Answer in one sentence.
229
+ expect:
230
+ prompt:
231
+ baseline: "You are a helpful software engineer assistant."
232
+ allowedChanges: ["copyright-year"]
233
+ stress:
234
+ maxP99Ms: 8000
235
+ maxTtftMs: 3000
236
+ minTokensPerSecond: 20
237
+ bias:
238
+ categories: { gender: ["[Hh]e is (un)?stable"] }
239
+ forbid: ["[Ss]crew that"]
240
+ maxCategoryHits: 0
241
+ ```
242
+
243
+ CI द्वार: प्रक्रिया केवल तभी 0 से निकलती है जब हर सुइट का हर केस पास हो — असफल मूल्यांकन बिल्ड को असफल कर देते हैं। हर केस `report.md`/`report.json` के पास एक पुनः-चलाने योग्य सत्र JSONL और एक ट्रेस JSON छोड़ता है; दावा परिणाम (प्रॉम्प्ट side-by-side diff सहित), टोकन उपयोग, स्ट्रेस/निष्पक्षता मेट्रिक्स और समीक्षा निर्णय सभी रिपोर्ट फ़ाइलों में लिखे जाते हैं।
244
+
245
+ ```yaml
246
+ - name: dsh-eval
247
+ run: npx dsh-eval eval/cases --model deepseek-v4-flash --timeout-ms 240000 --out .eval-reports
248
+ env:
249
+ DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
250
+ ```
251
+
252
+ `dsh-eval` [openai/codex-research](https://github.com/openai/codex-research) से भिन्न है: codex-research अनुसंधान तुलना के लिए एजेंट ट्रैजेक्टरी को स्कोर करता है; `dsh-eval` एक घोषणात्मक पास/फेल रिग्रेशन हार्नेस है — YAML केस, संरचित ट्रेस/प्रॉम्प्ट/स्ट्रेस/निष्पक्षता दावे, एक वैकल्पिक द्वितीय-मॉडल समीक्षा और एक CI एग्जिट कोड — किसी भी DSH एजेंट को गेट करने के लिए, अनुसंधान रैंकिंग के लिए नहीं।
214
253
 
215
254
  ## अनुमतियाँ और डेटा
216
255
 
217
256
  - **अनुमतियाँ**: workshop मेनिफ़ेस्ट `session:append`, `approval:answer`, `subagent:spawn`, `command:register` और `tools:observe` घोषित करता है।
218
257
  - **डेटा**: डिस्क पर कुछ नहीं लिखा जाता; रिपोर्ट रिंग बफ़र मेमोरी में और सीमित है। अपनी ओर से कोई नेटवर्क अनुरोध नहीं।
219
- - **सत्र लॉग**: `autoReview/*` घटनाएँ समीक्षक पहचान, निर्णय, कारण, जोखिम और अवधि रखती हैं — लिफ़ाफ़े के `ignorable: true` मार्कर के साथ जोड़ी जाती हैं ताकि कोई भी बिल्ड लॉग लोड कर सके। जिन होस्ट का `Session.append` मार्कर से पुराना है (`0.1.0-rc.8` तक की हर रिलीज़ rc लाइन — कोई रिलीज़ अभी मार्कर नहीं छापती) उन्हें पहले append से पहले ही पहचान लिया जाता है (peer संस्करण पूर्व-जाँच, फिर लौटे लिफ़ाफ़े की जाँच) और ऑडिट बिना मार्कर वाले फीडबैक के साथ इन-मेमोरी मिरर में डिग्रेड हो जाता है — सत्र हर जगह लोड होने योग्य रहते हैं।
258
+ - **सत्र लॉग**: `autoReview/*` घटनाएँ समीक्षक पहचान, निर्णय, कारण, जोखिम और अवधि रखती हैं — लिफ़ाफ़े के `ignorable: true` मार्कर के साथ जोड़ी जाती हैं ताकि कोई भी बिल्ड लॉग लोड कर सके। जिन होस्ट का `Session.append` मार्कर से पुराना है (`0.1.1-rc.2` तक की हर रिलीज़ rc लाइन — कोई रिलीज़ अभी मार्कर नहीं छापती) उन्हें पहले append से पहले ही पहचान लिया जाता है (peer संस्करण पूर्व-जाँच, फिर लौटे लिफ़ाफ़े की जाँच) और ऑडिट बिना मार्कर वाले फीडबैक के साथ इन-मेमोरी मिरर में डिग्रेड हो जाता है — सत्र हर जगह लोड होने योग्य रहते हैं।
220
259
 
221
260
  ## सुरक्षा सीमाएँ
222
261
 
package/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  <div align="center">
2
2
 
3
3
  # 🤖 dsh-auto-review
4
+ [![Gitee](https://img.shields.io/badge/Gitee-mirror-c71d23?logo=gitee)](https://gitee.com/perrylink/dsh-auto-review)
4
5
 
5
6
  **Second-model AI approval for DeepSeek Harness — a read-only reviewer subagent decides allow/deny on the approval chain, fail-closed by default.**
6
7
 
@@ -24,7 +25,7 @@
24
25
 
25
26
  | Surface | Status |
26
27
  |---|---|
27
- | Harness | DeepSeek Harness `0.1.0-rc.8` (peers pinned to `0.1.0-rc.8`) |
28
+ | Harness | DeepSeek Harness `0.1.1-rc.2` (dependencies pinned to `0.1.1-rc.2`; peers `>=0.1.0-rc.8 <0.2.0`) |
28
29
  | Node | `^22.19.0 \|\| >=24.0.0` |
29
30
  | Platforms | All (host answerer; optional Web review panel via the session-projection capability) |
30
31
  | Model | Any (the reviewer inherits the session agent's route; `reviewerModel` overrides) |
@@ -210,13 +211,52 @@ Run it (a DeepSeek API key must be in the environment):
210
211
  dsh-eval eval/cases --model deepseek-v4-flash --timeout-ms 240000 --out .eval-reports
211
212
  ```
212
213
 
213
- CI gate: the process exits 0 only when every case of every suite passed — drop it into a GitHub Action step and failing evaluations fail the build. Each case leaves a replayable session JSONL and a trace JSON beside `report.md`/`report.json`; assertion results, token usage, and the review verdict are all written into the report files.
214
+ ### Assertion families
215
+
216
+ The `expect` block supports six assertion families; each assertion is evaluated independently and reports its own pass/fail with expected/actual values, so a failing case explains itself without a rerun.
217
+
218
+ | Family | DSL keys | What it gates |
219
+ |---|---|---|
220
+ | Tool trace | `toolCalls`, `toolCallsExact`, `noToolCalls`, `results` | ordered tool-call sequence (subsequence with skips), exact name sequence, per-tool result (`isError`/`contains`/`regex`) |
221
+ | Output & budget | `output`, `turnEnds`, `maxTokens` | final-output substring/regex, turn outcome, token budget |
222
+ | Prompt regression | `prompt` | the rendered system prompt must match a committed `baseline` (or a `baselineFrom` file); any drift is reported as a **side-by-side diff**, with `allowedChanges` regexes to whitelist intended edits |
223
+ | Stress metrics | `stress` | P99 step latency (`maxP99Ms`), worst time-to-first-token (`maxTtftMs`), aggregate token generation speed (`minTokensPerSecond`) |
224
+ | Fairness | `bias` | bias radar over the final output: per-category regex counts (`categories`), hard `forbid` patterns, `maxHits`/`maxCategoryHits` caps |
225
+ | Second-model review | `review` | a supplementary pass/fail verdict from the reviewer subagent (a separate layer, same seam as the approval reviewer) |
226
+
227
+ ```yaml
228
+ - id: regression-gate
229
+ input: Answer in one sentence.
230
+ expect:
231
+ prompt:
232
+ baseline: "You are a helpful software engineer assistant."
233
+ allowedChanges: ["copyright-year"]
234
+ stress:
235
+ maxP99Ms: 8000
236
+ maxTtftMs: 3000
237
+ minTokensPerSecond: 20
238
+ bias:
239
+ categories: { gender: ["[Hh]e is (un)?stable"] }
240
+ forbid: ["[Ss]crew that"]
241
+ maxCategoryHits: 0
242
+ ```
243
+
244
+ CI gate: the process exits 0 only when every case of every suite passed — failing evaluations fail the build. Each case leaves a replayable session JSONL and a trace JSON beside `report.md`/`report.json`; assertion results (including the prompt side-by-side diff), token usage, stress/bias metrics, and the review verdict are all written into the report files.
245
+
246
+ ```yaml
247
+ - name: dsh-eval
248
+ run: npx dsh-eval eval/cases --model deepseek-v4-flash --timeout-ms 240000 --out .eval-reports
249
+ env:
250
+ DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
251
+ ```
252
+
253
+ `dsh-eval` differs from [openai/codex-research](https://github.com/openai/codex-research): codex-research scores agent trajectories for research comparison; `dsh-eval` is a declarative pass/fail regression harness — YAML cases, structured trace/prompt/stress/bias assertions, an optional second-model review, and a CI exit code — for gating any DSH agent, not research ranking.
214
254
 
215
255
  ## Permissions & data
216
256
 
217
257
  - **Permissions**: the workshop manifest declares `session:append`, `approval:answer`, `subagent:spawn`, `command:register`, and `tools:observe`.
218
258
  - **Data**: nothing is stored on disk; the report ring buffer is in-memory and bounded. No network requests of its own.
219
- - **Session log**: `autoReview/*` events carry reviewer identity, verdict, reason, risk, and duration — appended with the envelope's `ignorable: true` marker so any build loads the log. Hosts whose `Session.append` predates the marker (every released rc line through `0.1.0-rc.8` — no release stamps it yet) are detected before the first append (peer-version pre-check, then a probe of the returned envelope) and audit degrades to an in-memory mirror with marker-free feedback, so sessions stay loadable everywhere.
259
+ - **Session log**: `autoReview/*` events carry reviewer identity, verdict, reason, risk, and duration — appended with the envelope's `ignorable: true` marker so any build loads the log. Hosts whose `Session.append` predates the marker (every released rc line through `0.1.1-rc.2` — no release stamps it yet) are detected before the first append (peer-version pre-check, then a probe of the returned envelope) and audit degrades to an in-memory mirror with marker-free feedback, so sessions stay loadable everywhere.
220
260
 
221
261
  ## Security boundaries
222
262
 
@@ -250,7 +290,7 @@ CI gate: the process exits 0 only when every case of every suite passed — drop
250
290
  ```sh
251
291
  pnpm install # node ^22.19 || >=24
252
292
  pnpm run typecheck # tsc: src + tests against the local harness checkout
253
- pnpm test # vitest: 202 tests, 16 files
293
+ pnpm test # vitest: 233 tests, 20 files
254
294
  pnpm run build # tsc declarations + tsdown bundles (lib/, incl. the client bundle)
255
295
  pnpm run verify:self-contained
256
296
  pnpm pack # the published tarball
package/README.pt.md CHANGED
@@ -24,7 +24,7 @@
24
24
 
25
25
  | Superfície | Status |
26
26
  |---|---|
27
- | Harness | DeepSeek Harness `0.1.0-rc.8` (peers fixados em `0.1.0-rc.8`) |
27
+ | Harness | DeepSeek Harness `0.1.1-rc.2` (dependências fixadas em `0.1.1-rc.2`; peers `>=0.1.0-rc.8 <0.2.0`) |
28
28
  | Node | `^22.19.0 \|\| >=24.0.0` |
29
29
  | Plataformas | Todas (answerer de host; painel web opcional via capacidade de projeção de sessão) |
30
30
  | Modelo | Qualquer (o revisor herda a rota do agente da sessão; `reviewerModel` sobrescreve) |
@@ -210,13 +210,52 @@ Execute-o (uma chave de API da DeepSeek deve estar no ambiente):
210
210
  dsh-eval eval/cases --model deepseek-v4-flash --timeout-ms 240000 --out .eval-reports
211
211
  ```
212
212
 
213
- Portão de CI: o processo sai com 0 somente quando todos os casos de todas as suítes passam — coloque-o em um passo de GitHub Action e avaliações que falharem farão o build falhar. Cada caso deixa um JSONL de sessão reproduzível e um JSON de rastro ao lado de `report.md`/`report.json`; resultados de asserção, uso de tokens e o veredito de revisão são todos gravados nos arquivos de relatório.
213
+ ### Famílias de asserção
214
+
215
+ O bloco `expect` suporta seis famílias de asserção; cada asserção é avaliada de forma independente e informa seu próprio aprovação/falha com valores esperados/reais, de modo que um caso que falha se explica sozinho sem nova execução.
216
+
217
+ | Família | Chaves DSL | O que valida |
218
+ |---|---|---|
219
+ | Rastro de ferramentas | `toolCalls`, `toolCallsExact`, `noToolCalls`, `results` | sequência ordenada de chamadas (subsequência com saltos), sequência exata de nomes, resultado por ferramenta (`isError`/`contains`/`regex`) |
220
+ | Saída e orçamento | `output`, `turnEnds`, `maxTokens` | substring/regex da saída final, resultado do turno, orçamento de tokens |
221
+ | Regressão de prompt | `prompt` | o prompt de sistema renderizado deve corresponder a um `baseline` confirmado (ou um arquivo `baselineFrom`); qualquer deriva é relatada como um **diff lado a lado**, com regex `allowedChanges` para permitir edições intencionais |
222
+ | Métricas de estresse | `stress` | latência de passo P99 (`maxP99Ms`), pior tempo até o primeiro token (`maxTtftMs`), velocidade agregada de geração de tokens (`minTokensPerSecond`) |
223
+ | Equidade | `bias` | radar de viés sobre a saída final: contagens por categoria com regex (`categories`), padrões `forbid` rígidos, tetos `maxHits`/`maxCategoryHits` |
224
+ | Revisão de segundo modelo | `review` | um veredito aprovação/falha complementar do subagente revisor (camada separada, a mesma costura do revisor de aprovação) |
225
+
226
+ ```yaml
227
+ - id: regression-gate
228
+ input: Answer in one sentence.
229
+ expect:
230
+ prompt:
231
+ baseline: "You are a helpful software engineer assistant."
232
+ allowedChanges: ["copyright-year"]
233
+ stress:
234
+ maxP99Ms: 8000
235
+ maxTtftMs: 3000
236
+ minTokensPerSecond: 20
237
+ bias:
238
+ categories: { gender: ["[Hh]e is (un)?stable"] }
239
+ forbid: ["[Ss]crew that"]
240
+ maxCategoryHits: 0
241
+ ```
242
+
243
+ Portão de CI: o processo sai com 0 somente quando todos os casos de todas as suítes passam — avaliações que falharem fazem o build falhar. Cada caso deixa um JSONL de sessão reproduzível e um JSON de rastro ao lado de `report.md`/`report.json`; resultados de asserção (incluindo o diff lado a lado do prompt), uso de tokens, métricas de estresse/equidade e o veredito de revisão são todos gravados nos arquivos de relatório.
244
+
245
+ ```yaml
246
+ - name: dsh-eval
247
+ run: npx dsh-eval eval/cases --model deepseek-v4-flash --timeout-ms 240000 --out .eval-reports
248
+ env:
249
+ DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
250
+ ```
251
+
252
+ `dsh-eval` difere de [openai/codex-research](https://github.com/openai/codex-research): o codex-research pontua trajetórias de agentes para comparação de pesquisa; `dsh-eval` é um harness declarativo de regressão aprovação/falha — casos YAML, asserções estruturadas de rastro/prompt/estresse/equidade, uma revisão opcional de segundo modelo e um código de saída de CI — para gatear qualquer agente DSH, não para ranking de pesquisa.
214
253
 
215
254
  ## Permissões e dados
216
255
 
217
256
  - **Permissões**: o manifesto do workshop declara `session:append`, `approval:answer`, `subagent:spawn`, `command:register` e `tools:observe`.
218
257
  - **Dados**: nada é gravado em disco; o buffer circular de relatórios fica em memória e é limitado. Sem requisições de rede próprias.
219
- - **Log de sessão**: os eventos `autoReview/*` carregam identidade do revisor, veredito, razão, risco e duração — anexados com o marcador de envelope `ignorable: true` para que qualquer build carregue o log. Hosts cujo `Session.append` é anterior ao marcador (todas as linhas rc publicadas até `0.1.0-rc.8` — nenhuma versão o estampa ainda) são detectados antes do primeiro append (pré-checagem da versão do peer e, em seguida, sondagem do envelope retornado) e a auditoria degrada para um espelho em memória com feedback sem marcador, mantendo as sessões carregáveis em qualquer lugar.
258
+ - **Log de sessão**: os eventos `autoReview/*` carregam identidade do revisor, veredito, razão, risco e duração — anexados com o marcador de envelope `ignorable: true` para que qualquer build carregue o log. Hosts cujo `Session.append` é anterior ao marcador (todas as linhas rc publicadas até `0.1.1-rc.2` — nenhuma versão o estampa ainda) são detectados antes do primeiro append (pré-checagem da versão do peer e, em seguida, sondagem do envelope retornado) e a auditoria degrada para um espelho em memória com feedback sem marcador, mantendo as sessões carregáveis em qualquer lugar.
220
259
 
221
260
  ## Limites de segurança
222
261
 
package/README.zh.md CHANGED
@@ -24,7 +24,7 @@
24
24
 
25
25
  | 方面 | 状态 |
26
26
  |---|---|
27
- | Harness | DeepSeek Harness `0.1.0-rc.8`(peer 依赖锁定在 `0.1.0-rc.8`) |
27
+ | Harness | DeepSeek Harness `0.1.1-rc.2`(依赖锁定在 `0.1.1-rc.2`;peer 范围 `>=0.1.0-rc.8 <0.2.0`) |
28
28
  | Node | `^22.19.0 \|\| >=24.0.0` |
29
29
  | 平台 | 全部(宿主 answerer;可选 Web 审查面板,依赖会话投影能力) |
30
30
  | 模型 | 任意(审查器默认继承会话代理的路由;`reviewerModel` 可覆盖) |
@@ -210,13 +210,52 @@ suite:
210
210
  dsh-eval eval/cases --model deepseek-v4-flash --timeout-ms 240000 --out .eval-reports
211
211
  ```
212
212
 
213
- CI 门禁:仅当每个套件的每个用例都通过时,进程才以 0 退出 —— 放进 GitHub Action 步骤,评估失败即构建失败。每个用例都会在 `report.md`/`report.json` 旁边留下可重放的会话 JSONL 与轨迹 JSON;断言结果、token 用量与审查裁决都会写入报告文件。
213
+ ### 断言族
214
+
215
+ `expect` 块支持六类断言族;每条断言独立求值,并报告各自的通过/失败与期望/实际值,失败的用例无需重跑即可自我解释。
216
+
217
+ | 断言族 | DSL 键 | 门禁内容 |
218
+ |---|---|---|
219
+ | 工具轨迹 | `toolCalls`、`toolCallsExact`、`noToolCalls`、`results` | 有序工具调用序列(允许跳过的子序列)、精确名称序列、逐工具结果(`isError`/`contains`/`regex`) |
220
+ | 输出与预算 | `output`、`turnEnds`、`maxTokens` | 最终输出的子串/正则、回合结果、token 预算 |
221
+ | Prompt 回归 | `prompt` | 渲染后的系统提示词必须匹配已提交的 `baseline`(或 `baselineFrom` 文件);任何漂移都会以**并排 diff** 报告,可用 `allowedChanges` 正则白名单放行有意的修改 |
222
+ | 压测指标 | `stress` | P99 步延迟(`maxP99Ms`)、最差首 token 时延(`maxTtftMs`)、聚合 token 生成速度(`minTokensPerSecond`) |
223
+ | 公平性 | `bias` | 最终输出上的偏差雷达:逐类别正则计数(`categories`)、硬性 `forbid` 模式、`maxHits`/`maxCategoryHits` 上限 |
224
+ | 第二模型审查 | `review` | 审查子代理给出的补充通过/失败裁决(独立层,与审批审查器同一条接缝) |
225
+
226
+ ```yaml
227
+ - id: regression-gate
228
+ input: Answer in one sentence.
229
+ expect:
230
+ prompt:
231
+ baseline: "You are a helpful software engineer assistant."
232
+ allowedChanges: ["copyright-year"]
233
+ stress:
234
+ maxP99Ms: 8000
235
+ maxTtftMs: 3000
236
+ minTokensPerSecond: 20
237
+ bias:
238
+ categories: { gender: ["[Hh]e is (un)?stable"] }
239
+ forbid: ["[Ss]crew that"]
240
+ maxCategoryHits: 0
241
+ ```
242
+
243
+ CI 门禁:仅当每个套件的每个用例都通过时,进程才以 0 退出 —— 评估失败即构建失败。每个用例都会在 `report.md`/`report.json` 旁边留下可重放的会话 JSONL 与轨迹 JSON;断言结果(含 prompt 并排 diff)、token 用量、压测/公平性指标与审查裁决都会写入报告文件。
244
+
245
+ ```yaml
246
+ - name: dsh-eval
247
+ run: npx dsh-eval eval/cases --model deepseek-v4-flash --timeout-ms 240000 --out .eval-reports
248
+ env:
249
+ DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
250
+ ```
251
+
252
+ `dsh-eval` 与 [openai/codex-research](https://github.com/openai/codex-research) 的区别:codex-research 为研究对比对代理轨迹打分;`dsh-eval` 是声明式的通过/失败回归门禁 —— YAML 用例、结构化的轨迹/prompt/压测/公平性断言、可选的第二模型审查与 CI 退出码 —— 面向任何 DSH 代理的门禁,而非研究排名。
214
253
 
215
254
  ## 权限与数据
216
255
 
217
256
  - **权限**:workshop 清单声明 `session:append`、`approval:answer`、`subagent:spawn`、`command:register` 与 `tools:observe`。
218
257
  - **数据**:不向磁盘写入任何内容;报告环形缓冲在内存中且有界。自身不发起网络请求。
219
- - **会话日志**:`autoReview/*` 事件携带审查器身份、裁决、理由、风险与耗时 —— 以信封 `ignorable: true` 标记追加,任何构建都能加载日志。`Session.append` 早于该标记的宿主(迄今发布的所有 rc 版本,至 `0.1.0-rc.8`——任何发布版都还未盖标记)会在首次追加前被探测出来(peer 版本预检 + 返回信封探测),审计降级为内存镜像 + 无标记反馈,会话日志始终保持可加载。
258
+ - **会话日志**:`autoReview/*` 事件携带审查器身份、裁决、理由、风险与耗时 —— 以信封 `ignorable: true` 标记追加,任何构建都能加载日志。`Session.append` 早于该标记的宿主(迄今发布的所有 rc 版本,至 `0.1.1-rc.2`——任何发布版都还未盖标记)会在首次追加前被探测出来(peer 版本预检 + 返回信封探测),审计降级为内存镜像 + 无标记反馈,会话日志始终保持可加载。
220
259
 
221
260
  ## 安全边界
222
261
 
package/lib/client.js CHANGED
@@ -468,7 +468,7 @@ window.__ModuleLoader__.load({
468
468
  inject: (sessionId) => {
469
469
  const remote = ctx.remote;
470
470
  const executeCommand = async (line) => {
471
- const result = await remote.commands.execute(sessionId, line);
471
+ const result = await remote.commands.execute(sessionId, line, []);
472
472
  if (!result.ok) throw new Error(`${result.error.code}: ${result.error.message}`);
473
473
  if (result.value === void 0) throw new Error("unknown command: /auto-review");
474
474
  const command = result.value.result;
package/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["useState","useRef"],"sources":["../src/client/present.ts","../src/client/ReviewPanel.tsx","../src/client/locales.ts","../src/client/styles.ts","../src/client/index.ts"],"sourcesContent":["/**\n * Pure presentation for the review panel: verdict rows and badges as plain\n * strings and tone codes — no I/O, clock, or randomness. The component\n * owns layout and interaction; this module owns the text.\n * @module dsh-auto-review/client/present\n */\n\nimport type { AutoReviewVerdictView } from '../projection-types.ts'\n\n/** Badge tones the component maps to scoped styles. */\nexport type VerdictTone = 'allow' | 'deny' | 'fallback' | 'escalation'\n\n/** One presented verdict row. */\nexport interface PresentedVerdict {\n readonly reviewId: string\n readonly toolName: string\n readonly label: string\n readonly detail: string\n readonly tone: VerdictTone\n}\n\n/** Present one verdict row (the component supplies the localized label texts). */\nexport function presentVerdict(\n verdict: AutoReviewVerdictView,\n labels: {\n readonly allow: string\n readonly deny: string\n readonly fallback: (kind: string) => string\n readonly escalation: string\n },\n): PresentedVerdict {\n if (verdict.fallback !== undefined) {\n return {\n reviewId: verdict.reviewId,\n toolName: verdict.toolName,\n label: labels.fallback(verdict.fallback),\n detail: `${verdict.durationMs} ms`,\n tone: 'fallback',\n }\n }\n if (verdict.escalation === 'risk-policy') {\n return {\n reviewId: verdict.reviewId,\n toolName: verdict.toolName,\n label: labels.escalation,\n detail: verdict.reason ?? '',\n tone: 'escalation',\n }\n }\n const label = verdict.decision === 'deny' ? labels.deny : labels.allow\n return {\n reviewId: verdict.reviewId,\n toolName: verdict.toolName,\n label,\n detail: verdict.reason ?? `${verdict.durationMs} ms`,\n tone: verdict.decision === 'deny' ? 'deny' : 'allow',\n }\n}\n","/**\n * The session-header review panel: a button in the conversation header that\n * opens a popover with this session's auto-review state — the on/off switch,\n * budgets, cumulative statistics (including hard-disable rejections), the\n * circuit trip, recent verdicts, and one-shot approve buttons for recent\n * denials. Data arrives through the `autoReview` session projection;\n * switches and approves execute the `/auto-review` command through the\n * commands Remote.\n * @module dsh-auto-review/client/ReviewPanel\n */\n\nimport { useRef, useState, type ReactNode } from 'react'\nimport type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { AutoReviewProjection } from '../projection-types.ts'\nimport { presentVerdict } from './present.ts'\nimport { NS } from './locales.ts'\n\n/** Registration-side injected face: the approve action for one recent denial. */\nexport interface ReviewPanelInjected {\n /** Approve the n-th most recent denial (1 = most recent); resolves with the command result text. */\n approve: (n: number) => Promise<string>\n /** Switch the session's auto-review on/off (`/auto-review on|off`); resolves with the command result text. */\n setEnabled: (enabled: boolean) => Promise<string>\n}\n\n/** Full component props assembled by the session-header slot renderer. */\nexport type ReviewPanelProps =\n PropsRuntime<'conversation.session.header.actions'>\n & PropsLocale<typeof NS>\n & InjectFace<ReviewPanelInjected>\n\n/** Compact verdict-row badge label for the panel. */\nfunction verdictBadge(\n value: AutoReviewProjection | undefined,\n t: ReviewPanelProps['t'],\n): ReactNode {\n if (value === undefined) return t('unavailable')\n const fallback = (kind: string): string => t('fallbackLabel').replace('{kind}', kind)\n const labels = {\n allow: t('decisionAllow'),\n deny: t('decisionDeny'),\n fallback,\n escalation: t('escalationLabel'),\n }\n return value.recent.map(verdict => presentVerdict(verdict, labels)).map(row => (\n <div key={row.reviewId} data-dsh-auto-review-verdict>\n <span data-dsh-auto-review-muted>{row.toolName}</span>\n <span data-dsh-auto-review={`tone-${row.tone}`}>{row.label}</span>\n <span data-dsh-auto-review-muted>{row.detail}</span>\n </div>\n ))\n}\n\n/**\n * Render the review panel action.\n * @param props - framework kit (sessionId, useProjection), locale, and the approve face.\n * @returns the header button with its popover.\n */\nexport function ReviewPanel({ useProjection, approve, setEnabled, t }: ReviewPanelProps): ReactNode {\n const value = useProjection('autoReview')\n const [open, setOpen] = useState(false)\n const [notice, setNotice] = useState<string | null>(null)\n const anchor = useRef<HTMLDivElement | null>(null)\n const toggle = (): void => setOpen(previous => !previous)\n const runApprove = async (n: number): Promise<void> => {\n try {\n const text = await approve(n)\n setNotice(t('approveResult').replace('{text}', text))\n } catch (error: unknown) {\n setNotice(t('approveFailed').replace('{text}', error instanceof Error ? error.message : String(error)))\n }\n }\n const runSwitch = async (enabled: boolean): Promise<void> => {\n try {\n const text = await setEnabled(enabled)\n setNotice(t('switchedResult').replace('{text}', text))\n } catch (error: unknown) {\n setNotice(t('switchFailed').replace('{text}', error instanceof Error ? error.message : String(error)))\n }\n }\n return (\n <div data-dsh-auto-review ref={anchor}>\n <button type=\"button\" data-dsh-auto-review-button onClick={toggle}>\n {t('label')}\n </button>\n {open && (\n <div data-dsh-auto-review-panel>\n <div data-dsh-auto-review-title>{t('title')}</div>\n {value === undefined\n ? <div data-dsh-auto-review-muted>{t('unavailable')}</div>\n : (\n <div>\n <div data-dsh-auto-review-row>\n <span>{t('state')}</span>\n <span>{value.enabled ? t('stateOn') : t('stateOff')}</span>\n </div>\n <div data-dsh-auto-review-row>\n <span>{t('enable')} / {t('disable')}</span>\n <span>\n <button\n type=\"button\"\n data-dsh-auto-review-switch\n disabled={value.enabled}\n onClick={() => void runSwitch(true)}\n >\n {t('enable')}\n </button>\n <button\n type=\"button\"\n data-dsh-auto-review-switch\n disabled={!value.enabled}\n onClick={() => void runSwitch(false)}\n >\n {t('disable')}\n </button>\n </span>\n </div>\n <div data-dsh-auto-review-row>\n <span>{t('verdicts')}</span>\n <span>{value.verdictsUsed}</span>\n </div>\n <div data-dsh-auto-review-row>\n <span>{t('failures')}</span>\n <span>{value.failuresUsed}</span>\n </div>\n <div data-dsh-auto-review-row>\n <span>{t('allTime')}</span>\n <span>\n {value.allows} {t('allows')} · {value.denies} {t('denies')} · {value.fallbacks} {t('fallbacks')} · {value.neverRejects} {t('neverRejects')} · {t('avg')} {value.avgDurationMs} {t('ms')}\n </span>\n </div>\n {value.circuit !== null && (\n <div data-dsh-auto-review-circuit>\n {t('circuit')} — {t('circuitDetail')\n .replace('{kind}', value.circuit.trip.kind)\n .replace('{count}', String(value.circuit.trip.count))\n .replace('{action}', value.circuit.action)}\n </div>\n )}\n {value.recentDenies.length > 0 && (\n <div data-dsh-auto-review-section>\n <div data-dsh-auto-review-title>{t('denyList')}</div>\n {value.recentDenies.map((deny, index) => (\n <div key={deny.reviewId} data-dsh-auto-review-row>\n <span data-dsh-auto-review-muted>{deny.toolName}</span>\n <button type=\"button\" data-dsh-auto-review-approve onClick={() => void runApprove(index + 1)}>\n {t('approve')} #{index + 1}\n </button>\n </div>\n ))}\n </div>\n )}\n <div data-dsh-auto-review-section>\n <div data-dsh-auto-review-title>{t('recent')}</div>\n {value.recent.length === 0 ? <div data-dsh-auto-review-muted>{t('empty')}</div> : verdictBadge(value, t)}\n </div>\n </div>\n )}\n {notice !== null && <div data-dsh-auto-review-muted>{notice}</div>}\n </div>\n )}\n </div>\n )\n}\n","/**\n * Copy dictionaries for the auto-review session-header panel (en/zh — the\n * harness locale registry accepts these two UI language codes today).\n * @module dsh-auto-review/client/locales\n */\n\n/** Simplified Chinese dictionary and key source of truth. */\nexport const zh = {\n label: 'AI 审查',\n title: 'AI 自动审查',\n state: '状态',\n stateOn: '已开启',\n stateOff: '已关闭',\n enable: '开启',\n disable: '关闭',\n verdicts: '本回合 AI 裁决',\n failures: '本回合失败',\n allTime: '累计',\n allows: '放行',\n denies: '拒绝',\n fallbacks: '失败',\n neverRejects: '硬禁用拒绝',\n avg: '平均耗时',\n circuit: '拒绝熔断器已触发',\n circuitDetail: '触发类型:{kind},计数 {count},动作 {action}',\n empty: '本会话暂无 AI 审查裁决。',\n recent: '最近裁决',\n denyList: '可批准的重试',\n approve: '批准重试',\n approveResult: '已授权:{text}',\n approveFailed: '批准失败:{text}',\n switchedResult: '已切换:{text}',\n switchFailed: '切换失败:{text}',\n unavailable: '审查面板不可用(宿主未提供会话投影能力)。',\n decisionAllow: '放行',\n decisionDeny: '拒绝',\n fallbackLabel: '失败({kind})',\n escalationLabel: '风险策略升级',\n riskLow: '低风险',\n riskMedium: '中风险',\n riskHigh: '高风险',\n ms: 'ms',\n} satisfies Record<string, string>\n\n/** Auto-review panel locale key union. */\nexport type ReviewPanelLocaleKey = keyof typeof zh\n\n/** English dictionary checked against the Chinese key set. */\nexport const en: Record<ReviewPanelLocaleKey, string> = {\n label: 'AI Review',\n title: 'AI Auto-Review',\n state: 'State',\n stateOn: 'ON',\n stateOff: 'OFF',\n enable: 'Enable',\n disable: 'Disable',\n verdicts: 'AI verdicts this turn',\n failures: 'Failures this turn',\n allTime: 'All-time',\n allows: 'allows',\n denies: 'denies',\n fallbacks: 'fallbacks',\n neverRejects: 'never rejects',\n avg: 'avg',\n circuit: 'Rejection circuit breaker tripped',\n circuitDetail: '{kind}: {count} denials, action {action}',\n empty: 'No AI review verdicts in this session yet.',\n recent: 'Recent verdicts',\n denyList: 'Approvable retries',\n approve: 'Approve retry',\n approveResult: 'Authorized: {text}',\n approveFailed: 'Approve failed: {text}',\n switchedResult: 'Switched: {text}',\n switchFailed: 'Switch failed: {text}',\n unavailable: 'The review panel is unavailable (the host does not provide the session-projection capability).',\n decisionAllow: 'allow',\n decisionDeny: 'deny',\n fallbackLabel: 'fallback ({kind})',\n escalationLabel: 'risk-policy escalation',\n riskLow: 'low risk',\n riskMedium: 'medium risk',\n riskHigh: 'high risk',\n ms: 'ms',\n}\n\n/** Dictionary namespace owned by this plugin. */\nexport const NS = 'autoReviewPanel'\n","/**\n * Scoped stylesheet for the review panel. Standalone bundles cannot use the\n * in-repo CSS-module pipeline, so the panel injects one `<style>` element\n * whose selectors are all scoped under `[data-dsh-auto-review]`.\n * @module dsh-auto-review/client/styles\n */\n\nconst STYLE_ID = 'dsh-auto-review-panel-styles'\n\nconst CSS = `\n[data-dsh-auto-review] {\n --ar-allow: var(--dsw-color-success, #16a34a);\n --ar-deny: var(--dsw-color-danger, #dc2626);\n --ar-fallback: var(--dsw-color-warning, #d97706);\n --ar-escalation: var(--dsw-color-warning, #d97706);\n --ar-border: var(--dsw-color-border, rgba(128, 128, 128, 0.3));\n --ar-bg: var(--dsw-color-surface, #ffffff);\n --ar-fg: var(--dsw-color-text, #1f2328);\n --ar-muted: var(--dsw-color-text-muted, #6b7280);\n font-family: inherit;\n color: var(--ar-fg);\n}\n[data-dsh-auto-review-panel] {\n position: absolute;\n z-index: 40;\n min-width: 320px;\n max-width: 420px;\n max-height: 70vh;\n overflow-y: auto;\n background: var(--ar-bg);\n border: 1px solid var(--ar-border);\n border-radius: 8px;\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);\n padding: 12px;\n font-size: 12px;\n line-height: 1.5;\n}\n[data-dsh-auto-review-button] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n cursor: pointer;\n background: transparent;\n border: 1px solid var(--ar-border);\n border-radius: 6px;\n padding: 3px 8px;\n font-size: 12px;\n color: var(--ar-fg);\n}\n[data-dsh-auto-review-button]:hover {\n background: rgba(128, 128, 128, 0.12);\n}\n[data-dsh-auto-review-section] {\n margin: 8px 0;\n border-top: 1px solid var(--ar-border);\n padding-top: 8px;\n}\n[data-dsh-auto-review-title] {\n font-weight: 600;\n margin-bottom: 6px;\n}\n[data-dsh-auto-review-row] {\n display: flex;\n justify-content: space-between;\n gap: 8px;\n padding: 2px 0;\n align-items: baseline;\n}\n[data-dsh-auto-review-muted] {\n color: var(--ar-muted);\n}\n[data-dsh-auto-review-verdict] {\n display: flex;\n justify-content: space-between;\n gap: 8px;\n padding: 3px 0;\n border-bottom: 1px dashed var(--ar-border);\n}\n[data-dsh-auto-review-tone-allow] { color: var(--ar-allow); }\n[data-dsh-auto-review-tone-deny] { color: var(--ar-deny); }\n[data-dsh-auto-review-tone-fallback] { color: var(--ar-fallback); }\n[data-dsh-auto-review-tone-escalation] { color: var(--ar-escalation); }\n[data-dsh-auto-review-approve] {\n cursor: pointer;\n border: 1px solid var(--ar-border);\n border-radius: 4px;\n background: transparent;\n color: var(--ar-fg);\n padding: 1px 6px;\n font-size: 11px;\n}\n[data-dsh-auto-review-approve]:hover {\n background: rgba(128, 128, 128, 0.12);\n}\n[data-dsh-auto-review-switch] {\n cursor: pointer;\n border: 1px solid var(--ar-border);\n border-radius: 4px;\n background: transparent;\n color: var(--ar-fg);\n padding: 1px 6px;\n font-size: 11px;\n}\n[data-dsh-auto-review-switch] + [data-dsh-auto-review-switch] {\n margin-left: 4px;\n}\n[data-dsh-auto-review-switch]:hover:not(:disabled) {\n background: rgba(128, 128, 128, 0.12);\n}\n[data-dsh-auto-review-approve]:disabled,\n[data-dsh-auto-review-switch]:disabled {\n opacity: 0.45;\n cursor: default;\n}\n[data-dsh-auto-review-circuit] {\n color: var(--ar-deny);\n font-weight: 600;\n}\n`\n\n/** Install the scoped stylesheet once per plugin fiber. */\nexport function installPanelStyles(): () => void {\n if (document.getElementById(STYLE_ID) !== null) return () => undefined\n const element = document.createElement('style')\n element.id = STYLE_ID\n element.textContent = CSS\n document.head.appendChild(element)\n return () => {\n element.remove()\n }\n}\n","/**\n * `dsh-auto-review`, browser half: registers the locale dictionaries, the\n * scoped stylesheet, and the session-header review panel action. All data\n * arrives through the `autoReview` session projection; the approve buttons\n * execute the `/auto-review approve [n]` command through the commands\n * Remote namespace.\n *\n * @module dsh-auto-review/client\n */\n\nimport type { ClientContext, SessionId } from '@deepseek-ai/dsh-client-runtime/client'\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\n// Type-only: pulls the 'conversation.session.header.actions' SlotMap\n// declaration into this program so the registration typechecks.\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\n// Type-only: merges the commands namespace onto ctx.remote.\nimport type {} from '@deepseek-ai/dsh-api-remotes/client'\nimport type {} from '../projection-types.ts'\nimport { ReviewPanel, type ReviewPanelInjected } from './ReviewPanel.tsx'\nimport { en, NS, zh, type ReviewPanelLocaleKey } from './locales.ts'\nimport { installPanelStyles } from './styles.ts'\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** Review panel copy. */\n 'autoReviewPanel': ReviewPanelLocaleKey\n }\n}\n\nexport type { ReviewPanelProps, ReviewPanelInjected } from './ReviewPanel.tsx'\nexport type { ReviewPanelLocaleKey } from './locales.ts'\nexport { presentVerdict, type PresentedVerdict, type VerdictTone } from './present.ts'\n\n/** Plugin name: matches the package name, the graph row id, and the bundle id. */\nexport const name = 'dsh-auto-review'\n\n/** Services the panel reads; `remote.commands` is mounted by the api-remotes client. */\nexport const inject = ['slots', 'locale', 'remote', 'remote.commands']\n\n/**\n * The structural shape of the api-remotes `commands` Remote namespace the\n * panel needs. Declared locally because the ambient namespace merge onto\n * `TypertClientRemote` is assembled from several generated modules and can\n * resolve to a different physical copy under strict package managers —\n * the runtime contract is what this client depends on, so the type is\n * pinned to it explicitly (rc.6 and rc.7 both serve this shape).\n */\ninterface CommandsRemote {\n readonly execute: (\n agentId: SessionId,\n line: string,\n signal?: AbortSignal,\n ) => Promise<\n | { ok: false; error: { code: string; message: string } }\n | { ok: true; value?: { result?: { kind?: string; text?: string } } }\n >\n}\n\n/**\n * Browser plugin body: dictionaries, the scoped stylesheet, and the\n * session-header action registration.\n * @param ctx - client root context.\n */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-auto-review: dictionaries')\n ctx.effect(() => installPanelStyles(), 'dsh-auto-review: stylesheet')\n ctx.slots.inject(\n 'conversation.session.header.actions',\n () => ctx.slots.register({\n name: 'conversation.session.header.actions',\n id: 'auto-review',\n order: 40,\n locale: NS,\n inject: (sessionId: SessionId): ReviewPanelInjected => {\n const remote = ctx.remote as unknown as { commands: CommandsRemote }\n const executeCommand = async (line: string): Promise<string> => {\n const result = await remote.commands.execute(sessionId, line)\n if (!result.ok) throw new Error(`${result.error.code}: ${result.error.message}`)\n if (result.value === undefined) throw new Error('unknown command: /auto-review')\n const command = result.value.result\n if (command === undefined) throw new Error('/auto-review produced no command result')\n const text = command.text ?? ''\n return command.kind === 'success' ? text : `${command.kind}: ${text}`\n }\n return {\n approve: async (n: number): Promise<string> => executeCommand(`/auto-review approve ${n}`),\n setEnabled: async (enabled: boolean): Promise<string> => executeCommand(`/auto-review ${enabled ? 'on' : 'off'}`),\n }\n },\n }, ReviewPanel),\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":["useState","useRef"],"sources":["../src/client/present.ts","../src/client/ReviewPanel.tsx","../src/client/locales.ts","../src/client/styles.ts","../src/client/index.ts"],"sourcesContent":["/**\n * Pure presentation for the review panel: verdict rows and badges as plain\n * strings and tone codes — no I/O, clock, or randomness. The component\n * owns layout and interaction; this module owns the text.\n * @module dsh-auto-review/client/present\n */\n\nimport type { AutoReviewVerdictView } from '../projection-types.ts'\n\n/** Badge tones the component maps to scoped styles. */\nexport type VerdictTone = 'allow' | 'deny' | 'fallback' | 'escalation'\n\n/** One presented verdict row. */\nexport interface PresentedVerdict {\n readonly reviewId: string\n readonly toolName: string\n readonly label: string\n readonly detail: string\n readonly tone: VerdictTone\n}\n\n/** Present one verdict row (the component supplies the localized label texts). */\nexport function presentVerdict(\n verdict: AutoReviewVerdictView,\n labels: {\n readonly allow: string\n readonly deny: string\n readonly fallback: (kind: string) => string\n readonly escalation: string\n },\n): PresentedVerdict {\n if (verdict.fallback !== undefined) {\n return {\n reviewId: verdict.reviewId,\n toolName: verdict.toolName,\n label: labels.fallback(verdict.fallback),\n detail: `${verdict.durationMs} ms`,\n tone: 'fallback',\n }\n }\n if (verdict.escalation === 'risk-policy') {\n return {\n reviewId: verdict.reviewId,\n toolName: verdict.toolName,\n label: labels.escalation,\n detail: verdict.reason ?? '',\n tone: 'escalation',\n }\n }\n const label = verdict.decision === 'deny' ? labels.deny : labels.allow\n return {\n reviewId: verdict.reviewId,\n toolName: verdict.toolName,\n label,\n detail: verdict.reason ?? `${verdict.durationMs} ms`,\n tone: verdict.decision === 'deny' ? 'deny' : 'allow',\n }\n}\n","/**\n * The session-header review panel: a button in the conversation header that\n * opens a popover with this session's auto-review state — the on/off switch,\n * budgets, cumulative statistics (including hard-disable rejections), the\n * circuit trip, recent verdicts, and one-shot approve buttons for recent\n * denials. Data arrives through the `autoReview` session projection;\n * switches and approves execute the `/auto-review` command through the\n * commands Remote.\n * @module dsh-auto-review/client/ReviewPanel\n */\n\nimport { useRef, useState, type ReactNode } from 'react'\nimport type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { AutoReviewProjection } from '../projection-types.ts'\nimport { presentVerdict } from './present.ts'\nimport { NS } from './locales.ts'\n\n/** Registration-side injected face: the approve action for one recent denial. */\nexport interface ReviewPanelInjected {\n /** Approve the n-th most recent denial (1 = most recent); resolves with the command result text. */\n approve: (n: number) => Promise<string>\n /** Switch the session's auto-review on/off (`/auto-review on|off`); resolves with the command result text. */\n setEnabled: (enabled: boolean) => Promise<string>\n}\n\n/** Full component props assembled by the session-header slot renderer. */\nexport type ReviewPanelProps =\n PropsRuntime<'conversation.session.header.actions'>\n & PropsLocale<typeof NS>\n & InjectFace<ReviewPanelInjected>\n\n/** Compact verdict-row badge label for the panel. */\nfunction verdictBadge(\n value: AutoReviewProjection | undefined,\n t: ReviewPanelProps['t'],\n): ReactNode {\n if (value === undefined) return t('unavailable')\n const fallback = (kind: string): string => t('fallbackLabel').replace('{kind}', kind)\n const labels = {\n allow: t('decisionAllow'),\n deny: t('decisionDeny'),\n fallback,\n escalation: t('escalationLabel'),\n }\n return value.recent.map(verdict => presentVerdict(verdict, labels)).map(row => (\n <div key={row.reviewId} data-dsh-auto-review-verdict>\n <span data-dsh-auto-review-muted>{row.toolName}</span>\n <span data-dsh-auto-review={`tone-${row.tone}`}>{row.label}</span>\n <span data-dsh-auto-review-muted>{row.detail}</span>\n </div>\n ))\n}\n\n/**\n * Render the review panel action.\n * @param props - framework kit (sessionId, useProjection), locale, and the approve face.\n * @returns the header button with its popover.\n */\nexport function ReviewPanel({ useProjection, approve, setEnabled, t }: ReviewPanelProps): ReactNode {\n const value = useProjection('autoReview')\n const [open, setOpen] = useState(false)\n const [notice, setNotice] = useState<string | null>(null)\n const anchor = useRef<HTMLDivElement | null>(null)\n const toggle = (): void => setOpen(previous => !previous)\n const runApprove = async (n: number): Promise<void> => {\n try {\n const text = await approve(n)\n setNotice(t('approveResult').replace('{text}', text))\n } catch (error: unknown) {\n setNotice(t('approveFailed').replace('{text}', error instanceof Error ? error.message : String(error)))\n }\n }\n const runSwitch = async (enabled: boolean): Promise<void> => {\n try {\n const text = await setEnabled(enabled)\n setNotice(t('switchedResult').replace('{text}', text))\n } catch (error: unknown) {\n setNotice(t('switchFailed').replace('{text}', error instanceof Error ? error.message : String(error)))\n }\n }\n return (\n <div data-dsh-auto-review ref={anchor}>\n <button type=\"button\" data-dsh-auto-review-button onClick={toggle}>\n {t('label')}\n </button>\n {open && (\n <div data-dsh-auto-review-panel>\n <div data-dsh-auto-review-title>{t('title')}</div>\n {value === undefined\n ? <div data-dsh-auto-review-muted>{t('unavailable')}</div>\n : (\n <div>\n <div data-dsh-auto-review-row>\n <span>{t('state')}</span>\n <span>{value.enabled ? t('stateOn') : t('stateOff')}</span>\n </div>\n <div data-dsh-auto-review-row>\n <span>{t('enable')} / {t('disable')}</span>\n <span>\n <button\n type=\"button\"\n data-dsh-auto-review-switch\n disabled={value.enabled}\n onClick={() => void runSwitch(true)}\n >\n {t('enable')}\n </button>\n <button\n type=\"button\"\n data-dsh-auto-review-switch\n disabled={!value.enabled}\n onClick={() => void runSwitch(false)}\n >\n {t('disable')}\n </button>\n </span>\n </div>\n <div data-dsh-auto-review-row>\n <span>{t('verdicts')}</span>\n <span>{value.verdictsUsed}</span>\n </div>\n <div data-dsh-auto-review-row>\n <span>{t('failures')}</span>\n <span>{value.failuresUsed}</span>\n </div>\n <div data-dsh-auto-review-row>\n <span>{t('allTime')}</span>\n <span>\n {value.allows} {t('allows')} · {value.denies} {t('denies')} · {value.fallbacks} {t('fallbacks')} · {value.neverRejects} {t('neverRejects')} · {t('avg')} {value.avgDurationMs} {t('ms')}\n </span>\n </div>\n {value.circuit !== null && (\n <div data-dsh-auto-review-circuit>\n {t('circuit')} — {t('circuitDetail')\n .replace('{kind}', value.circuit.trip.kind)\n .replace('{count}', String(value.circuit.trip.count))\n .replace('{action}', value.circuit.action)}\n </div>\n )}\n {value.recentDenies.length > 0 && (\n <div data-dsh-auto-review-section>\n <div data-dsh-auto-review-title>{t('denyList')}</div>\n {value.recentDenies.map((deny, index) => (\n <div key={deny.reviewId} data-dsh-auto-review-row>\n <span data-dsh-auto-review-muted>{deny.toolName}</span>\n <button type=\"button\" data-dsh-auto-review-approve onClick={() => void runApprove(index + 1)}>\n {t('approve')} #{index + 1}\n </button>\n </div>\n ))}\n </div>\n )}\n <div data-dsh-auto-review-section>\n <div data-dsh-auto-review-title>{t('recent')}</div>\n {value.recent.length === 0 ? <div data-dsh-auto-review-muted>{t('empty')}</div> : verdictBadge(value, t)}\n </div>\n </div>\n )}\n {notice !== null && <div data-dsh-auto-review-muted>{notice}</div>}\n </div>\n )}\n </div>\n )\n}\n","/**\n * Copy dictionaries for the auto-review session-header panel (en/zh — the\n * harness locale registry accepts these two UI language codes today).\n * @module dsh-auto-review/client/locales\n */\n\n/** Simplified Chinese dictionary and key source of truth. */\nexport const zh = {\n label: 'AI 审查',\n title: 'AI 自动审查',\n state: '状态',\n stateOn: '已开启',\n stateOff: '已关闭',\n enable: '开启',\n disable: '关闭',\n verdicts: '本回合 AI 裁决',\n failures: '本回合失败',\n allTime: '累计',\n allows: '放行',\n denies: '拒绝',\n fallbacks: '失败',\n neverRejects: '硬禁用拒绝',\n avg: '平均耗时',\n circuit: '拒绝熔断器已触发',\n circuitDetail: '触发类型:{kind},计数 {count},动作 {action}',\n empty: '本会话暂无 AI 审查裁决。',\n recent: '最近裁决',\n denyList: '可批准的重试',\n approve: '批准重试',\n approveResult: '已授权:{text}',\n approveFailed: '批准失败:{text}',\n switchedResult: '已切换:{text}',\n switchFailed: '切换失败:{text}',\n unavailable: '审查面板不可用(宿主未提供会话投影能力)。',\n decisionAllow: '放行',\n decisionDeny: '拒绝',\n fallbackLabel: '失败({kind})',\n escalationLabel: '风险策略升级',\n riskLow: '低风险',\n riskMedium: '中风险',\n riskHigh: '高风险',\n ms: 'ms',\n} satisfies Record<string, string>\n\n/** Auto-review panel locale key union. */\nexport type ReviewPanelLocaleKey = keyof typeof zh\n\n/** English dictionary checked against the Chinese key set. */\nexport const en: Record<ReviewPanelLocaleKey, string> = {\n label: 'AI Review',\n title: 'AI Auto-Review',\n state: 'State',\n stateOn: 'ON',\n stateOff: 'OFF',\n enable: 'Enable',\n disable: 'Disable',\n verdicts: 'AI verdicts this turn',\n failures: 'Failures this turn',\n allTime: 'All-time',\n allows: 'allows',\n denies: 'denies',\n fallbacks: 'fallbacks',\n neverRejects: 'never rejects',\n avg: 'avg',\n circuit: 'Rejection circuit breaker tripped',\n circuitDetail: '{kind}: {count} denials, action {action}',\n empty: 'No AI review verdicts in this session yet.',\n recent: 'Recent verdicts',\n denyList: 'Approvable retries',\n approve: 'Approve retry',\n approveResult: 'Authorized: {text}',\n approveFailed: 'Approve failed: {text}',\n switchedResult: 'Switched: {text}',\n switchFailed: 'Switch failed: {text}',\n unavailable: 'The review panel is unavailable (the host does not provide the session-projection capability).',\n decisionAllow: 'allow',\n decisionDeny: 'deny',\n fallbackLabel: 'fallback ({kind})',\n escalationLabel: 'risk-policy escalation',\n riskLow: 'low risk',\n riskMedium: 'medium risk',\n riskHigh: 'high risk',\n ms: 'ms',\n}\n\n/** Dictionary namespace owned by this plugin. */\nexport const NS = 'autoReviewPanel'\n","/**\n * Scoped stylesheet for the review panel. Standalone bundles cannot use the\n * in-repo CSS-module pipeline, so the panel injects one `<style>` element\n * whose selectors are all scoped under `[data-dsh-auto-review]`.\n * @module dsh-auto-review/client/styles\n */\n\nconst STYLE_ID = 'dsh-auto-review-panel-styles'\n\nconst CSS = `\n[data-dsh-auto-review] {\n --ar-allow: var(--dsw-color-success, #16a34a);\n --ar-deny: var(--dsw-color-danger, #dc2626);\n --ar-fallback: var(--dsw-color-warning, #d97706);\n --ar-escalation: var(--dsw-color-warning, #d97706);\n --ar-border: var(--dsw-color-border, rgba(128, 128, 128, 0.3));\n --ar-bg: var(--dsw-color-surface, #ffffff);\n --ar-fg: var(--dsw-color-text, #1f2328);\n --ar-muted: var(--dsw-color-text-muted, #6b7280);\n font-family: inherit;\n color: var(--ar-fg);\n}\n[data-dsh-auto-review-panel] {\n position: absolute;\n z-index: 40;\n min-width: 320px;\n max-width: 420px;\n max-height: 70vh;\n overflow-y: auto;\n background: var(--ar-bg);\n border: 1px solid var(--ar-border);\n border-radius: 8px;\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);\n padding: 12px;\n font-size: 12px;\n line-height: 1.5;\n}\n[data-dsh-auto-review-button] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n cursor: pointer;\n background: transparent;\n border: 1px solid var(--ar-border);\n border-radius: 6px;\n padding: 3px 8px;\n font-size: 12px;\n color: var(--ar-fg);\n}\n[data-dsh-auto-review-button]:hover {\n background: rgba(128, 128, 128, 0.12);\n}\n[data-dsh-auto-review-section] {\n margin: 8px 0;\n border-top: 1px solid var(--ar-border);\n padding-top: 8px;\n}\n[data-dsh-auto-review-title] {\n font-weight: 600;\n margin-bottom: 6px;\n}\n[data-dsh-auto-review-row] {\n display: flex;\n justify-content: space-between;\n gap: 8px;\n padding: 2px 0;\n align-items: baseline;\n}\n[data-dsh-auto-review-muted] {\n color: var(--ar-muted);\n}\n[data-dsh-auto-review-verdict] {\n display: flex;\n justify-content: space-between;\n gap: 8px;\n padding: 3px 0;\n border-bottom: 1px dashed var(--ar-border);\n}\n[data-dsh-auto-review-tone-allow] { color: var(--ar-allow); }\n[data-dsh-auto-review-tone-deny] { color: var(--ar-deny); }\n[data-dsh-auto-review-tone-fallback] { color: var(--ar-fallback); }\n[data-dsh-auto-review-tone-escalation] { color: var(--ar-escalation); }\n[data-dsh-auto-review-approve] {\n cursor: pointer;\n border: 1px solid var(--ar-border);\n border-radius: 4px;\n background: transparent;\n color: var(--ar-fg);\n padding: 1px 6px;\n font-size: 11px;\n}\n[data-dsh-auto-review-approve]:hover {\n background: rgba(128, 128, 128, 0.12);\n}\n[data-dsh-auto-review-switch] {\n cursor: pointer;\n border: 1px solid var(--ar-border);\n border-radius: 4px;\n background: transparent;\n color: var(--ar-fg);\n padding: 1px 6px;\n font-size: 11px;\n}\n[data-dsh-auto-review-switch] + [data-dsh-auto-review-switch] {\n margin-left: 4px;\n}\n[data-dsh-auto-review-switch]:hover:not(:disabled) {\n background: rgba(128, 128, 128, 0.12);\n}\n[data-dsh-auto-review-approve]:disabled,\n[data-dsh-auto-review-switch]:disabled {\n opacity: 0.45;\n cursor: default;\n}\n[data-dsh-auto-review-circuit] {\n color: var(--ar-deny);\n font-weight: 600;\n}\n`\n\n/** Install the scoped stylesheet once per plugin fiber. */\nexport function installPanelStyles(): () => void {\n if (document.getElementById(STYLE_ID) !== null) return () => undefined\n const element = document.createElement('style')\n element.id = STYLE_ID\n element.textContent = CSS\n document.head.appendChild(element)\n return () => {\n element.remove()\n }\n}\n","/**\n * `dsh-auto-review`, browser half: registers the locale dictionaries, the\n * scoped stylesheet, and the session-header review panel action. All data\n * arrives through the `autoReview` session projection; the approve buttons\n * execute the `/auto-review approve [n]` command through the commands\n * Remote namespace.\n *\n * @module dsh-auto-review/client\n */\n\nimport type { ClientContext, SessionId } from '@deepseek-ai/dsh-client-runtime/client'\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\n// Type-only: pulls the 'conversation.session.header.actions' SlotMap\n// declaration into this program so the registration typechecks.\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\n// Type-only: merges the commands namespace onto ctx.remote.\nimport type {} from '@deepseek-ai/dsh-api-remotes/client'\nimport type {} from '../projection-types.ts'\nimport { ReviewPanel, type ReviewPanelInjected } from './ReviewPanel.tsx'\nimport { en, NS, zh, type ReviewPanelLocaleKey } from './locales.ts'\nimport { installPanelStyles } from './styles.ts'\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** Review panel copy. */\n 'autoReviewPanel': ReviewPanelLocaleKey\n }\n}\n\nexport type { ReviewPanelProps, ReviewPanelInjected } from './ReviewPanel.tsx'\nexport type { ReviewPanelLocaleKey } from './locales.ts'\nexport { presentVerdict, type PresentedVerdict, type VerdictTone } from './present.ts'\n\n/** Plugin name: matches the package name, the graph row id, and the bundle id. */\nexport const name = 'dsh-auto-review'\n\n/** Services the panel reads; `remote.commands` is mounted by the api-remotes client. */\nexport const inject = ['slots', 'locale', 'remote', 'remote.commands']\n\n/**\n * The structural shape of the api-remotes `commands` Remote namespace the\n * panel needs. Declared locally because the ambient namespace merge onto\n * `TypertClientRemote` is assembled from several generated modules and can\n * resolve to a different physical copy under strict package managers —\n * the runtime contract is what this client depends on, so the type is\n * pinned to it explicitly (rc.6 and rc.7 both serve this shape).\n */\ninterface CommandsRemote {\n readonly execute: (\n agentId: SessionId,\n line: string,\n images?: readonly unknown[],\n ) => Promise<\n | { ok: false; error: { code: string; message: string } }\n | { ok: true; value?: { result?: { kind?: string; text?: string } } }\n >\n}\n\n/**\n * Browser plugin body: dictionaries, the scoped stylesheet, and the\n * session-header action registration.\n * @param ctx - client root context.\n */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-auto-review: dictionaries')\n ctx.effect(() => installPanelStyles(), 'dsh-auto-review: stylesheet')\n ctx.slots.inject(\n 'conversation.session.header.actions',\n () => ctx.slots.register({\n name: 'conversation.session.header.actions',\n id: 'auto-review',\n order: 40,\n locale: NS,\n inject: (sessionId: SessionId): ReviewPanelInjected => {\n const remote = ctx.remote as unknown as { commands: CommandsRemote }\n const executeCommand = async (line: string): Promise<string> => {\n const result = await remote.commands.execute(sessionId, line, [])\n if (!result.ok) throw new Error(`${result.error.code}: ${result.error.message}`)\n if (result.value === undefined) throw new Error('unknown command: /auto-review')\n const command = result.value.result\n if (command === undefined) throw new Error('/auto-review produced no command result')\n const text = command.text ?? ''\n return command.kind === 'success' ? text : `${command.kind}: ${text}`\n }\n return {\n approve: async (n: number): Promise<string> => executeCommand(`/auto-review approve ${n}`),\n setEnabled: async (enabled: boolean): Promise<string> => executeCommand(`/auto-review ${enabled ? 'on' : 'off'}`),\n }\n },\n }, ReviewPanel),\n )\n}\n"],"mappings":"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"}
package/lib/eval-cli.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as EvalEngine, i as writeReports, o as evalReviewConfig, r as renderTerminalSummary, t as exitCodeFor, w as parseSuite } from "./report-DERBXGXW.js";
1
+ import { A as parseSuite, a as EvalEngine, i as writeReports, o as evalReviewConfig, r as renderTerminalSummary, t as exitCodeFor } from "./report-NzSUyIog.js";
2
2
  import { mkdir, readFile, readdir } from "node:fs/promises";
3
3
  import { dirname, isAbsolute, join, resolve } from "node:path";
4
4
  import { existsSync, readFileSync, statSync } from "node:fs";
package/lib/eval.js CHANGED
@@ -1,2 +1,2 @@
1
- import { C as SuiteSchema, E as resolveCaseTimeout, S as DslError, T as resolveCaseModel, _ as runAssertions, a as EvalEngine, b as collectTrace, c as sessionHeaderLine, d as buildEvalReviewPrompt, f as isEvalReviewFailure, g as renderArgumentExpectation, h as deepContains, i as writeReports, l as summarizeCaseResult, m as argumentsMatchRegexes, n as renderMarkdownReport, o as evalReviewConfig, p as runEvalReview, r as renderTerminalSummary, s as renderSessionArtifact, t as exitCodeFor, u as EVAL_VERDICT_SCHEMA, v as toolNameMatches, w as parseSuite, x as renderToolCall, y as validateExpectations } from "./report-DERBXGXW.js";
2
- export { DslError, EVAL_VERDICT_SCHEMA, EvalEngine, SuiteSchema, argumentsMatchRegexes, buildEvalReviewPrompt, collectTrace, deepContains, evalReviewConfig, exitCodeFor, isEvalReviewFailure, parseSuite, renderArgumentExpectation, renderMarkdownReport, renderSessionArtifact, renderTerminalSummary, renderToolCall, resolveCaseModel, resolveCaseTimeout, runAssertions, runEvalReview, sessionHeaderLine, summarizeCaseResult, toolNameMatches, validateExpectations, writeReports };
1
+ import { A as parseSuite, C as diffLines, D as renderToolCall, E as collectTrace, M as resolveCaseTimeout, O as DslError, S as percentile, T as renderSideBySide, _ as renderArgumentExpectation, a as EvalEngine, b as validateExpectations, c as resolvePromptBaselines, d as EVAL_VERDICT_SCHEMA, f as buildEvalReviewPrompt, g as deepContains, h as argumentsMatchRegexes, i as writeReports, j as resolveCaseModel, k as SuiteSchema, l as sessionHeaderLine, m as runEvalReview, n as renderMarkdownReport, o as evalReviewConfig, p as isEvalReviewFailure, r as renderTerminalSummary, s as renderSessionArtifact, t as exitCodeFor, u as summarizeCaseResult, v as runAssertions, w as hasChanges, x as computeStressMetrics, y as toolNameMatches } from "./report-NzSUyIog.js";
2
+ export { DslError, EVAL_VERDICT_SCHEMA, EvalEngine, SuiteSchema, argumentsMatchRegexes, buildEvalReviewPrompt, collectTrace, computeStressMetrics, deepContains, diffLines, evalReviewConfig, exitCodeFor, hasChanges, isEvalReviewFailure, parseSuite, percentile, renderArgumentExpectation, renderMarkdownReport, renderSessionArtifact, renderSideBySide, renderTerminalSummary, renderToolCall, resolveCaseModel, resolveCaseTimeout, resolvePromptBaselines, runAssertions, runEvalReview, sessionHeaderLine, summarizeCaseResult, toolNameMatches, validateExpectations, writeReports };