dsh-permission-rules 0.2.0 → 0.3.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 CHANGED
@@ -2,6 +2,27 @@
2
2
 
3
3
  All notable changes to dsh-permission-rules are recorded here, newest first.
4
4
 
5
+ ## v0.3.0 — 2026-08-14
6
+
7
+ ### Rules vocabulary
8
+
9
+ - New `match.agents` dimension: selector globs (`main`, `subagent`, `preset:<name>`) matched against the caller's session-header identity, ANDed with the other dimensions; unknown identity never matches (fail closed), so agent-scoped rules cannot leak onto unidentified callers.
10
+ - Path normalization: a candidate equal to the workspace root itself is dropped explicitly, and the root comparison only ignores case when `caseInsensitivePaths` is on.
11
+
12
+ ### Dry-run rollout and audit fidelity
13
+
14
+ - `enforce: false` dry-run mode: deny/ask hits are audit-logged with a `dryRun` marker — the record keeps the would-be action AND the real downstream outcome — while every call is delegated via `next()`. `/rules` prints a dry-run notice while the mode is active and `/rules decisions` renders `(dry-run → <outcome>)` on such rows. Trial a new policy in production before enforcing it.
15
+ - `permissionRules/decision` events now carry `cwd`, the workspace the rule chain was resolved for.
16
+
17
+ ### Commands and observability
18
+
19
+ - `/rules test` accepts leading flags: `--cwd <dir>` (evaluate against another workspace's rules), `--env KEY=VALUE` (repeatable; overrides host env for `when.env`), and `--agent <selector>` (repeatable; supplies identity candidates for the `agents` dimension). Quoted JSON argument tails are preserved verbatim.
20
+ - `resolveConfig` validates the closed enums (`badFilePolicy`, `patternMode`, `language`, `audit`) and boolean flags loudly, so plain-JS mounts without the Schemastery loader also fail on bad values instead of crashing later.
21
+
22
+ ### Engineering
23
+
24
+ - 125 tests across 8 suites; coverage gate held. Five-language READMEs, both rules-format references, the JSON Schema, and the `/rules` command hint updated for `agents` and `enforce`.
25
+
5
26
  ## v0.2.0 — 2026-08-14
6
27
 
7
28
  ### Rules vocabulary
package/README.es.md CHANGED
@@ -48,13 +48,14 @@ Un segundo modelo responde *"¿es segura ESTA llamada?"* con criterio, pero cues
48
48
  ## Características
49
49
 
50
50
  - ✅ **Semántica de tres estados** — `allow`, `deny`, `ask`, evaluadas en orden de archivo; gana la primera coincidencia
51
- - ✅ **Coincidencia rica** — globs de nombre de herramienta (incluido `mcp__*`), globs **o** regex de clave/valor de argumentos (con negación `!pattern` y la dimensión de clave `absent`), globs de rutas relativas al workspace extraídos de claves de argumentos documentadas a **cualquier profundidad de anidamiento**, y condiciones de host `when` (variables de entorno, plataforma)
51
+ - ✅ **Coincidencia rica** — globs de nombre de herramienta (incluido `mcp__*`), selectores de identidad de agente (`main` / `subagent` / `preset:*`), globs **o** regex de clave/valor de argumentos (con negación `!pattern` y la dimensión de clave `absent`), globs de rutas relativas al workspace extraídos de claves de argumentos documentadas a **cualquier profundidad de anidamiento**, y condiciones de host `when` (variables de entorno, plataforma)
52
52
  - ✅ **Archivos de reglas jerárquicos** — `searchUp` opcional combina cada `.dsh/rules.yaml` desde el cwd de la sesión hasta la raíz del sistema de archivos, el más cercano primero, para que un proyecto hijo pueda sobrescribir las reglas del padre
53
53
  - ✅ **Metadatos de reglas** — `enabled: false`, `description`, `tags`; `/rules` advierte sobre reglas eclipsadas por un catch-all anterior
54
54
  - ✅ **Seguro para el waterfall** — `allow`/paso siempre llaman a `next()`; solo `deny`/`ask` cortocircuitan
55
55
  - ✅ **Seam de aprobación oficial** — `ask` fluye por `ctx.approval`; nunca reimplementado, nunca eludido
56
- - ✅ **Auditoría completa** — los eventos `permissionRules/decision` llevan la acción de la regla Y el resultado final de cada llamada; `/rules decisions` reproduce el rastro en la sesión
57
- - ✅ **Prueba en seco** — `/rules test <tool> <json-args>` evalúa las reglas activas sin ejecutar nada
56
+ - ✅ **Auditoría completa** — los eventos `permissionRules/decision` llevan la acción de la regla, el cwd del workspace Y el resultado final de cada llamada; `/rules decisions` reproduce el rastro en la sesión
57
+ - ✅ **Despliegue en simulación** — `enforce: false` audita lo que la política *haría* (acción hipotética + resultado real posterior, marcado `dryRun`) mientras deja pasar todas las llamadas; prueba segura de políticas en producción
58
+ - ✅ **Prueba en seco** — `/rules test <tool> <json-args>` evalúa las reglas activas sin ejecutar nada, con sobrescrituras `--cwd`, `--env` y `--agent` para cada dimensión
58
59
  - ✅ **Recarga en caliente** — vigilancia Chokidar con debounce; una edición rota conserva las reglas previas, nunca falla
59
60
  - ✅ **Fallo ruidoso** — YAML inválido, acciones/campos desconocidos, globs/regex incorrectos, patrones propensos a backtracking o > `maxRules` hacen fallar la carga
60
61
  - ✅ **Ruta caliente acotada** — matchers precompilados, O(reglas × patrones), limitado por `maxRules`; el grado de backtracking de glob limitado por `maxGlobStars`
@@ -67,7 +68,7 @@ dsh plugin --profile web add "github:PerryLink/dsh-permission-rules#main"
67
68
 
68
69
  # o desde un tarball empaquetado (artefactos compilados, sin permiso de build)
69
70
  pnpm pack
70
- dsh plugin --profile web add ./dsh-permission-rules-0.1.1.tgz
71
+ dsh plugin --profile web add ./dsh-permission-rules-0.3.0.tgz
71
72
 
72
73
  # 2. reinicia
73
74
  dsh --profile web
@@ -112,6 +113,7 @@ Todos los ajustes son campos `Config` de Schemastery (modificables desde cordis.
112
113
  | `audit` | `all` | Granularidad de auditoría: `all` registra cada acierto Y cada paso; `hits` omite los eventos de paso |
113
114
  | `searchUp` | `false` | Recorre los directorios padre desde el cwd de la sesión y combina cada archivo de reglas encontrado, el más cercano primero |
114
115
  | `maxGlobStars` | `2` | Límite duro de cuantificadores `*`/`**` sin límite por patrón glob (cota del grado de backtracking) |
116
+ | `enforce` | `true` | `false` = modo simulación: los aciertos deny/ask solo se registran en auditoría con un marcador `dryRun` (acción hipotética + resultado real posterior) y todas las llamadas pasan — prueba una política antes de aplicarla |
115
117
 
116
118
  ### Comandos de sesión
117
119
 
@@ -122,6 +124,8 @@ Todos los ajustes son campos `Config` de Schemastery (modificables desde cordis.
122
124
  /rules test <tool> <json> evalúa en seco las reglas contra una llamada hipotética, p. ej. /rules test bash {"command":"git push origin main"}
123
125
  ```
124
126
 
127
+ `/rules test` también acepta banderas iniciales: `--cwd <dir>` evalúa contra otro workspace, `--env CLAVE=VALOR` (repetible) sobrescribe el entorno para `when.env`, y `--agent <selector>` (repetible) suministra candidatos de identidad para la dimensión `agents`.
128
+
125
129
  La salida de los comandos es solo UI: el modelo aprende las reglas únicamente a través de los resultados de herramienta que producen. `language` elige el idioma de salida. Un JSON Schema del archivo de reglas se distribuye en [docs/rules-format.schema.json](docs/rules-format.schema.json) (conéctalo con `# yaml-language-server: $schema=...` para autocompletado en el editor).
126
130
 
127
131
  ## Colaboración con dsh-auto-review
package/README.hi.md CHANGED
@@ -48,13 +48,14 @@ tools/pre-execute waterfall approval/request waterfall (answ
48
48
  ## विशेषताएँ
49
49
 
50
50
  - ✅ **तीन-अवस्था शब्दार्थ** — `allow`, `deny`, `ask`, फ़ाइल क्रम में मूल्यांकित; पहला मिलान जीतता है
51
- - ✅ **समृद्ध मिलान** — टूल-नाम globs (`mcp__*` सहित), आर्गुमेंट कुंजी/मान globs **या** regex (`!pattern` निषेध और `absent` कुंजी आयाम के साथ), दस्तावेज़ित आर्गुमेंट कुंजियों से **किसी भी नेस्टिंग गहराई** पर निकाले गए workspace-सापेक्ष पथ globs, और `when` होस्ट शर्तें (env vars, प्लेटफ़ॉर्म)
51
+ - ✅ **समृद्ध मिलान** — टूल-नाम globs (`mcp__*` सहित), एजेंट-पहचान चयनकर्ता (`main` / `subagent` / `preset:*`), आर्गुमेंट कुंजी/मान globs **या** regex (`!pattern` निषेध और `absent` कुंजी आयाम के साथ), दस्तावेज़ित आर्गुमेंट कुंजियों से **किसी भी नेस्टिंग गहराई** पर निकाले गए workspace-सापेक्ष पथ globs, और `when` होस्ट शर्तें (env vars, प्लेटफ़ॉर्म)
52
52
  - ✅ **पदानुक्रमित नियम फ़ाइलें** — वैकल्पिक `searchUp` सत्र cwd से फ़ाइलसिस्टम रूट तक हर `.dsh/rules.yaml` को मिलाता है, निकटतम पहले, ताकि चाइल्ड प्रोजेक्ट पैरेंट नियमों को ओवरराइड कर सके
53
53
  - ✅ **नियम मेटाडेटा** — `enabled: false`, `description`, `tags`; `/rules` पहले किसी catch-all से छिपे नियमों की चेतावनी देता है
54
54
  - ✅ **waterfall-सुरक्षित** — `allow`/पास-थ्रू हमेशा `next()` बुलाते हैं; केवल `deny`/`ask` शॉर्ट-सर्किट करते हैं
55
55
  - ✅ **आधिकारिक approval seam** — `ask` `ctx.approval` से होकर जाता है; कभी पुनः-कार्यान्वित नहीं, कभी बायपास नहीं
56
- - ✅ **पूर्ण ऑडिट** — `permissionRules/decision` इवेंट हर कॉल के लिए नियम क्रिया और अंतिम परिणाम दोनों रखते हैं; `/rules decisions` सत्र में रास्ता फिर चलाता है
57
- - ✅ **dry-run परीक्षण** — `/rules test <tool> <json-args>` कुछ भी निष्पादित किए बिना सक्रिय नियमों का मूल्यांकन करता है
56
+ - ✅ **पूर्ण ऑडिट** — `permissionRules/decision` इवेंट हर कॉल के लिए नियम क्रिया, workspace cwd और अंतिम परिणाम रखते हैं; `/rules decisions` सत्र में रास्ता फिर चलाता है
57
+ - ✅ **dry-run रोलआउट** — `enforce: false` केवल ऑडिट करता है कि नीति *क्या करती* (काल्पनिक क्रिया + वास्तविक डाउनस्ट्रीम परिणाम, `dryRun` चिह्नित) और हर कॉल को पास करता है; उत्पादन में नई नीति का सुरक्षित परीक्षण
58
+ - ✅ **dry-run परीक्षण** — `/rules test <tool> <json-args>` कुछ भी निष्पादित किए बिना सक्रिय नियमों का मूल्यांकन करता है, हर मिलान आयाम के लिए `--cwd`, `--env`, और `--agent` ओवरराइड के साथ
58
59
  - ✅ **हॉट रीलोड** — debounce सहित Chokidar निगरानी; टूटा हुआ संपादन पिछले नियम रखता है, कभी क्रैश नहीं करता
59
60
  - ✅ **ज़ोरदार विफलता** — अमान्य YAML, अज्ञात action/फ़ील्ड, ख़राब globs/regex, बैकट्रैकिंग-प्रवण पैटर्न, या `maxRules` से अधिक → लोड विफल
60
61
  - ✅ **सीमित हॉट पथ** — पूर्व-संकलित matchers, O(नियम × पैटर्न), `maxRules` द्वारा सीमित; glob बैकट्रैकिंग डिग्री `maxGlobStars` द्वारा सीमित
@@ -67,7 +68,7 @@ dsh plugin --profile web add "github:PerryLink/dsh-permission-rules#main"
67
68
 
68
69
  # या पैक किए गए tarball से (बिल्ट आर्टिफ़ैक्ट, build अनुमति की ज़रूरत नहीं)
69
70
  pnpm pack
70
- dsh plugin --profile web add ./dsh-permission-rules-0.1.1.tgz
71
+ dsh plugin --profile web add ./dsh-permission-rules-0.3.0.tgz
71
72
 
72
73
  # 2. पुनः आरंभ करें
73
74
  dsh --profile web
@@ -112,6 +113,7 @@ dsh --profile web --dump-config | grep -A4 'id: permission-rules' # पंक
112
113
  | `audit` | `all` | ऑडिट विवरण स्तर: `all` हर मिलान और पास-थ्रू लॉग करता है; `hits` पास-थ्रू इवेंट छोड़ता है |
113
114
  | `searchUp` | `false` | सत्र cwd से पैरेंट निर्देशिकाओं पर चलकर हर मिली नियम फ़ाइल मिलाता है, निकटतम पहले |
114
115
  | `maxGlobStars` | `2` | प्रति glob पैटर्न असीमित `*`/`**` क्वांटिफ़ायर की कठोर सीमा (बैकट्रैकिंग-डिग्री सीमा) |
116
+ | `enforce` | `true` | `false` = dry-run मोड: deny/ask मिलान केवल ऑडिट में `dryRun` चिह्न के साथ दर्ज होते हैं (काल्पनिक क्रिया + वास्तविक डाउनस्ट्रीम परिणाम) और हर कॉल पास होती है — लागू करने से पहले नीति का परीक्षण करें |
115
117
 
116
118
  ### सेशन कमांड
117
119
 
@@ -122,6 +124,8 @@ dsh --profile web --dump-config | grep -A4 'id: permission-rules' # पंक
122
124
  /rules test <tool> <json> काल्पनिक कॉल के विरुद्ध नियमों का dry-मूल्यांकन, जैसे /rules test bash {"command":"git push origin main"}
123
125
  ```
124
126
 
127
+ `/rules test` शुरुआती फ़्लैग भी स्वीकार करता है: `--cwd <dir>` दूसरे workspace के विरुद्ध मूल्यांकन करता है, `--env KEY=मान` (दोहराने योग्य) `when.env` के लिए होस्ट env ओवरराइड करता है, और `--agent <चयनकर्ता>` (दोहराने योग्य) `agents` आयाम के लिए पहचान उम्मीदवार देता है।
128
+
125
129
  कमांड आउटपुट केवल UI के लिए है — मॉडल नियमों को केवल उनके उत्पन्न टूल परिणामों से ही सीखता है। `language` आउटपुट भाषा चुनता है। नियम फ़ाइल का JSON Schema [docs/rules-format.schema.json](docs/rules-format.schema.json) पर साथ आता है (संपादक पूर्णता के लिए `# yaml-language-server: $schema=...` से जोड़ें)।
126
130
 
127
131
  ## dsh-auto-review के साथ सहयोग
package/README.md CHANGED
@@ -48,13 +48,14 @@ A second model answers *"is THIS call okay?"* with judgment, but costs a round-t
48
48
  ## Features
49
49
 
50
50
  - ✅ **Three-state semantics** — `allow`, `deny`, `ask`, evaluated in file order, first match wins
51
- - ✅ **Rich matching** — tool-name globs (including `mcp__*`), argument key/value globs **or** regexes (with `!pattern` negation and an `absent` key dimension), workspace-relative path globs extracted from documented argument keys at **any nesting depth**, and `when` host conditions (env vars, platform)
51
+ - ✅ **Rich matching** — tool-name globs (including `mcp__*`), agent-identity selectors (`main` / `subagent` / `preset:*`), argument key/value globs **or** regexes (with `!pattern` negation and an `absent` key dimension), workspace-relative path globs extracted from documented argument keys at **any nesting depth**, and `when` host conditions (env vars, platform)
52
52
  - ✅ **Hierarchical rule files** — optional `searchUp` merges every `.dsh/rules.yaml` from the session cwd to the filesystem root, nearest first, so a child project can override parent rules
53
53
  - ✅ **Rule metadata** — `enabled: false`, `description`, `tags`; `/rules` warns about rules shadowed by an earlier catch-all
54
54
  - ✅ **Waterfall-safe** — `allow`/passthrough always call `next()`; only `deny`/`ask` short-circuit
55
55
  - ✅ **Official approval seam** — `ask` flows through `ctx.approval`; never re-implemented, never bypassed
56
- - ✅ **Full audit** — `permissionRules/decision` events carry the rule action AND the final outcome for every call; `/rules decisions` replays the trail in-session
57
- - ✅ **Dry-run testing** — `/rules test <tool> <json-args>` evaluates the active rules without executing anything
56
+ - ✅ **Full audit** — `permissionRules/decision` events carry the rule action, the workspace cwd, AND the final outcome for every call; `/rules decisions` replays the trail in-session
57
+ - ✅ **Dry-run rollout** — `enforce: false` audits what the policy *would* do (would-be action + real downstream outcome, `dryRun`-marked) while passing every call through; safe policy trialing in production
58
+ - ✅ **Dry-run testing** — `/rules test <tool> <json-args>` evaluates the active rules without executing anything, with `--cwd`, `--env`, and `--agent` overrides for every match dimension
58
59
  - ✅ **Hot reload** — Chokidar watch with debounce; a broken edit keeps the previous rules, never crashes
59
60
  - ✅ **Fail loud** — invalid YAML, unknown actions/fields, bad globs/regexes, backtracking-prone patterns, or > `maxRules` fail the load
60
61
  - ✅ **Bounded hot path** — precompiled matchers, O(rules × patterns), capped by `maxRules`; glob backtracking degree capped by `maxGlobStars`
@@ -67,7 +68,7 @@ dsh plugin --profile web add "github:PerryLink/dsh-permission-rules#main"
67
68
 
68
69
  # or from a packed tarball (built artifacts, no build permission needed)
69
70
  pnpm pack
70
- dsh plugin --profile web add ./dsh-permission-rules-0.1.1.tgz
71
+ dsh plugin --profile web add ./dsh-permission-rules-0.3.0.tgz
71
72
 
72
73
  # 2. restart
73
74
  dsh --profile web
@@ -112,6 +113,7 @@ All tunables are Schemastery `Config` fields (changeable from cordis.yml). An id
112
113
  | `audit` | `all` | Audit granularity: `all` logs every hit AND passthrough; `hits` skips passthrough events |
113
114
  | `searchUp` | `false` | Walk parent directories from the session cwd and merge every found rule file, nearest first |
114
115
  | `maxGlobStars` | `2` | Hard cap on unbounded `*`/`**` quantifiers per glob pattern (backtracking-degree bound) |
116
+ | `enforce` | `true` | `false` = dry-run mode: deny/ask hits are audit-logged with a `dryRun` marker (would-be action + real downstream outcome) and every call passes through — trial a policy before enforcing it |
115
117
 
116
118
  ### Session commands
117
119
 
@@ -122,6 +124,8 @@ All tunables are Schemastery `Config` fields (changeable from cordis.yml). An id
122
124
  /rules test <tool> <json> dry-evaluate the rules against a hypothetical call, e.g. /rules test bash {"command":"git push origin main"}
123
125
  ```
124
126
 
127
+ `/rules test` also accepts leading flags: `--cwd <dir>` evaluates against another workspace, `--env KEY=VALUE` (repeatable) overrides host env for `when.env`, and `--agent <selector>` (repeatable) supplies identity candidates for the `agents` dimension.
128
+
125
129
  Command output is UI-only — the model learns the rules only through the tool results they produce. `language` picks the output language. A JSON Schema for the rule file ships at [docs/rules-format.schema.json](docs/rules-format.schema.json) (wire it up with `# yaml-language-server: $schema=...` for editor completion).
126
130
 
127
131
  ## Collaborating with dsh-auto-review
package/README.pt.md CHANGED
@@ -48,13 +48,14 @@ Um segundo modelo responde *"ESTA chamada é segura?"* com julgamento, mas custa
48
48
  ## Funcionalidades
49
49
 
50
50
  - ✅ **Semântica de três estados** — `allow`, `deny`, `ask`, avaliadas na ordem do arquivo; a primeira correspondência vence
51
- - ✅ **Correspondência rica** — globs de nome de ferramenta (incluindo `mcp__*`), globs **ou** regex de chave/valor de argumentos (com negação `!pattern` e a dimensão de chave `absent`), globs de caminhos relativos ao workspace extraídos de chaves de argumentos documentadas em **qualquer profundidade de aninhamento**, e condições de host `when` (variáveis de ambiente, plataforma)
51
+ - ✅ **Correspondência rica** — globs de nome de ferramenta (incluindo `mcp__*`), seletores de identidade de agente (`main` / `subagent` / `preset:*`), globs **ou** regex de chave/valor de argumentos (com negação `!pattern` e a dimensão de chave `absent`), globs de caminhos relativos ao workspace extraídos de chaves de argumentos documentadas em **qualquer profundidade de aninhamento**, e condições de host `when` (variáveis de ambiente, plataforma)
52
52
  - ✅ **Arquivos de regras hierárquicos** — `searchUp` opcional combina cada `.dsh/rules.yaml` do cwd da sessão até a raiz do sistema de arquivos, o mais próximo primeiro, para que um projeto filho possa sobrescrever regras do pai
53
53
  - ✅ **Metadados de regras** — `enabled: false`, `description`, `tags`; `/rules` avisa sobre regras encobertas por um catch-all anterior
54
54
  - ✅ **Seguro para o waterfall** — `allow`/passagem sempre chamam `next()`; somente `deny`/`ask` curto-circuitam
55
55
  - ✅ **Seam de aprovação oficial** — `ask` flui por `ctx.approval`; nunca reimplementado, nunca contornado
56
- - ✅ **Auditoria completa** — os eventos `permissionRules/decision` carregam a ação da regra E o resultado final de cada chamada; `/rules decisions` reproduz o rastro na sessão
57
- - ✅ **Teste em seco** — `/rules test <tool> <json-args>` avalia as regras ativas sem executar nada
56
+ - ✅ **Auditoria completa** — os eventos `permissionRules/decision` carregam a ação da regra, o cwd do workspace E o resultado final de cada chamada; `/rules decisions` reproduz o rastro na sessão
57
+ - ✅ **Implantação em simulação** — `enforce: false` audita o que a política *faria* (ação hipotética + resultado real posterior, marcado com `dryRun`) enquanto deixa todas as chamadas passarem; teste seguro de políticas em produção
58
+ - ✅ **Teste em seco** — `/rules test <tool> <json-args>` avalia as regras ativas sem executar nada, com sobrescritas `--cwd`, `--env` e `--agent` para cada dimensão
58
59
  - ✅ **Recarga a quente** — vigilância Chokidar com debounce; uma edição quebrada mantém as regras anteriores, nunca quebra
59
60
  - ✅ **Falha ruidosa** — YAML inválido, ações/campos desconhecidos, globs/regex ruins, padrões propensos a backtracking ou > `maxRules` fazem a carga falhar
60
61
  - ✅ **Caminho quente limitado** — matchers pré-compilados, O(regras × padrões), limitado por `maxRules`; o grau de backtracking de glob limitado por `maxGlobStars`
@@ -67,7 +68,7 @@ dsh plugin --profile web add "github:PerryLink/dsh-permission-rules#main"
67
68
 
68
69
  # ou a partir de um tarball empacotado (artefatos compilados, sem permissão de build)
69
70
  pnpm pack
70
- dsh plugin --profile web add ./dsh-permission-rules-0.1.1.tgz
71
+ dsh plugin --profile web add ./dsh-permission-rules-0.3.0.tgz
71
72
 
72
73
  # 2. reinicie
73
74
  dsh --profile web
@@ -112,6 +113,7 @@ Todos os ajustes são campos `Config` do Schemastery (alteráveis no cordis.yml)
112
113
  | `audit` | `all` | Granularidade da auditoria: `all` registra cada acerto E passagem; `hits` pula eventos de passagem |
113
114
  | `searchUp` | `false` | Percorre os diretórios pais a partir do cwd da sessão e combina cada arquivo de regras encontrado, o mais próximo primeiro |
114
115
  | `maxGlobStars` | `2` | Limite rígido de quantificadores `*`/`**` ilimitados por padrão glob (cota do grau de backtracking) |
116
+ | `enforce` | `true` | `false` = modo simulação: acertos deny/ask são apenas registrados na auditoria com um marcador `dryRun` (ação hipotética + resultado real posterior) e todas as chamadas passam — teste uma política antes de aplicá-la |
115
117
 
116
118
  ### Comandos de sessão
117
119
 
@@ -122,6 +124,8 @@ Todos os ajustes são campos `Config` do Schemastery (alteráveis no cordis.yml)
122
124
  /rules test <tool> <json> avalia em seco as regras contra uma chamada hipotética, ex.: /rules test bash {"command":"git push origin main"}
123
125
  ```
124
126
 
127
+ `/rules test` também aceita sinalizadores iniciais: `--cwd <dir>` avalia contra outro workspace, `--env CHAVE=VALOR` (repetível) sobrescreve o ambiente para `when.env`, e `--agent <seletor>` (repetível) fornece candidatos de identidade para a dimensão `agents`.
128
+
125
129
  A saída dos comandos é somente UI — o modelo aprende as regras apenas pelos resultados de ferramenta que elas produzem. `language` escolhe o idioma da saída. Um JSON Schema do arquivo de regras é distribuído em [docs/rules-format.schema.json](docs/rules-format.schema.json) (conecte-o com `# yaml-language-server: $schema=...` para autocompletar no editor).
126
130
 
127
131
  ## Colaboração com o dsh-auto-review
package/README.zh.md CHANGED
@@ -48,13 +48,14 @@ tools/pre-execute waterfall approval/request waterfall(ans
48
48
  ## 特性
49
49
 
50
50
  - ✅ **三态语义** — `allow`、`deny`、`ask`,按文件顺序求值,首条命中生效
51
- - ✅ **丰富匹配** — 工具名 glob(含 `mcp__*`)、参数键值 glob **或** 正则(支持 `!pattern` 否定与 `absent` 键维度)、工作区相对路径 glob(从文档化参数键在**任意嵌套深度**提取候选),以及 `when` 宿主条件(环境变量、平台)
51
+ - ✅ **丰富匹配** — 工具名 glob(含 `mcp__*`)、代理身份选择器(`main` / `subagent` / `preset:*`)、参数键值 glob **或** 正则(支持 `!pattern` 否定与 `absent` 键维度)、工作区相对路径 glob(从文档化参数键在**任意嵌套深度**提取候选),以及 `when` 宿主条件(环境变量、平台)
52
52
  - ✅ **分层规则文件** — 可选 `searchUp` 从会话 cwd 向上逐级合并每份 `.dsh/rules.yaml`(最近的优先),子项目可覆盖父级规则
53
53
  - ✅ **规则元数据** — `enabled: false`、`description`、`tags`;`/rules` 会告警被前面通配规则遮蔽的规则
54
54
  - ✅ **waterfall 安全** — `allow`/透传一律 `next()`;只有 `deny`/`ask` 短路
55
55
  - ✅ **官方审批 seam** — `ask` 走 `ctx.approval`;不重复实现、绝不绕过
56
- - ✅ **完整审计** — `permissionRules/decision` 事件同时记录规则动作与最终裁决;`/rules decisions` 可在会话内回放审计轨迹
57
- - ✅ **干跑测试** — `/rules test <工具> <json-参数>` 不执行任何东西地评估规则命中
56
+ - ✅ **完整审计** — `permissionRules/decision` 事件记录规则动作、工作区 cwd 与最终裁决;`/rules decisions` 可在会话内回放审计轨迹
57
+ - ✅ **干跑上线** — `enforce: false` 只审计策略「本会做什么」(记录本会动作 + 下游真实结果,带 `dryRun` 标记),所有调用照常透传;可在生产环境安全试跑新策略
58
+ - ✅ **干跑测试** — `/rules test <工具> <json-参数>` 不执行任何东西地评估规则命中,支持 `--cwd`、`--env`、`--agent` 覆盖每个匹配维度
58
59
  - ✅ **热更新** — Chokidar 监视 + 去抖;改坏了保留旧规则,绝不崩溃
59
60
  - ✅ **响亮失败** — 非法 YAML、未知字段/action、坏 glob/正则、易回溯灾难的模式、超过 `maxRules` 都使加载失败
60
61
  - ✅ **热路径有界** — 预编译匹配器,O(规则数 × 模式数),`maxRules` 封顶;glob 回溯度由 `maxGlobStars` 封顶
@@ -67,7 +68,7 @@ dsh plugin --profile web add "github:PerryLink/dsh-permission-rules#main"
67
68
 
68
69
  # 或从打包好的 tarball 安装(预构建产物,无需构建许可)
69
70
  pnpm pack
70
- dsh plugin --profile web add ./dsh-permission-rules-0.1.1.tgz
71
+ dsh plugin --profile web add ./dsh-permission-rules-0.3.0.tgz
71
72
 
72
73
  # 2. 重启
73
74
  dsh --profile web
@@ -112,6 +113,7 @@ dsh --profile web --dump-config | grep -A4 'id: permission-rules' # 验证挂
112
113
  | `audit` | `all` | 审计粒度:`all` 记录每次命中与透传;`hits` 跳过透传事件 |
113
114
  | `searchUp` | `false` | 从会话 cwd 向上逐级查找并合并规则文件,最近的优先 |
114
115
  | `maxGlobStars` | `2` | 每个 glob 模式的无界 `*`/`**` 量词数硬上界(回溯度封顶) |
116
+ | `enforce` | `true` | `false` = 干跑模式:deny/ask 命中只写审计(带 `dryRun` 标记,记录本会动作与下游真实结果),所有调用照常透传——先试跑策略再强制执行 |
115
117
 
116
118
  ### 会话命令
117
119
 
@@ -122,6 +124,8 @@ dsh --profile web --dump-config | grep -A4 'id: permission-rules' # 验证挂
122
124
  /rules test <工具> <json> 对假设调用干跑评估,如 /rules test bash {"command":"git push origin main"}
123
125
  ```
124
126
 
127
+ `/rules test` 还支持前置标志:`--cwd <目录>` 换一个工作区评估,`--env 键=值`(可重复)覆盖宿主环境变量以测试 `when.env`,`--agent <选择器>`(可重复)为 `agents` 维度提供身份候选。
128
+
125
129
  命令输出只进 UI——模型只通过规则产生的工具结果感知规则。`language` 选择输出语言。规则文件的 JSON Schema 随包发布在 [docs/rules-format.schema.json](docs/rules-format.schema.json)(用 `# yaml-language-server: $schema=...` 启用编辑器补全)。
126
130
 
127
131
  ## 与 dsh-auto-review 协作
@@ -17,13 +17,14 @@ rules: # rule list, evaluated in written order; may be omitted or empty
17
17
  reason: ...
18
18
  ```
19
19
 
20
- Only `rules` is allowed at the top level; each rule allows only `match`, `action`, `reason`, `enabled`, `description`, `tags`; each `match` allows only `tools`, `params`, `paths`, `absent`, `when`. Any unknown field fails the load loudly — never silently ignored.
20
+ Only `rules` is allowed at the top level; each rule allows only `match`, `action`, `reason`, `enabled`, `description`, `tags`; each `match` allows only `tools`, `agents`, `params`, `paths`, `absent`, `when`. Any unknown field fails the load loudly — never silently ignored.
21
21
 
22
22
  ## match dimensions (AND)
23
23
 
24
24
  | Dimension | Type | Semantics |
25
25
  |---|---|---|
26
26
  | `tools` | `string[]` | Tool-name globs (always globs, regardless of `patternMode`). Empty/absent = unrestricted. Supports prefixes like `mcp__*`. |
27
+ | `agents` | `string[]` | Agent-identity selector globs against the caller's session header candidates: `main` (top-level sessions), `subagent` (subagent children), and `preset:<name>` (when a preset composed the agent). **Any selector matching any candidate** satisfies the dimension; empty/absent = unrestricted. Unknown identity (no candidates) never matches, so agent-scoped rules fail closed. |
27
28
  | `params` | `Record<string, string \| number \| boolean \| (…)[]>` | Argument key → pattern (or pattern list). **Every listed key must be present AND match** (AND); absent = unrestricted. Scalars match as strings (numbers/booleans stringified); array elements match any-of; nested objects contribute their scalar leaves (depth-capped at 8). A `!`-prefixed pattern negates: the value must NOT match it; a key with only negations matches when the key is present and no negation hits (quote `!` patterns in YAML: `"!git*"`). `/` is an ordinary character in params patterns — `*` crosses it. |
28
29
  | `paths` | `string[]` | Workspace-relative path patterns; any candidate matching any pattern satisfies the dimension. Candidates come from argument values under these keys at ANY nesting depth (depth-capped): `path` `paths` `file` `files` `file_path` `dir` `directory` `directories` `cwd` `workspace` `root` `target` `targets` `output`. Absolute candidates outside the workspace are dropped; relative `../` forms are kept so explicit out-of-root globs still work. In path patterns `*` does not cross `/`; `**` crosses any depth (including zero). With `caseInsensitivePaths` on (Windows default) the root comparison and the patterns ignore ASCII case. |
29
30
  | `absent` | `string[]` | Argument keys that must be ABSENT; **every listed key must be missing** (non-object arguments satisfy this trivially). |
@@ -103,3 +104,5 @@ This plugin only produces `ask` decisions; the `ask` goes to the official `ctx.a
103
104
  - Invalid YAML, unknown fields/actions, bad globs/regexes, backtracking-prone patterns, and counts over `maxRules` are load-time errors: `badFilePolicy: fail` (default) makes the first tool call in that workspace fail loudly; `ignore-with-warning` warns and degrades to an empty rule set.
104
105
  - An absolute `rulesFile` or a configured `fallbackPath` is validated at plugin mount — missing or invalid fails the mount.
105
106
  - File changes reload through Chokidar with a debounce (`watch`, `watchStabilityThresholdMs`); a failed reload keeps the previous rules and only warns — never crashes. `/rules reload` triggers the same path manually; `/rules decisions` and `/rules test` inspect the trail and dry-run the rules.
107
+ - `/rules test` accepts leading flags: `--cwd <dir>` evaluates against another workspace (rule discovery AND path normalization), `--env KEY=VALUE` (repeatable) overrides host env for `when.env`, and `--agent <selector>` (repeatable) supplies identity candidates for the `agents` dimension.
108
+ - `enforce: false` puts the plugin in dry-run mode: deny/ask hits are audit-logged with a `dryRun` marker (keeping the would-be action and the real downstream outcome) and every call passes through — use it to trial a new policy in production before enforcing it. `/rules` prints a dry-run notice while the mode is active.
@@ -17,13 +17,14 @@ rules: # 规则列表,按书写顺序求值;可以省略或为空
17
17
  reason: ...
18
18
  ```
19
19
 
20
- 顶层只允许 `rules` 一个键;每条规则只允许 `match`、`action`、`reason`、`enabled`、`description`、`tags` 六个键;`match` 只允许 `tools`、`params`、`paths`、`absent`、`when` 五个键。出现任何未知键都在加载期报错(响亮失败),绝不静默忽略。
20
+ 顶层只允许 `rules` 一个键;每条规则只允许 `match`、`action`、`reason`、`enabled`、`description`、`tags` 六个键;`match` 只允许 `tools`、`agents`、`params`、`paths`、`absent`、`when` 六个键。出现任何未知键都在加载期报错(响亮失败),绝不静默忽略。
21
21
 
22
22
  ## match 维度(全部 AND)
23
23
 
24
24
  | 维度 | 类型 | 语义 |
25
25
  |---|---|---|
26
26
  | `tools` | `string[]` | 工具名 glob(始终 glob 解释,与 `patternMode` 无关)。空/缺省 = 不限制。支持 `mcp__*` 等前缀匹配。 |
27
+ | `agents` | `string[]` | 调用方代理身份选择器 glob,匹配会话头推导出的候选:`main`(顶层会话)、`subagent`(子代理会话)、`preset:<name>`(由预设组合的代理)。**任一选择器命中任一候选**即满足该维度;空/缺省 = 不限制。身份未知(无候选)永不命中——面向代理的规则按失败关闭(fail closed)处理。 |
27
28
  | `params` | `Record<string, string \| number \| boolean \| (…)[]>` | 参数键 → 模式(或模式列表)。**列出的每个键都必须存在且命中**(AND);缺省 = 不限制。标量值按字符串匹配(数字/布尔字符串化);数组任一元素命中即可;嵌套对象贡献其标量叶子(深度上限 8)。`!` 前缀为否定:值必须**不**命中该模式;只含否定的键在键存在且全部否定未命中时命中(YAML 里请给 `!` 模式加引号:`"!git*"`)。params 模式中 `/` 是普通字符,`*` 可跨 `/`。 |
28
29
  | `paths` | `string[]` | 工作区相对路径模式,任一候选命中任一模式即可。候选来自参数中下列键的值(**任意嵌套深度**、深度上限):`path` `paths` `file` `files` `file_path` `dir` `directory` `directories` `cwd` `workspace` `root` `target` `targets` `output`。工作区外的绝对候选被丢弃;相对 `../` 形式保留,可显式匹配工作区外。路径模式中 `*` 不跨 `/`,`**` 跨任意深度(含零层)。`caseInsensitivePaths` 开启时(Windows 默认)根比较与模式匹配忽略 ASCII 大小写。 |
29
30
  | `absent` | `string[]` | 必须**缺席**的参数键;**每个列出的键都必须缺失**(非对象参数天然满足)。 |
@@ -101,3 +102,5 @@ rules:
101
102
  - 非法 YAML / 未知字段或 action / 坏 glob 与正则 / 易回溯灾难的模式 / 超 `maxRules` 都是加载期错误:`badFilePolicy: fail`(默认)时首个使用该工作区的工具调用响亮失败;`ignore-with-warning` 时告警并降级为空规则集。
102
103
  - 绝对 `rulesFile` 或配置的 `fallbackPath` 在插件挂载时即校验,缺失/非法直接挂载失败。
103
104
  - 文件变更后经 Chokidar 去抖重读(`watch`、`watchStabilityThresholdMs`);重读失败保留旧规则、只告警,绝不崩溃。`/rules reload` 手动触发同样路径;`/rules decisions` 回放审计轨迹,`/rules test` 对假设调用干跑规则。
105
+ - `/rules test` 支持前置标志:`--cwd <目录>` 换一个工作区求值(规则发现与路径归一化都切换);`--env 键=值`(可重复)覆盖宿主环境变量以测试 `when.env`;`--agent <选择器>`(可重复)为 `agents` 维度提供身份候选。
106
+ - `enforce: false` 让插件进入干跑模式:deny/ask 命中只写审计(带 `dryRun` 标记,记录「本会做什么」与下游真实结果),所有调用照常透传——用于在生产环境先试跑新策略再强制执行。激活期间 `/rules` 会打印干跑提示。
@@ -54,6 +54,11 @@
54
54
  "type": "array",
55
55
  "items": { "type": "string", "minLength": 1 }
56
56
  },
57
+ "agents": {
58
+ "description": "Agent-identity selector globs (main, subagent, preset:<name>); any selector matching any candidate satisfies the dimension. Unknown identity never matches.",
59
+ "type": "array",
60
+ "items": { "type": "string", "minLength": 1 }
61
+ },
57
62
  "params": {
58
63
  "description": "Param key to pattern(s). EVERY key must be present and match; a !-prefixed pattern negates. Scalars are stringified; arrays and nested objects contribute their scalar leaves.",
59
64
  "type": "object",