dsh-permission-rules 0.7.21 → 0.7.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README-es.md DELETED
@@ -1,443 +0,0 @@
1
- <div align="center">
2
-
3
- # 🛡️ dsh-permission-rules
4
- - **Canal 1024 store**: `npm i -g dsh1024` una vez, luego `dsh1024 plugin --profile web add dsh-permission-rules` (cuenta para el ranking de instalaciones de [deepseek1024.com](https://deepseek1024.com)).
5
-
6
- **Reglas de permisos declarativas estilo Claude Code para DeepSeek Harness.**
7
-
8
- *Las reglas deciden lo conocido. Un modelo revisor decide lo que no lo es.*
9
-
10
- [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
11
- [![Gitee](https://img.shields.io/badge/Gitee-mirror-c71d23?logo=gitee)](https://gitee.com/perrylink/dsh-permission-rules)
12
- [![DSH plugin](https://img.shields.io/badge/dsh--plugin-✅-green)](https://github.com/topics/dsh-plugin)
13
- [![dsh-doctor](https://raw.githubusercontent.com/PerryLink/dsh-plugin-doctor/main/badges/PerryLink__dsh-permission-rules.svg)](https://github.com/PerryLink/dsh-plugin-doctor#verified-徽章)
14
- [![DSH Market](https://raw.githubusercontent.com/2BingLing/dsh-market/master/assets/readme/badge-top-rated.svg)](https://dsh.market/)
15
- [![Node](https://img.shields.io/badge/node-%5E22.19%20%7C%7C%20%3E%3D24-brightgreen.svg)](#)
16
- [![CI](https://img.shields.io/github/actions/workflow/status/PerryLink/dsh-permission-rules/ci.yml?branch=main&label=CI)](https://github.com/PerryLink/dsh-permission-rules/actions)
17
- [![Version](https://img.shields.io/github/v/tag/PerryLink/dsh-permission-rules?label=version)](https://github.com/PerryLink/dsh-permission-rules/releases)
18
- [![npm version](https://img.shields.io/npm/v/dsh-permission-rules)](https://www.npmjs.com/package/dsh-permission-rules)
19
- [![npm downloads](https://img.shields.io/npm/dm/dsh-permission-rules)](https://www.npmjs.com/package/dsh-permission-rules)
20
- [![dshfind](https://dshfind.com/api/badge/PerryLink/dsh-permission-rules?metric=downloads&lang=es)](https://dshfind.com/es/plugins/PerryLink/dsh-permission-rules?ref=badge)
21
-
22
- [English](README.md) · [简体中文](README-zh.md) · [Español](README-es.md) · [Português](README-pt.md) · [हिन्दी](README-hi.md)
23
-
24
- ```sh
25
- dsh plugin --profile <name> add dsh-permission-rules
26
- ```
27
-
28
-
29
- </div>
30
-
31
- ---
32
-
33
- **📖 Base de conocimiento del ecosistema** — datos medidos, no marketing: [guía de desarrollo · datos de selección · criterios de mantenimiento](https://perrylink.github.io/dsh-plugin-guide/).
34
-
35
- <!-- star-cta -->
36
- ## Install & uninstall
37
-
38
- - **canal git** (último `main`): `dsh plugin --profile web add github:PerryLink/dsh-permission-rules` — el script `prepare` compila solo con dependencias de producción.
39
- - **canal npm** (versiones publicadas): `dsh plugin --profile web add dsh-permission-rules`.
40
- - **canal tarball**: `pnpm pack` en este repo, luego `dsh plugin --profile web add ./dsh-permission-rules-<version>.tgz`.
41
- - **desinstalar**: `dsh plugin --profile web remove dsh-permission-rules`.
42
-
43
- ## ⭐ 如果它帮到了你
44
-
45
- Este plugin forma parte de la [familia de plugins DSH](https://github.com/PerryLink) (más de 40, todos Apache-2.0). Si te resulta útil, **dale una estrella**: no desbloquea nada, pero ayuda a que la siguiente persona lo encuentre antes.
46
-
47
- *English:* part of a 95 plugin family for DeepSeek Harness. If it is useful, **a star helps the next person find it** — nothing is gated behind it.
48
-
49
- ## What is dsh-permission-rules?
50
-
51
- Reglas de permisos declarativas estilo Claude Code para DeepSeek Harness.
52
-
53
- Las reglas deciden lo conocido. Un modelo revisor decide lo que no lo es.
54
-
55
- ![Diagrama de flujo de dsh-permission-rules: solicitud → política/revisión → veredicto.](docs/assets/dsh-permission-rules-flow.svg)
56
-
57
- *Resumen del flujo: solicitud → política/revisión → veredicto. Los ejemplos siguientes usan la superficie de comandos real.*
58
-
59
-
60
- *La misma ejecución, animada.*
61
-
62
- ## Compatibility
63
-
64
- | Surface | Status |
65
- |---|---|
66
- | Harness | DeepSeek Harness `dsh-v0.2.1-alpha.1` (adaptado el 2026-09-22, cadena completa de gates): la configuración del plugin es el contrato de settings en vivo — cada campo se declara `.volatile()`, el Loader entrega a `apply` una referencia viva por campo, y una edición guardada se confirma en las referencias en ejecución SIN remontar el plugin, con los valores fuera de rango rechazados por el esquema antes de escribir nada. Un host cuyo Schemastery sea anterior a `volatile()` (antes de 3.18.3) sigue montando y ejecutando este plugin — los campos simplemente quedan como valores ordinarios y el formulario de configuración en vivo no está disponible — porque el marcador se aplica mediante una sonda de capacidad, no se da por supuesto. `Session.append` en esta línea todavía no puede estampar el marcador `ignorable` (su tercer argumento es el paquete surface-intent, y el campo del sobre solo sobrevive para la lectura de logs almacenados), así que toda la línea `0.2-alpha` se preverifica como no marcada y la auditoría del registro de sesión queda desactivada por defecto — `append` solo lee `sourceEventSeqs`/`surfaceOp` y congela el sobre, de modo que no tiene ninguna ruta de código capaz de estampar el marcador; las líneas anteriores `0.1.7-alpha` (adaptada el 2026-09-22), `0.1.5-rc.2` (adaptada el 2026-09-09) y `0.1.3-alpha` mantienen la misma firma de append surface-only, y cada versión preliminar `0.2` se clasifica con ellas (una versión ESTABLE `0.2.x` queda a cargo de la sonda de append, así que una superficie de marcado restaurada se adopta sin una nueva publicación de este plugin). Las migraciones de log de esas líneas rechazan eventos de plugin sin clasificar incluso marcados: aplica `strip` a las filas de auditoría v1 antes de que un host `0.1.3-alpha` abra el log y a las v2 antes de que un host `0.1.5-alpha` lo migre (los logs v3 nativos solo necesitan `repair`). |
67
- | Node | `^22.19.0 || >=24.0.0` |
68
- | Platforms | Todas (host + cliente web de settings) |
69
- | Model | Cualquiera (las razones deny/ask se muestran a través de los resultados de herramienta) |
70
-
71
- ## What you get
72
-
73
- `dsh-permission-rules` antepone una lista ordenada de reglas **`allow` / `deny` / `ask`** a cada llamada de herramienta en la cascada `tools/pre-execute` — determinista, instantánea, auditable y escrita por ti en YAML plano:
74
-
75
- - **`deny`** bloquea la llamada; la `reason` de la regla se convierte en el error visible para el modelo.
76
- - **`ask`** usa la costura oficial de aprobación (monta `dsh-auto-review` para un answerer de segundo modelo, o responde un humano; sin ninguno, el harness falla cerrado).
77
- - **`allow`** (y sin coincidencia) delega estrictamente vía `next()` — los listeners posteriores nunca se cortocircuitan.
78
-
79
- Cada acierto **y** cada paso directo se registra como un evento de sesión `permissionRules/decision` (solo registro — nada extra se inyecta en el contexto del modelo).
80
-
81
- - **Emparejamiento rico** — globs de nombre de herramienta (incl. `mcp__*`), selectores de identidad de agente (`main` / `subagent` / `preset:*`), globs **o** regexes de clave/valor de argumentos (con negación `!pattern` y una dimensión de clave `absent`), globs de ruta relativos al workspace a **cualquier profundidad de anidamiento**, condiciones de host `when` (variables de entorno, plataforma), y **descomposición de comandos de shell** (`argv`: palabra de comando, tokens de argumento, firma de pipeline) para emparejamiento preciso a nivel de token.
82
- - **Línea base de alto riesgo integrada** — un conjunto deny/ask embarcado (comandos destructivos, escalada de privilegios, descarga-y-ejecución, rutas sensibles) habilitado por defecto y añadido después de las reglas de usuario (una regla de usuario más cercana puede sobrescribirlo); se alterna con `builtin.enabled`.
83
- - **Archivos de reglas jerárquicos** — `searchUp` opcional fusiona cada `.dsh/rules.yaml` desde el cwd de la sesión hasta la raíz del sistema de archivos, el más cercano primero.
84
- - **Despliegue en dry-run** — `enforce: false` audita lo que la política *haría* mientras deja pasar cada llamada.
85
- - **Recarga en caliente** — vigilancia Chokidar con debounce; una edición rota conserva las reglas anteriores, nunca falla. En un host WSL, o para un archivo de reglas bajo `/mnt/<unidad>`, la vigilancia pasa a sondeo (polling) porque allí los eventos nativos de cambio no son fiables.
86
- - **Fallo ruidoso** — YAML inválido, acciones/campos desconocidos, globs/regexes malos, patrones propensos a backtracking o más de `maxRules` reglas fallan la carga.
87
-
88
- ## Rule syntax
89
-
90
- ```yaml
91
- # <project>/.dsh/rules.yaml
92
- rules:
93
- - match: { tools: [bash, pwsh], params: { command: "git push*" }, paths: ["**/secrets/**"] }
94
- action: deny
95
- reason: "No pushes from protected paths"
96
-
97
- - match: { tools: [edit, write] }
98
- action: ask
99
- reason: "File writes need confirmation"
100
- ```
101
-
102
- - **Dimensiones de coincidencia** — `tools` (globs, incl. `mcp__*`), `agents` (`main` / `subagent` / `preset:<name>`; identidad desconocida nunca coincide — falla cerrado), `params` (globs o regexes de clave/valor, negación `!pattern`, dimensión de clave `absent`), `paths` (globs relativos al workspace extraídos a cualquier profundidad), `when` (globs/regexes de variables `env` + una lista `platform` cerrada), y `network` (`domains` / `ips` / `ports` / `schemes` — globs, comodines, CIDR, rangos de puertos).
103
- - **Acciones** — `allow` / `deny` / `ask`, evaluadas en orden de archivo, la primera coincidencia gana.
104
- - **Metadatos de regla** — `enabled: false` (visible pero inerte), `description`, `tags`; los campos desconocidos fallan la carga.
105
- - **Schema** — un JSON Schema se distribuye en [docs/rules-format.schema.json](docs/rules-format.schema.json) (autocompletado de editor vía `# yaml-language-server: $schema=...`); el vocabulario completo y una línea base de 5 reglas viven en [docs/rules-format.en.md](docs/rules-format.en.md).
106
-
107
- ## Network policy
108
-
109
- Una **política de red a nivel de proceso** estilo Codex: el tráfico de subprocesos de shell fluye a través de un **proxy HTTP/CONNECT** local integrado, y cada conexión se decide mediante reglas de red ordenadas o mediante tres modos mapeados sobre los presets oficiales del sandbox:
110
-
111
- - **`deny-all`** — el preset de sandbox de solo lectura: bloquear todo el tráfico saliente.
112
- - **`whitelist`** — el preset workspace-write: permitir los destinos listados, `unlisted: ask` (o `deny`) para el resto.
113
- - **`allow-all`** — el preset danger-full-access: permitir todo.
114
- - **`auto`** (por defecto) — sigue el preset del sandbox; en hosts sin el servicio de política de sandbox se resuelve a `autoFallback` (`allow-all`).
115
-
116
- - **Emparejamiento** — `match.network` con `domains` / `ips` / `ports` / `schemes` (globs, comodines, CIDR, rangos de puertos; se aceptan puertos YAML numéricos). La extracción de candidatos URL en la ruta caliente `tools/pre-execute` se dispara sobre argumentos de herramientas web y URLs embebidas en texto de comandos bash/pwsh; los destinos de loopback pueden cortocircuitar reglas según la política `loopback`. Los literales IPv6 mapeados a IPv4 se normalizan a su forma IPv4 antes de comparar, y el proxy se conecta a las direcciones con las que se tomó la decisión, sin una segunda resolución DNS; una conexión cuya adjudicación no resolvió ninguna dirección falla cerrada (502) en lugar de marcar el nombre.
117
- - **Encadenado a un proxy ascendente** — `network.upstreamProxy` (por defecto `off`) envía las conexiones que este proxy **permite** a través de un proxy ascendente: CONNECT le pide un túnel (`CONNECT host:port`) y las peticiones HTTP simples se le reenvían en forma absoluta. Un destino bloqueado nunca llega al proxy ascendente: sigue recibiendo el 403 estructurado de este plugin. Dos casos no se encadenan nunca aunque haya un proxy ascendente configurado: un destino de **loopback** (un proxy fuera de este host no puede enrutar su loopback) y cualquier decisión producida por una **regla con ámbito `ips`** (encadenar entrega el nombre de host al proxy ascendente, así que «la conexión aterriza en una dirección que las reglas vieron» — issue #21 — dejaría de cumplirse justo donde a las reglas les importaba la dirección; esas decisiones siguen conectando directamente a la dirección adjudicada), más un destino cuyo esquema no tenga un proxy ascendente utilizable. El nombre de host del propio proxy ascendente es configuración del operador, no entrada del agente, y no lo adjudican estas reglas. Una URL con credenciales nunca se emite en claro: los avisos, `/rules network` y la instantánea de settings enmascaran la contraseña (`http://user:***@host:port`). Un proxy ascendente inalcanzable, un timeout (10 s) o una respuesta no 2xx producen **502**; no hay a propósito una caída silenciosa a conexión directa, para que una mala configuración siga siendo visible.
118
- - **Auditoría** — las conexiones denegadas anexan `permissionRules/network` a la sesión propietaria (la misma puerta adaptativa `ignorable`), con contadores de bloqueo e intercepciones recientes en `/rules network` y la página de settings.
119
- - **Diagnóstico** — las conexiones bloqueadas llevan un mensaje `[network: …]` que nombra el destino bloqueado, el modo o la regla que decidió y la solución.
120
- - **Permitir desde la página de settings** — cada intercepción reciente ofrece una acción **Permitir**. Escribe UNA regla mínima `match: { network: { domains: [<host>] } }` / `action: allow` en la **parte superior** (índice 0) del archivo de reglas más cercano que realmente adjudica esa conexión: el archivo de proyecto del workspace al que se atribuyó el bloqueo (el `rulesFile` absoluto cuando hay uno configurado) o, para un bloqueo a nivel de host sin sesión, el archivo que resuelve la cadena del host (`rulesFile` absoluto → `<processCwd>/<rulesFile>` existente → `fallbackPath` configurado → `<processCwd>/<rulesFile>`, creado). Con varios workspaces cargados, la página ofrece primero un selector, porque cualquier cadena de workspace cargada tiene prioridad sobre la cadena del host. En el índice 0 porque las reglas son de primera coincidencia: añadida después de un `deny` existente, la regla sería texto muerto. Solo `domains`, porque esa dimensión incluye subdominios y es independiente de puerto/esquema, así que el host no vuelve a bloquearse en su siguiente puerto; los comentarios y las reglas no tocadas se conservan, y el texto pasa por la misma puerta de validación que una edición manual. El cambio surte efecto de inmediato —se releen las cadenas de workspace en caché Y la cadena de host sin sesión, sin reinicio y sin `/rules reload`— y la decisión se recalcula, así que el aviso informa del resultado REAL y nunca afirma éxito mientras la conexión sigue bloqueada. Rechaza, sin tocar el archivo, un workspace desconocido, un destino fuera de las fuentes de reglas conocidas, la línea base integrada de solo lectura y un archivo que no puede leer o analizar; una conexión ya permitida no escribe nada. `network.allowHostAction: false` oculta el botón y hace que el RPC rechace.
121
-
122
- ## Quick start
123
-
124
- ```sh
125
- dsh plugin --profile web add github:PerryLink/dsh-permission-rules
126
- ```
127
-
128
- ```sh
129
- # 1. install the bundle into your profile
130
- dsh plugin --profile web add github:PerryLink/dsh-permission-rules
131
-
132
- # or from npm (published releases)
133
- dsh plugin --profile web add dsh-permission-rules
134
-
135
- # 2. restart and verify the row
136
- dsh --profile web --dump-config | grep -A4 'id: permission-rules'
137
- ```
138
-
139
- ### Ejemplos
140
-
141
- Son los puntos de entrada reales; instala y llama a uno.
142
-
143
- #### Ejemplo detallado
144
-
145
- **Comando**
146
-
147
- ~~~sh
148
- /rules test bash {"command":"git push origin main","cwd":"src/secrets/app"}
149
- ~~~
150
-
151
- **Salida**
152
-
153
- ~~~text
154
- "bash" matches rule 1 (deny): 禁止 push 到受保护路径
155
- ~~~
156
-
157
- *Observado en `test/command.spec.ts:204-219 (assertions at :209-213); src/prose.ts:121`*
158
-
159
- #### Rama de fallo
160
-
161
- **Entrada**
162
-
163
- ~~~text
164
- /rules test bash {not json
165
- ~~~
166
-
167
- **Salida**
168
-
169
- ~~~text
170
- Invalid JSON arguments "{not json": the arguments must parse as JSON.
171
- ~~~
172
-
173
- *Observado en `plugins/dsh-permission-rules/src/prose.ts:123; plugins/dsh-permission-rules/src/runtime.ts:1266-1272; plugins/dsh-permission-rules/test/command.spec.ts:221-228`*
174
-
175
- #### Ejemplos breves
176
-
177
- - `tools/pre-execute`
178
-
179
- ## Configuration
180
-
181
- Todos los parámetros son campos Schemastery `Config` (modificables desde cordis.yml). Una sobrescritura dirigida por id reemplaza toda la fila — reafirma cada clave que necesites. En un host `0.1.7-alpha` cada campo se declara `.volatile()`, así que TAMBIÉN se edita in situ desde la página de Plugins: la edición se confirma en las referencias en ejecución sin remontar el plugin, el runtime lee el valor nuevo en su siguiente uso, y un cambio en `network.proxyPort`, `proxyBind`, `injectEnv` o `noProxy` revincula el proxy local de inmediato. Los límites numéricos viven en el esquema, y el editor de configuración resuelve la configuración fusionada contra ese esquema antes de persistir, así que un valor fuera de rango se rechaza en el momento de escribir. En un host cuyo Schemastery sea anterior a `volatile()` estos campos solo se cambian por cordis.yml.
182
-
183
- | Key | Default | Meaning |
184
- |---|---|---|
185
- | `rulesFile` | `.dsh/rules.yaml` | Ubicación del archivo de reglas; relativo = resuelto contra el cwd de la sesión, absoluto = global y validado al montar |
186
- | `fallbackPath` | *(none)* | Archivo de reglas usado cuando la detección por cwd no encuentra nada; validado al montar |
187
- | `badFilePolicy` | `fail` | Archivo de reglas malo: `fail` hace fallar la llamada pendiente ruidosamente; `ignore-with-warning` advierte y continúa vacío |
188
- | `maxRules` | `256` | Límite duro de recuento de reglas en la cadena fuente efectiva |
189
- | `maxCachedWorkspaces` | `512` | Límite duro de cargas de reglas por workspace en caché (evicción LRU) |
190
- | `patternMode` | `glob` | Sabor de patrón `params`/`paths`/`when.env`: `glob` o `regex` (los nombres de herramienta siempre son globs) |
191
- | `watch` | `true` | Vigilancia Chokidar + recarga al cambiar |
192
- | `watchStabilityThresholdMs` | `200` | Ventana de debounce de recarga (ms) |
193
- | `language` | `en` | Idioma de salida de `/rules`: `en`, `zh`, `es`, `pt`, `hi` |
194
- | `caseInsensitivePaths` | *(win32)* | Los patrones `paths` y la comparación de raíz del workspace ignoran mayúsculas ASCII; `true` en Windows |
195
- | `audit` | `all` | Granularidad de auditoría: `all` registra cada acierto Y paso directo; `hits` omite eventos de paso |
196
- | `searchUp` | `false` | Recorrer directorios padre desde el cwd y fusionar cada archivo de reglas encontrado, el más cercano primero |
197
- | `maxGlobStars` | `2` | Límite duro de cuantificadores `*`/`**` no acotados por patrón glob |
198
- | `enforce` | `true` | `false` = modo dry-run: los aciertos deny/ask se registran con marcador `dryRun` y cada llamada pasa |
199
- | `allowUnmarkedAudit` | `false` | Los hosts previos al marcador descartan el marcador `ignorable`; el plugin desactiva la auditoría de registro con una advertencia. Pon `true` para reactivar |
200
- | `network.enabled` | `true` | Interruptor maestro del proxy, la inyección de entorno y los modos por defecto de herramienta web |
201
- | `network.mode` | `auto` | Modo de política: `auto` sigue el preset del sandbox, o `deny-all` / `whitelist` / `allow-all` |
202
- | `network.autoFallback` | `allow-all` | Modo usado cuando `auto` no tiene servicio de política de sandbox |
203
- | `network.unlisted` | `ask` | Manejo en modo whitelist de destinos sin regla coincidente: `ask` o `deny` |
204
- | `network.proxyBind` | `127.0.0.1` | Dirección de enlace del proxy local (solo loopback) |
205
- | `network.proxyPort` | `0` | Puerto del proxy local; `0` elige un puerto efímero libre |
206
- | `network.proxyMaxRecent` | `100` | Límite de registros de bloqueo recientes para la página de settings |
207
- | `network.loopback` | `allow` | Destinos de loopback: `allow` (paridad Codex) o `policy` |
208
- | `network.injectEnv` | `true` | Si se inyectan variables de entorno del proxy para subprocesos |
209
- | `network.noProxy` | `clear` | Manejo de NO_PROXY en subprocesos: `clear` aplica la política o `preserve` |
210
- | `network.upstreamProxy` | `off` | Proxy ascendente para las conexiones que este plugin PERMITE: `off` conecta directamente, `inherit` reutiliza los nombres de proxy del entorno de lanzamiento, o una URL de proxy `http(s)://` explícita |
211
- | `network.allowHostAction` | `true` | Acción «permitir este host» por bloque de la página de settings: `false` oculta el botón Permitir y hace que el RPC `permissionRules/allowHost` rechace (el editor de reglas no se ve afectado) |
212
- | `builtin.enabled` | `true` | Línea base de alto riesgo integrada: `false` deshabilita por completo el conjunto deny/ask embarcado |
213
- | `builtin.path` | *(embarcado)* | Archivo de línea base de reemplazo (absoluto, o relativo a `process.cwd()`); validado al montar |
214
-
215
- ## Tools & surfaces
216
-
217
- | Surface | Kind | Notes |
218
- |---|---|---|
219
- | `tools/pre-execute` | listener | Reglas allow/deny/ask de primera coincidencia + extracción de candidatos URL de red |
220
- | `/rules` | command | `list` · `reload` · `decisions [n]` · `test <tool> <json>` |
221
- | `permissionRules/decision` | event | Auditoría solo de registro para cada acierto y paso directo |
222
- | `permissionRules/network` | event | Auditoría de capa de proxy para conexiones bloqueadas |
223
- | HTTP/CONNECT proxy | service | Proxy local integrado que gobierna el tráfico de subprocesos de shell |
224
- | settings page | client | Editor de modo de red, editor de reglas, contadores de bloqueo, intercepciones recientes con una acción de permiso por bloque |
225
-
226
- ```
227
- /rules list the active rules, their source files, and any last-reload error
228
- /rules list explicit alias for the bare listing
229
- /rules reload re-read the rule-file chain for this workspace
230
- /rules decisions [n] show the last n permission decisions of this session (default 10)
231
- /rules test <tool> <json> dry-evaluate the rules against a hypothetical call
232
- ```
233
-
234
- `/rules test` también acepta banderas iniciales: `--cwd <dir>`, `--env KEY=VALUE` (repetible), `--agent <selector>` (repetible) y `--platform <name>`. En cadenas multi-archivo (p. ej. `searchUp`), cada línea de regla listada se atribuye a su propio archivo fuente.
235
-
236
- ## Permissions & data
237
-
238
- - **Permissions**: el manifiesto de workshop declara `files:read`, `files:watch`, `files:write`, `session:append` y `network:outbound`. Las decisiones `ask` usan la costura oficial de aprobación — nada se reimplementa ni se evade.
239
- - **Data**: los archivos de reglas se leen del disco; no se escribe ningún dato de reglas. Sin llamadas al modelo, sin subagentes revisores.
240
- - **Session log**: `permissionRules/decision` nunca se inyecta en el contexto del modelo y se anexa con el marcador `ignorable: true` del sobre, de modo que cualquier build del harness carga el registro.
241
-
242
- ## Security boundaries
243
-
244
- - **Política, no kernel.** Los candidatos `paths` provienen solo de un conjunto documentado de claves de argumento (a cualquier profundidad, con tope), y solo coinciden las rutas relativas al workspace.
245
- - **Aquí no hay revisor.** El plugin nunca genera subagentes ni llama modelos — producir una decisión `ask` es el fin de su trabajo.
246
- - **Sin cambios de sandbox.** La política de sandbox a nivel de SO pertenece a la costura del sandbox, no a este plugin.
247
- - **Rechazo ruidoso de mala configuración.** Campos YAML desconocidos, acciones desconocidas y patrones malos se rechazan al cargar.
248
- - **Límites de backtracking.** Los patrones glob se limitan a `maxGlobStars` expansiones de estrella no acotadas; los patrones regex rechazan cuantificadores anidados no acotados y alternancias literales solapadas cuantificadas.
249
- - **Marcado a prueba de fallos.** Los reenvíos HTTP simples y los túneles CONNECT conectan solo a direcciones adjudicadas; un allow cuya adjudicación no resolvió ninguna dirección falla cerrado con 502 en lugar de marcar el nombre (issue #23).
250
-
251
- ## Known limitations
252
-
253
- - **Marcador de auditoría en hosts previos al marcador o que rechazan eventos.** `permissionRules/decision` se anexa con `ignorable: true`; los hosts cuyo `Session.append` es anterior al marcador (las líneas `0.1.0-rc.1`–`rc.7` y `0.1.1-rc.1`–`rc.7`) lo descartan silenciosamente, la línea `0.1.2-rc` trae la superficie alpha.5 (ninguna opción de append escribe el marcador), la línea `0.1.2-alpha` rechaza eventos de plugin al leerlos incluso marcados, y las líneas `0.1.3-alpha` y `0.1.5-alpha` mantienen la misma firma de append surface-only (verificado en los paquetes publicados `0.1.3-alpha.1`/`0.1.5-alpha.1`) — el runtime preverifica todas ellas antes del primer append y desactiva la auditoría de registro con una advertencia única. La migración entre generaciones también rechaza filas de auditoría marcadas: la puerta v1→v2 de `0.1.3-alpha` rechaza eventos v1 desconocidos, y la puerta v2→v3 de `0.1.5-alpha` rechaza todo evento sin clasificar (su inventario está congelado al vocabulario v2 publicado), así que aplica `strip` a las filas v1 antes de que un host 0.1.3 abra el log y a las v2 antes de actualizar a un host 0.1.5. Los logs v3 nativos aceptan filas de plugin marcadas, así que solo necesitan `repair`. Pon `allowUnmarkedAudit: true` para reactivar; repara registros ya escritos con `scripts/repair-session-logs.mjs` (su modo `strip` elimina filas de auditoría donde el marcador no ayuda).
254
- - **Los candidatos de ruta son heurísticos.** Solo las claves de argumento documentadas alimentan el emparejamiento de rutas, y el emparejamiento relativo al workspace es insensible a mayúsculas ASCII solo con `caseInsensitivePaths` activado.
255
- - **Los globs son un subconjunto conservador.** Sin expansión de llaves — escribe dos patrones, o usa modo regex.
256
- - **La guardia de backtracking de regex es estructural, no exhaustiva.** Prefiere el modo glob para archivos no confiables.
257
- - **Las peticiones salientes del propio host no pasan por este proxy.** El entorno de proxy inyectado cubre los **subprocesos de shell derivados** —que es para lo que sirve la inyección— y cualquier consumidor que lea los nombres de proxy en el momento de la petición. **No** cubre el tráfico basado en `fetch` del propio proceso del host: el launcher instala el dispatcher global de undici desde el **entorno de lanzamiento** antes de que se monte el primer plugin, ese dispatcher enruta por su política y no por el entorno, y Node muestrea el entorno de proxy al arrancar, así que un plugin que se monte después no puede redirigirlo. Consecuencias: los endpoints del proveedor **no** necesitan una regla allow, y `network.injectEnv: false` no saca al proceso del host de nada. Medido el 2026-09-10 en Node 22; reproducible con `scripts/host-egress-probe.mjs`.
258
- - **El encadenado solo cubre lo que llega a este proxy.** En `dsh-v0.1.7-alpha.1` y posteriores, la propia política de proxy de lanzamiento del harness y su capa de entorno para subprocesos pueden tener precedencia sobre los nombres de proxy que inyecta este plugin, así que `network.upstreamProxy` describe qué ocurre con las conexiones que este proxy maneja — no es una afirmación sobre todas las rutas salientes del proceso del host.
259
-
260
- ## Collaborating with dsh-auto-review
261
-
262
- - `dsh-permission-rules` produce `ask`; `dsh-auto-review` responde en la cascada `approval/request` con un veredicto de segundo modelo de solo lectura (o delega en humanos). Monta ambos para el bucle completo cerrado.
263
- - Probado en integración: `permissionRules/decision` → `approval/asked` → `autoReview/verdict` → `approval/decided`, con el revisor reemplazado por un mock guionado.
264
- - La política de aprobación `never` y toda garantía de fallo cerrado del harness oficial permanecen intactas.
265
-
266
- ## Session log repair
267
-
268
- Los registros de sesión escritos antes de que existiera el marcador `ignorable` pueden ser rechazados por builds más nuevas del harness (`SessionFormatUnsupportedError`). El `scripts/repair-session-logs.mjs` distribuido reescribe solo las filas de auditoría objetivo para llevar `ignorable: true`, preservando marcos, con copias de seguridad:
269
-
270
- ```sh
271
- node scripts/repair-session-logs.mjs scan [--home DIR] # reporta filas ajenas, no cambia nada
272
- node scripts/repair-session-logs.mjs repair [--home DIR] [--dry-run]
273
- node scripts/repair-session-logs.mjs strip [--home DIR] [--dry-run]
274
- ```
275
-
276
- `--home` por defecto es `$DSH_HOME/sessions` (o `~/.dsh/sessions`). Descubre cada log por su nombre canónico de generación —`session.jsonl`, `session.v2.jsonl`, `session.v3.jsonl`, cada uno opcionalmente comprimido en `.zstd`—, así que elige el modo según la generación:
277
-
278
- - **v3 (`session.v3.jsonl`, escrito nativamente por la línea `0.1.5-alpha`)** — la ruta de lectura acepta filas de plugin marcadas, así que basta `repair`.
279
- - **v2 (`session.v2.jsonl`, escrito por la línea `0.1.3-alpha`)** — `repair` lo abre en el host que lo escribió, pero la migración v2→v3 de `0.1.5-alpha` rechaza todo evento sin clasificar incluso marcado: ejecuta `strip` sobre los logs v2 **antes** de actualizar a un host `0.1.5-alpha`.
280
- - **v1 (`session.jsonl`)** — la migración v1→v2 de `0.1.3-alpha` rechaza eventos v1 desconocidos incluso marcados: ejecuta `strip` antes de que un host 0.1.3 o posterior abra el log por primera vez.
281
-
282
- ## Development
283
-
284
- ```sh
285
- pnpm install # node ^22.19 || >=24
286
- pnpm run typecheck # tsc, src + tests
287
- pnpm run lint # eslint, src + tests + scripts
288
- pnpm test # vitest: 24 spec files
289
- pnpm run test:coverage # coverage gate (90/80/90/90)
290
- pnpm run build # tsc declarations + tsdown bundles (lib/)
291
- pnpm run pack:check # build + pack (the published artifact)
292
- node scripts/check-readme-sync.mjs # five-language README sync gate (also in CI)
293
- ```
294
-
295
- Consulta [VERIFICATION.md](VERIFICATION.md) para el registro de verificación end-to-end sin cabeza.
296
-
297
- ## Interoperability with other DSH plugins
298
-
299
- Verificado contra **DSH `0.2.0-rc.2`** (el runtime para el que se publica este README) y el conjunto de plugins con más estrellas sondeado el 2026-10-05.
300
-
301
- Este plugin **no interfiere** con otros plugins, incluidos los de más estrellas:
302
-
303
- - **Sin colisión de nombres de herramienta.** Todas las herramientas llevan espacio de nombres; ninguna ocupa un nombre desnudo ya perteneciente a una herramienta incluida u otro plugin.
304
- - **Sin colisión de clave de servicio.** Solo provee `permissionRules`, `permissionRulesRuntime`; esa clave no es una costura incluida ni la provee ningún plugin de más estrellas.
305
- - **Sin colisión de slot.** No registra ninguna clave de slot de cliente, así que no disputa un asiento `shadows-shipped-ui`.
306
- - **Sin colisión de ruta HTTP.** No registra ningún prefijo `webServer`.
307
- - **Sin colisión en la capa de patch.** El patch del bundle solo hace `insert` de su propia fila; nunca sobrescribe el `config` de una fila incluida.
308
- - **Sin mutación global.** No parchea prototipos, ni reescribe `process.env`, ni reemplaza el dispatcher global de fetch.
309
-
310
- **Los listeners de eventos compartidos no interfieren por construcción.** Observa los eventos sensibles al orden `tools/post-execute`, `tools/pre-execute` con `ctx.on()` — el registro de difusión de Cordis, donde cada listener se ejecuta y ninguno puede dejar sin turno a otro. **Todos los listeners aquí delegan por `next()`**, así que la cadena nunca se cortocircuita:
311
- - `tools/post-execute` — also used by `cc-safety-net` (1576★).
312
- - `tools/pre-execute` — also used by `cc-safety-net` (1576★).
313
-
314
- Evidencia estática: `dsh-plugin-doctor` K10–K13 dan `pass` en todas las comprobaciones de este repositorio.
315
-
316
- ## Topics
317
-
318
- `dsh`, `dsh-plugin`, `deepseek-harness`, `permission`, `policy`, `allow-deny-ask`, `approval`, `safety`, `network`, `network-policy`, `proxy`
319
-
320
- ## Contributors
321
-
322
- - [@PerryLink](https://github.com/PerryLink) — creador y mantenedor: vocabulario y evaluación de reglas, runtime, vigilancia HMR, auditoría de registro de sesión, política de red + proxy, y la documentación en cinco idiomas.
323
- - [@22xuan](https://github.com/22xuan) — el informe detallado sobre hosts rc.6 que descartan silenciosamente el marcador `ignorable` del evento de auditoría ([#2](https://github.com/PerryLink/dsh-permission-rules/issues/2)) y la discusión del harness upstream; la detección de capacidad de host en runtime v0.4.1 y la corrección de documentación se derivaron directamente de ese análisis.
324
- - [@sjh9714](https://github.com/sjh9714) — propuso el corpus compartido de vectores de prueba de sintaxis de reglas ([#4](https://github.com/PerryLink/dsh-permission-rules/issues/4), [#5](https://github.com/PerryLink/dsh-permission-rules/issues/5)), incluido en v0.5.1 como `docs/rule-test-vectors/`, y aportó los casos límite de descomposición AST en la [discusión de diseño](https://github.com/PerryLink/dsh-permission-rules/discussions/10).
325
- - [@weipeng1999](https://github.com/weipeng1999) — la propuesta de descomposición de comandos basada en AST ([#8](https://github.com/PerryLink/dsh-permission-rules/issues/8)) detrás de la discusión de diseño.
326
- - [@alexchenzl](https://github.com/alexchenzl) — la solicitud de inclusión en el DSH Directory ([#7](https://github.com/PerryLink/dsh-permission-rules/issues/7)).
327
- - [@zl190](https://github.com/zl190) — informó y verificó la brecha de compatibilidad del harness `0.1.0-rc.7` ([PR #9](https://github.com/PerryLink/dsh-permission-rules/pulls/9)).
328
- - [@cuohua](https://github.com/cuohua) — informó de que la línea `0.1.1-rc` sigue descartando el marcador `ignorable` aunque la compuerta de versión solo cubría `0.1.0` ([#11](https://github.com/PerryLink/dsh-permission-rules/issues/11)); la compuerta ampliada surgió directamente de ese análisis.
329
-
330
- ## PerryLink DSH Plugin Family
331
-
332
- Este proyecto forma parte de la familia de plugins de DeepSeek Harness de [PerryLink](https://github.com/PerryLink). El registro permanece plegado abajo para que cada README conserve una primera pantalla corta; expándelo para ver cada fila y su estado.
333
-
334
- <details>
335
- <summary>Expandir todos los plugins</summary>
336
-
337
- | Plugin | Estado |
338
- |---|---|
339
- | **[dsh-aqua-input-check](https://github.com/PerryLink/dsh-aqua-input-check)** | |
340
- | **[dsh-archive-check](https://github.com/PerryLink/dsh-archive-check)** | |
341
- | **[dsh-auto-review](https://github.com/PerryLink/dsh-auto-review)** | |
342
- | **[dsh-autotier](https://github.com/PerryLink/dsh-autotier)** | |
343
- | **[dsh-bid-ca-precheck](https://github.com/PerryLink/dsh-bid-ca-precheck)** | |
344
- | **[dsh-bid-qual-check](https://github.com/PerryLink/dsh-bid-qual-check)** | |
345
- | **[dsh-cert-mcp](https://github.com/PerryLink/dsh-cert-mcp)** | |
346
- | **[dsh-checkpoint-rewind](https://github.com/PerryLink/dsh-checkpoint-rewind)** | |
347
- | **[dsh-click](https://github.com/PerryLink/dsh-click)** | |
348
- | **[dsh-composer-history](https://github.com/PerryLink/dsh-composer-history)** | |
349
- | **[dsh-contract-stance](https://github.com/PerryLink/dsh-contract-stance)** | |
350
- | **[dsh-customs-doc-check](https://github.com/PerryLink/dsh-customs-doc-check)** | |
351
- | **[dsh-data-quality](https://github.com/PerryLink/dsh-data-quality)** | |
352
- | **[dsh-demurrage-ledger](https://github.com/PerryLink/dsh-demurrage-ledger)** | |
353
- | **[dsh-doublecheck](https://github.com/PerryLink/dsh-doublecheck)** | |
354
- | **[dsh-drill-script-check](https://github.com/PerryLink/dsh-drill-script-check)** | |
355
- | **[dsh-eia-guide-check](https://github.com/PerryLink/dsh-eia-guide-check)** | |
356
- | **[dsh-emergency-plan](https://github.com/PerryLink/dsh-emergency-plan)** | |
357
- | **[dsh-essay-rubric-check](https://github.com/PerryLink/dsh-essay-rubric-check)** | |
358
- | **[dsh-evidence-check](https://github.com/PerryLink/dsh-evidence-check)** | |
359
- | **[dsh-export-ctl-check](https://github.com/PerryLink/dsh-export-ctl-check)** | |
360
- | **[dsh-fast](https://github.com/PerryLink/dsh-fast)** | |
361
- | **[dsh-fmea-table-check](https://github.com/PerryLink/dsh-fmea-table-check)** | |
362
- | **[dsh-forecast-penalty](https://github.com/PerryLink/dsh-forecast-penalty)** | |
363
- | **[dsh-fund-research](https://github.com/PerryLink/dsh-fund-research)** | |
364
- | **[dsh-github](https://github.com/PerryLink/dsh-github)** | |
365
- | **[dsh-gongwen-flow-check](https://github.com/PerryLink/dsh-gongwen-flow-check)** | |
366
- | **[dsh-gongwen-word-check](https://github.com/PerryLink/dsh-gongwen-word-check)** | |
367
- | **[dsh-guard-plan-qc](https://github.com/PerryLink/dsh-guard-plan-qc)** | |
368
- | **[dsh-hazchem-check](https://github.com/PerryLink/dsh-hazchem-check)** | |
369
- | **[dsh-hazplan-check](https://github.com/PerryLink/dsh-hazplan-check)** | |
370
- | **[dsh-hidden-risk-map](https://github.com/PerryLink/dsh-hidden-risk-map)** | |
371
- | **[dsh-hs-classify](https://github.com/PerryLink/dsh-hs-classify)** | |
372
- | **[dsh-icd-rule-check](https://github.com/PerryLink/dsh-icd-rule-check)** | |
373
- | **[dsh-industry-research](https://github.com/PerryLink/dsh-industry-research)** | |
374
- | **[dsh-lawcite-adapter](https://github.com/PerryLink/dsh-lawcite-adapter)** | |
375
- | **[dsh-laya](https://github.com/PerryLink/dsh-laya)** | |
376
- | **[dsh-lc-doc-check](https://github.com/PerryLink/dsh-lc-doc-check)** | |
377
- | **[dsh-learning-gap-check](https://github.com/PerryLink/dsh-learning-gap-check)** | |
378
- | **[dsh-library](https://github.com/PerryLink/dsh-library)** | |
379
- | **[dsh-local-ai](https://github.com/PerryLink/dsh-local-ai)** | |
380
- | **[dsh-lsp-actions](https://github.com/PerryLink/dsh-lsp-actions)** | |
381
- | **[dsh-mask](https://github.com/PerryLink/dsh-mask)** | |
382
- | **[dsh-mcp-panel](https://github.com/PerryLink/dsh-mcp-panel)** | |
383
- | **[dsh-medrec-qc](https://github.com/PerryLink/dsh-medrec-qc)** | |
384
- | **[dsh-memento](https://github.com/PerryLink/dsh-memento)** | |
385
- | **[dsh-nsfc-form-check](https://github.com/PerryLink/dsh-nsfc-form-check)** | |
386
- | **[dsh-nurse-record-check](https://github.com/PerryLink/dsh-nurse-record-check)** | |
387
- | **[dsh-observe](https://github.com/PerryLink/dsh-observe)** | |
388
- | **[dsh-origin-rvc-check](https://github.com/PerryLink/dsh-origin-rvc-check)** | |
389
- | **[dsh-ota-review-check](https://github.com/PerryLink/dsh-ota-review-check)** | |
390
- | **[dsh-output-styles](https://github.com/PerryLink/dsh-output-styles)** | |
391
- | **[dsh-paper-doc-adapter](https://github.com/PerryLink/dsh-paper-doc-adapter)** | |
392
- | **[dsh-permission-rules](https://github.com/PerryLink/dsh-permission-rules)** | |
393
- | **[dsh-permit-report-check](https://github.com/PerryLink/dsh-permit-report-check)** | |
394
- | **[dsh-pipeline-check](https://github.com/PerryLink/dsh-pipeline-check)** | |
395
- | **[dsh-pleading-draft](https://github.com/PerryLink/dsh-pleading-draft)** | |
396
- | **[dsh-plugin-doctor](https://github.com/PerryLink/dsh-plugin-doctor)** | |
397
- | **[dsh-plugin-guide](https://github.com/PerryLink/dsh-plugin-guide)** | |
398
- | **[dsh-plugin-kit](https://github.com/PerryLink/dsh-plugin-kit)** | |
399
- | **[dsh-plugin-upgrade](https://github.com/PerryLink/dsh-plugin-upgrade)** | |
400
- | **[dsh-policy-brief-draft](https://github.com/PerryLink/dsh-policy-brief-draft)** | |
401
- | **[dsh-power-loss-split](https://github.com/PerryLink/dsh-power-loss-split)** | |
402
- | **[dsh-power-ticket-check](https://github.com/PerryLink/dsh-power-ticket-check)** | |
403
- | **[dsh-ppap-check](https://github.com/PerryLink/dsh-ppap-check)** | |
404
- | **[dsh-protest-deadline](https://github.com/PerryLink/dsh-protest-deadline)** | |
405
- | **[dsh-railway-window](https://github.com/PerryLink/dsh-railway-window)** | |
406
- | **[dsh-repair-order-qc](https://github.com/PerryLink/dsh-repair-order-qc)** | |
407
- | **[dsh-research-report](https://github.com/PerryLink/dsh-research-report)** | |
408
- | **[dsh-review-reply-check](https://github.com/PerryLink/dsh-review-reply-check)** | |
409
- | **[dsh-rulefile-check](https://github.com/PerryLink/dsh-rulefile-check)** | |
410
- | **[dsh-safety-brief-check](https://github.com/PerryLink/dsh-safety-brief-check)** | |
411
- | **[dsh-score](https://github.com/PerryLink/dsh-score)** | |
412
- | **[dsh-session-sync](https://github.com/PerryLink/dsh-session-sync)** | |
413
- | **[dsh-site-log-check](https://github.com/PerryLink/dsh-site-log-check)** | |
414
- | **[dsh-skill-pack-security](https://github.com/PerryLink/dsh-skill-pack-security)** | |
415
- | **[dsh-soe-decision-check](https://github.com/PerryLink/dsh-soe-decision-check)** | |
416
- | **[dsh-soilwater-check](https://github.com/PerryLink/dsh-soilwater-check)** | |
417
- | **[dsh-sop-sync-check](https://github.com/PerryLink/dsh-sop-sync-check)** | |
418
- | **[dsh-spc-gbt-adapter](https://github.com/PerryLink/dsh-spc-gbt-adapter)** | |
419
- | **[dsh-talk](https://github.com/PerryLink/dsh-talk)** | |
420
- | **[dsh-tender-extract](https://github.com/PerryLink/dsh-tender-extract)** | |
421
- | **[dsh-tender-matrix](https://github.com/PerryLink/dsh-tender-matrix)** | |
422
- | **[dsh-test-drive](https://github.com/PerryLink/dsh-test-drive)** | |
423
- | **[dsh-ticktick](https://github.com/PerryLink/dsh-ticktick)** | |
424
- | **[dsh-translate](https://github.com/PerryLink/dsh-translate)** | |
425
- | **[dsh-warranty-calc](https://github.com/PerryLink/dsh-warranty-calc)** | |
426
- | **[dsh-budget](https://github.com/PerryLink/dsh-budget)** | 🧊 CONGELADO — ver el README del repositorio |
427
- | **[dsh-claude-move](https://github.com/PerryLink/dsh-claude-move)** | 🧊 CONGELADO — ver el README del repositorio |
428
- | **[dsh-defend](https://github.com/PerryLink/dsh-defend)** | 🧊 CONGELADO — ver el README del repositorio |
429
- | **[dsh-draw](https://github.com/PerryLink/dsh-draw)** | 🧊 CONGELADO — ver el README del repositorio |
430
- | **[dsh-reach](https://github.com/PerryLink/dsh-reach)** | 🧊 CONGELADO — ver el README del repositorio |
431
- | **[dsh-background-agents](https://github.com/PerryLink/dsh-background-agents)** | 🚫 RETIRADO — ver el README del repositorio |
432
- | **[dsh-session-pin](https://github.com/PerryLink/dsh-session-pin)** | 🚫 RETIRADO — ver el README del repositorio |
433
- | **[dsh-team-rooms](https://github.com/PerryLink/dsh-team-rooms)** | 🚫 RETIRADO — ver el README del repositorio |
434
-
435
- </details>
436
-
437
- ## License
438
-
439
- [Apache License 2.0](LICENSE) © 2026 dsh-permission-rules contributors
440
-
441
- ### Instalar desde el mercado de DSH Desktop
442
-
443
- Todos los plugins de PerryLink pueden explorarse en el mercado integrado de DSH Desktop: **Market → Sources → add source → pegar** `https://perrylink-dsh-catalog.perrylink.workers.dev/catalog-source.json` **→ seleccionarlo**. La instalación sigue pasando por la verificación de identidad npm del mercado y tu confirmación.