@aimarket/warden 0.19.0 → 0.21.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 (79) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/README-es.md +31 -13
  3. package/README-fr.md +31 -13
  4. package/README-ru.md +31 -13
  5. package/README-zh.md +31 -13
  6. package/README.md +31 -13
  7. package/dist/atomic-screen.d.ts +61 -0
  8. package/dist/atomic-screen.d.ts.map +1 -0
  9. package/dist/atomic-screen.js +165 -0
  10. package/dist/atomic-screen.js.map +1 -0
  11. package/dist/flow-guard.d.ts +222 -14
  12. package/dist/flow-guard.d.ts.map +1 -1
  13. package/dist/flow-guard.js +1394 -97
  14. package/dist/flow-guard.js.map +1 -1
  15. package/dist/mcp-server.js +5 -0
  16. package/dist/mcp-server.js.map +1 -1
  17. package/dist/mcp-tools.d.ts.map +1 -1
  18. package/dist/mcp-tools.js +46 -9
  19. package/dist/mcp-tools.js.map +1 -1
  20. package/dist/review-diff.d.ts +36 -0
  21. package/dist/review-diff.d.ts.map +1 -1
  22. package/dist/review-diff.js +179 -7
  23. package/dist/review-diff.js.map +1 -1
  24. package/dist/static-scan.d.ts +1 -1
  25. package/dist/static-scan.d.ts.map +1 -1
  26. package/dist/static-scan.js +64 -1
  27. package/dist/static-scan.js.map +1 -1
  28. package/dist/task-adapter.d.ts +73 -0
  29. package/dist/task-adapter.d.ts.map +1 -0
  30. package/dist/task-adapter.js +379 -0
  31. package/dist/task-adapter.js.map +1 -0
  32. package/dist/task-cli.d.ts +3 -0
  33. package/dist/task-cli.d.ts.map +1 -0
  34. package/dist/task-cli.js +162 -0
  35. package/dist/task-cli.js.map +1 -0
  36. package/dist/types.d.ts +9 -0
  37. package/dist/types.d.ts.map +1 -1
  38. package/dist/wrap-cli.d.ts +11 -0
  39. package/dist/wrap-cli.d.ts.map +1 -1
  40. package/dist/wrap-cli.js +34 -2
  41. package/dist/wrap-cli.js.map +1 -1
  42. package/dist/wrap-screen.d.ts +3 -0
  43. package/dist/wrap-screen.d.ts.map +1 -0
  44. package/dist/wrap-screen.js +20 -0
  45. package/dist/wrap-screen.js.map +1 -0
  46. package/dist/wrap-state.d.ts +5 -0
  47. package/dist/wrap-state.d.ts.map +1 -1
  48. package/dist/wrap-state.js +3 -1
  49. package/dist/wrap-state.js.map +1 -1
  50. package/dist/wrap.d.ts.map +1 -1
  51. package/dist/wrap.js +282 -20
  52. package/dist/wrap.js.map +1 -1
  53. package/docs/flow-guard.es.md +302 -19
  54. package/docs/flow-guard.fr.md +303 -20
  55. package/docs/flow-guard.md +303 -18
  56. package/docs/flow-guard.ru.md +302 -18
  57. package/docs/flow-guard.zh.md +295 -13
  58. package/docs/gates.es.md +22 -6
  59. package/docs/gates.fr.md +21 -5
  60. package/docs/gates.md +22 -6
  61. package/docs/gates.ru.md +21 -6
  62. package/docs/gates.zh.md +8 -5
  63. package/docs/scanner-comparison.es.md +13 -11
  64. package/docs/scanner-comparison.fr.md +13 -11
  65. package/docs/scanner-comparison.md +13 -11
  66. package/docs/scanner-comparison.ru.md +13 -11
  67. package/docs/scanner-comparison.zh.md +13 -11
  68. package/docs/security-hardening.es.md +1 -1
  69. package/docs/security-hardening.fr.md +1 -1
  70. package/docs/security-hardening.md +1 -1
  71. package/docs/security-hardening.ru.md +1 -1
  72. package/docs/security-hardening.zh.md +1 -1
  73. package/examples/atomic-screen-server.mjs +42 -0
  74. package/native/macos/Package.swift +9 -0
  75. package/native/macos/Sources/WardenDesktop/NativeSurface.swift +246 -0
  76. package/native/macos/Sources/WardenDesktop/StandardIO.swift +139 -0
  77. package/native/macos/Sources/WardenDesktopHost/Main.swift +211 -0
  78. package/package.json +17 -5
  79. package/scripts/build-macos.mjs +22 -0
@@ -2,6 +2,273 @@
2
2
 
3
3
  > 🌐 [English](flow-guard.md) · [Русский](flow-guard.ru.md) · **Español** · [Français](flow-guard.fr.md) · [中文](flow-guard.zh.md)
4
4
 
5
+ Auditoría de seguimiento, 2026-10-23: los datos binarios `image`/`audio` de los resultados ya no consumen el límite de texto; una captura grande antes del texto no oculta etiquetas, títulos de ventanas con archivos de credenciales ni JSON en bloques de texto. Las pantallas devueltas por acciones del puntero y del teclado, incluidas las de `computer`, también actualizan la vista. Una confirmación simple conserva la vista anterior; un resultado de acción con solo una imagen la borra. Los argumentos salientes no reciben esta excepción y siguen sujetos a la comprobación de secretos. Las pruebas de regresión reproducen ambos fallos mediante hooks y el proxy stdio real. El código local ahora también exige aprobación para las acciones GUI opacas descritas abajo.
6
+
7
+ Estas correcciones del control de pantalla van en npm `0.21.0`. npm `0.20.0` no las incluía.
8
+
9
+ ## Límite de credenciales y fragmentos codificados (código local, 2026-10-23)
10
+
11
+ Después de registrar una lectura de credenciales y texto externo, **cada llamada saliente/MCP admitida requiere `ask` antes de ejecutar** (`deny` rechaza), sin excepciones por longitud, codificación, destinatario nombrado o etiqueta MCP de solo lectura. También se revisan escrituras normales del proyecto; se conserva la excepción de archivos de credenciales. `credentialRead` persiste entre turnos y al rotar el historial. Se impide así el envío automático de partes de uno a cuatro caracteres y datos con codificación desconocida mediante un límite, no prometiendo descifrarlo todo.
12
+
13
+ Secretos conocidos siguen rechazándose; coincidencias cortas ambiguas usan `secret_fragment`. Las demás revisiones usan `credential_egress`, sin acusar al texto inocuo de ser un secreto. GUI estricto sigue por defecto; ni sus excepciones de copia omiten este límite. `audit` solo registra; `off` desactiva. El cliente debe respetar la decisión.
14
+
15
+ **Menos revisiones innecesarias:** el operador puede incluir solicitudes públicas exactas y revisadas independientemente en el `flow-policy.json` protegido del usuario. `publicRequestDigest(tool, args)` de `dist/flow-guard.js` vincula herramienta completa y todos los argumentos, incluido destino. Ordena claves de objetos; conserva orden de arrays. Cada regla autoriza una llamada por sesión, atómicamente entre proxies, y se consume incluso si falla. No cubre cambios, reintentos, metadatos RPC extra ni argumentos inválidos. No exime secretos, texto privado, datos personales, fragmentos detectados, análisis incompleto, almacenes saturados, control GUI, shell, destrucción o configuración protegida. La regla autoriza esos datos, no demuestra que sean públicos: inspeccione el contenido decodificado antes de aprobarlo. No se instalan reglas automáticamente.
16
+
17
+ Ejemplo exclusivo para `mcp__mail__send_email` con `{"to":"bob@corp.com","subject":"status","body":"The public release is ready."}`:
18
+
19
+ ```json
20
+ {"credentialEgress":{"allow":[{"tool":"mcp__mail__send_email","requestSha256":"62e02f4cb9c21a472999fac9316b66fc8fefe3fcafd2b2860c32c5409dae4a65"}]}}
21
+ ```
22
+
23
+ Las llamadas con protección de flujo reenvían el objeto JSON inspeccionado, sin campos duplicados ignorados; sus valores ocultos no viajan con un permiso exacto.
24
+
25
+ Calcule localmente con `publicRequestDigest` tras revisar la solicitud completa. Límites: 32 reglas/permisos consumidos, 1024 nodos JSON, profundidad 32, 16 384 unidades UTF-16 serializadas y 65 536 unidades de trabajo de serialización. Un bloque inválido no concede nada. Se registra `credential_public_request` / `policy_allowed`, aprobación unknown; no afirma que una persona aceptara un formulario.
26
+
27
+ Los metadatos siguen en versión `v2`. Revisiones inocuas: **12/12 → 0/12** con reglas exactas revisadas; el coste por defecto sigue explícito.
28
+
29
+ **Decodificación y errores:** cola limitada para URL, base64/base64url canónico, hex UTF-8, escapes Unicode/hex escalares, entidades HTML numéricas y listas explícitas inglesas. Máximo cuatro niveles, 64 candidatos y 524 288 unidades UTF-16 acumuladas. Agotar presupuesto produce `payload_incomplete` y revisión, nunca un resultado limpio ni permiso público. Los literales inválidos no se ejecutan; no hay descompresión ni inferencia semántica. Estado/huellas corruptos no autorizan. Un índice v1 requiere revisión; una sesión nueva y relectura restauran el seguimiento. Hashes, posiciones, tamaños y colas salientes acotadas siguen siendo datos sensibles sin cifrado.
30
+
31
+ **Compromiso medido:** el escenario tiene 12 solicitudes inocuas y 16 ataques. El modo estricto revisa las 12 inocuas; reglas públicas exactas permiten las 12 una sola vez. Ambos perfiles permiten automáticamente **0/16 ataques**, con **0 rechazos falsos firmes**. La prueba separada solo de huellas usa 480 claves sintéticas y 758 archivos: 0 rechazos firmes y 2 archivos con revisión por coincidencia corta. Son mediciones diferentes, no una tasa de producción. El mismo conjunto de 23 casos sigue en **0/23 entregados por completo**, incluidos `secret-chunks-6-filler` y `secret-spelled-out`; dos casos solo de respuesta reciben nota/revisión. Tests stdio reales confirman cero llamadas para datos cortos/codificados sin aprobación, una concesión concurrente y ningún atajo por metadatos o reintentos.
32
+
33
+ ```bash
34
+ npm run build
35
+ npm test
36
+ node scripts/redteam/credential-egress.mjs /tmp/warden-egress.json
37
+ node scripts/redteam/flow-redteam.mjs /tmp/warden-redteam.json
38
+ node scripts/redteam/fragment-collisions.mjs /tmp/warden-fragments.json docs src test ../docs ../core
39
+ ```
40
+
41
+ **Límites:** control del límite de herramientas registrado, no análisis universal del flujo de información. Fuentes no vistas, integraciones desconocidas/renombradas, respuestas sin llamadas, canales temporales/ocultos, efectos ocultos del servidor, divulgación aprobada y clientes que omiten decisiones quedan fuera. Sin reglas revisadas hay deliberadamente más confirmaciones inocuas. Producción/npm no cambian; el oracle replay de AgentDojo se repitió abajo; modelo real y 115 millones de caracteres/HISTOR siguen siendo históricos.
42
+
43
+ ## Acciones GUI opacas (código local, 2026-10-23)
44
+
45
+ En el modo GUI estricto predeterminado, después de contenido no confiable, las llamadas MCP reconocidas de puntero y teclado preguntan antes de ejecutarse (`deny` las rechaza). Se cierran los pasos automáticos de clics por coordenadas, pantallas de solo píxeles, Enter en un campo con foco y etiquetas no inglesas **mediante una persona**, no interpretando cualquier pantalla. Escribir también pregunta: un salto de línea o un campo que envía automáticamente puede mandar el texto. Pegar pregunta incluso sin una lectura de credenciales registrada; con ella conserva la explicación `paste_after_secret`. Las demás decisiones GUI opacas usan `screen_action`.
46
+
47
+ Solo queda exenta una combinación exacta de seleccionar todo/copiar (`cmd+a`, `cmd+c`, `ctrl+a`, `ctrl+c`, con los alias de modificador admitidos) en un único campo de teclas conocido. Campos extra, secuencias, arrays incorrectos, saltos de línea finales y valores demasiado largos preguntan. Se mantienen las comprobaciones de secretos visibles. Los controladores genéricos conocidos (`computer`, `screen_control`, `desktop_control`, `browser_control`) preguntan si la acción falta o es desconocida. Una operación de observación completa y conocida como `screenshot` o `get_screen` queda exenta solo con el campo `action`. Se normalizan nombres camelCase, con puntos y con guiones. Un destino nombrado o permitido no autoriza acciones GUI.
48
+
49
+ La última lista por servidor (Playwright `[ref=e14]`, `[ref_14]`, listas numeradas y nodos JSON) solo aporta contexto explicativo. Otro formato ya no evita la aprobación; una muestra ayuda a mejorar la explicación, pero no hace falta para detener el clic. El aviso usa la palabra inglesa canónica del guardia cuando la reconoce, nunca la etiqueta original. Antes de aprobar, compruebe la ventana, el objetivo, el portapapeles y el efecto real. En el proxy stdio, cada acción exige su propio formulario MCP elicitation aceptado y con la casilla marcada; sin formulario, con rechazo o cancelación, el servidor no recibe la acción. El hook Claude devuelve `ask` al cliente, cuyo sistema de permisos debe aplicarlo.
50
+
51
+ En modo estricto habrá más confirmaciones, incluso al navegar o pulsar el cuerpo de un mensaje; no se ha medido la utilidad ni la fatiga. `audit` registra sin bloquear y `off` desactiva las comprobaciones. Hace falta contenido no confiable registrado previamente (las capturas lo son por defecto); marcar todas las observaciones como private puede quitar ese disparador. Se reconocen nombres de operación, no herramientas renombradas arbitrariamente, ejecutores JavaScript ni efectos ocultos de una herramienta maliciosa. Ahí siguen haciendo falta políticas del operador y aislamiento del sistema. No se añadió OCR ni clasificación visual de instrucciones: una inyección solo en píxeles aún puede cambiar la respuesta sin una llamada protegida. Las pruebas locales de hooks y del proxy stdio real verifican rechazo antes del envío, aprobación por acción y los cuatro casos comunicados. Estos cambios locales todavía no están desplegados ni publicados en npm.
52
+
53
+ La última captura mejora ahora la explicación de tres formas. Las coordenadas (`x`/`y`, `coordinate`, `coordinates` o `position`) se pueden asociar a un único elemento JSON mediante `bounds`, `boundingBox`, `bounding_box` o `rect`: números finitos en `{x,y,width,height}` o `{left,top,right,bottom}`, con dimensiones positivas. Se excluyen los bordes derecho e inferior; `center` solo coincide exactamente. Superposiciones, cajas o referencias contradictorias, números inválidos y arrays con convenciones xywh/xyxy ambiguas no producen un objetivo inferido. No se adivinan conversiones entre pantalla, viewport o zoom. La coincidencia describe la geometría de la captura anterior, no un objetivo actual verificado.
54
+
55
+ Para Enter/Return, un nodo inequívoco `focused: true` o `focusedRef`/`focusedElementId`/`activeElementId` identifica el elemento enfocado. Un campo explícitamente de una línea (`multiline: false`) en un formulario con un único control de envío reconocido recibe la explicación «Enter puede enviar»; un campo multilínea la recibe para cmd/ctrl+Enter. La relación exige `form`/`formId` o anidación JSON bajo el formulario. Foco ausente o contradictorio, multiline desconocido, formularios distintos o varios candidatos no permiten inferir el envío. Los identificadores accessibility/automation reconocidos y `type: "submit"` aportan pistas canónicas incluso sin etiqueta visible. Listas pequeñas explican etiquetas como Senden, Envoyer, Enviar y Wyślij. Identificadores, roles y traducciones siguen siendo pistas no confiables; el campo accessibility original no se copia a los metadatos adicionales ni se muestra en la solicitud; las referencias y etiquetas existentes permanecen en el estado de sesión.
56
+
57
+ Estas mejoras solo explican; los metadatos de pantalla nunca autorizan una interacción. Preguntar solo después de pegar o escribir omitiría mensajes ya rellenados, pagos y borrados. Geometría, foco o accessibility-id de una captura no prueban que navegar sea inocuo. Una nueva vista solo de imagen borra estas pistas; los nodos duplicados contradictorios no se usan. Los tests de hooks y stdio verifican explicaciones y rechazo antes del envío.
58
+
59
+ ## Regresiones de Gautam: códigos, NATO, metadatos Git y CI
60
+
61
+ El decodificador acotado añade listas de códigos decimales y palabras NATO. Requiere al menos cinco escalares válidos o cinco unidades NATO explícitas. Separan espacios, comas, punto y coma, barras verticales y saltos de línea; unidades desconocidas, escalares inválidos o marcadores de mayúsculas ambiguos rompen la secuencia. Admite alfa/alpha, juliett/juliet, xray/x-ray, dígitos/palabras, puntuación nombrada y capital/uppercase/lowercase. También inspecciona arrays JSON numéricos de 5–4096 enteros. Comparte los límites previos; agotarlos produce `payload_incomplete`. Sin eval ni descompresión arbitraria.
62
+
63
+ Decodificar no declara una fuga: sigue haciendo falta patrón/huella de secreto. Números normales y prosa NATO no se prohíben automáticamente. Los primeros 1–4 caracteres siguen bajo el límite de credenciales aunque no alcancen el mínimo del decodificador. Se prueban claves reconstruidas y texto inocuo, sin llamar secreto a toda coincidencia corta.
64
+
65
+ MCP sin etiquetas: `git_push`, `push_files`, `git_commit`, creación de commits y variantes camelCase/con puntos se clasifican como salida o persistencia publicable. El mensaje de commit puede publicarse después. `get_commit` / `list_commits` siguen siendo lecturas. Las etiquetas del operador requieren configuración correcta; el límite independiente cubre también herramientas MCP mal etiquetadas. Git shell mantiene sus controles. No se hizo ningún commit/push real.
66
+
67
+ **Cambio local del gate:** `npm run build && npm run test:redteam` ejecuta todo el hook replay con `--check`: los 23 originales más cinco de Gautam, **28 casos: 26 ataques con herramientas requieren ask/deny y dos solo de respuesta note/recheck**. Casos faltantes/duplicados, resultados incoherentes o una vía automática reabierta fallan con salida no cero. Pruebas con informes alterados verifican el fallo. Se integra en CI WARDEN/workspace, npm `prepublishOnly` y workflow del registro antes de publicar. Son archivos locales: no se inició CI remoto, release ni publicación. Una nota no demuestra bloquear una respuesta manipulada del modelo.
68
+
69
+ Reproducir: `npm run build`, `npm run test:redteam` o `node scripts/redteam/flow-redteam.mjs /tmp/warden-redteam.json --check`. Fuentes: `test/flow-guard-gautam.test.ts`, `scripts/redteam/flow-gate.mjs`.
70
+
71
+ ## Modo de tareas limitado: menos confirmaciones y control del efecto real
72
+
73
+ El GUI estricto sigue siendo predeterminado. `gui.mode: "policy"` / `gui.allow` se leen para migración, pero ya no autorizan clics, escritura ni teclas de navegación tras texto externo. La página puede falsificar incluso origin, rol e accessibility ID. Geometría, foco y traducciones solo explican. Nombres compuestos como `press_keys_then_execute` no obtienen la excepción Copy; metadatos RPC adicionales tampoco autorizan automáticamente.
74
+
75
+ El adaptador integrado (`node dist/mcp-server.js task`) aplica la restricción: lista recursos fijos, lee instantáneas UTF-8 locales fijadas por hash sin ejecutar HTML/JS y ejecuta operaciones HTTP revisadas por ID. El modelo no aporta URL, método, cabeceras, cuerpo, destinatario, adjunto ni ruta. No hay perfil del navegador, cookies, portapapeles del sistema, scripts, service workers, subprocesos ni reenvío general de herramientas. El GUI normal conserva sus confirmaciones. Es una alternativa estructurada para tareas admitidas, no un sandbox universal de navegador/VM.
76
+
77
+ **Configuración explícita:** guarde el manifiesto en `<WARDEN_STATE_DIR>/task-manifest.json` y la política en el flow policy del usuario. Cambie ruta, hash y endpoint; `expiresAt` debe quedar como máximo a 24 horas. Exponga solo material elegido deliberadamente. El hash del recurso corresponde a `The public release is ready.\n`. Revise cada petición y calcule `manifestSha256` sobre los bytes exactos guardados, incluidos espacios y salto final. La política de ejemplo corresponde al manifiesto mostrado con un salto final. No se instalan permisos automáticamente.
78
+
79
+ ```json
80
+ {
81
+ "version": 1,
82
+ "taskId": "public-report",
83
+ "expiresAt": "2026-10-23T23:00:00.000Z",
84
+ "resources": [
85
+ {
86
+ "id": "report",
87
+ "title": "Public report",
88
+ "path": "/absolute/path/public-report.txt",
89
+ "sha256": "1403cece199725b166f0f6e59faa9d45e3d5a25db26080fecf95e818ac8d4d0d"
90
+ }
91
+ ],
92
+ "operations": [
93
+ {
94
+ "id": "send_report",
95
+ "title": "Send reviewed report to Bob, no attachments",
96
+ "maxUses": 1,
97
+ "request": {
98
+ "method": "POST",
99
+ "url": "https://mail.example/api/send",
100
+ "headers": {
101
+ "content-type": "application/json",
102
+ "idempotency-key": "public-report-once"
103
+ },
104
+ "body": "{\"to\":\"bob@corp.com\",\"body\":\"The public release is ready.\",\"attachments\":[]}"
105
+ }
106
+ }
107
+ ]
108
+ }
109
+ ```
110
+
111
+ ```json
112
+ {
113
+ "mode": "ask",
114
+ "taskAdapter": {
115
+ "mode": "capabilities",
116
+ "server": "warden_task",
117
+ "manifestSha256": "1531d08beffb7c86d6f236c3bdfa85a8fbac1c3e8734d701e79455c53d0d94b7"
118
+ }
119
+ }
120
+ ```
121
+ ```json
122
+ {
123
+ "mcpServers": {
124
+ "warden_task": {
125
+ "command": "node",
126
+ "args": [
127
+ "/absolute/path/warden/dist/mcp-server.js",
128
+ "task"
129
+ ],
130
+ "env": {
131
+ "WARDEN_STATE_DIR": "/absolute/state/warden",
132
+ "WARDEN_FLOW_POLICY": "/absolute/state/warden/flow-policy.json"
133
+ }
134
+ }
135
+ }
136
+ }
137
+ ```
138
+
139
+ ```sh
140
+ node -e 'console.log(new Date(Date.now() + 3600000).toISOString())'
141
+ node --input-type=module -e 'import {createHash} from "node:crypto"; import {readFileSync} from "node:fs"; console.log(createHash("sha256").update(readFileSync(process.argv[1])).digest("hex"))' /absolute/state/warden/task-manifest.json
142
+ ```
143
+
144
+
145
+ Registre **`warden_task`** con comando `node`, argumentos `[/absolute/path/warden/dist/mcp-server.js, task]` y el mismo `WARDEN_STATE_DIR` / `WARDEN_FLOW_POLICY` que los hooks o `wrap --flow warden_task`. Vincule ese nombre solo al adaptador integrado de WARDEN: dárselo a otro servidor le concede autoridad. Nombre y `readOnlyHint` no prueban identidad. Política ausente, estricta o inválida rechaza la red; `deny`, `audit`, `off` globales tampoco permiten ejecutar. El cliente puede mantener sus propios permisos.
146
+
147
+ Llamadas: `task_list {}`, `task_read {"id":"report"}`, `task_execute {"id":"send_report"}`. Solo se acepta el ID fijo; campos extra, claves JSON duplicadas, arrays y metadatos RPC ocultos se rechazan. `task_adapter_delegated` registra delegación, no ejecución ni aprobación humana. El runtime revalida hash, esquema, caducidad y política; reserva el presupuesto compartido con bloqueo entre procesos y fsync antes de enviar. Reiniciar o modificar el manifiesto no repone un task ID gastado. Error/resultado incierto y cancelación tras enviar gastan el permiso; cancelar antes no. Estado corrupto o lock abandonado exige intervención y conciliación, no borrar estado para reintentar.
148
+
149
+ Solo peticiones HTTPS fijas; `loopback-test` permite además HTTP a localhost numérico para pruebas aisladas. Sin redirecciones, cookies, ejecución de enlaces/instrucciones ni reintentos. Tiempo total y respuesta limitados. `bearerEnv` referencia una variable elegida por el operador: el token solo va al endpoint fijo, nunca al listado ni a auditoría. La auditoría guarda IDs/resultados, no payloads/tokens; las respuestas siguen siendo datos no confiables.
150
+
151
+ **Pagos y transacciones:** fije cuenta/factura, importe, moneda y cuerpo; use `maxUses: 1` y un `idempotency-key` fijo admitido por el proveedor. Un envío no garantiza liquidación exactamente una vez. Tras timeout/error, concilie con el proveedor antes de otra autorización. Las pruebas usan loopback y claves sintéticas, sin pagos reales.
152
+
153
+ **Límites:** se confía en el proceso WARDEN y se protegen policy/state, runtime y vinculación MCP. Cada endpoint conserva su semántica; GET no se supone inocuo. Permisos erróneos del operador, secretos literales autorizados, efectos del servidor, canales ocultos de ID/orden/tiempo y otros clientes/herramientas que eviten el guardia quedan fuera. No se añadió OCR ni interceptación universal del GUI.
154
+
155
+ **Verificación 2026-10-23:** nuevo oracle replay determinista de AgentDojo 0.1.35 / v1.2.2, Docker local sin red ni API de modelo: variante intent 0/579 ataques de acción entregados con éxito; 57/97 tareas limpias sin preguntas, 58/97 completadas y 608/949 completadas bajo ataque (antes 613/949). `ask` cuenta como rechazo; objetivos sin referencia o solo de respuesta no quedan certificados. AgentDojo no tiene GUI/adaptador: los cubren stdio, múltiples procesos y loopback. No se repitieron las mediciones históricas con modelo real, 115 millones de caracteres o HISTOR. [Resumen](../scripts/agentdojo/results/summary-2026-10-23-local.json).
156
+
157
+ Regresiones: `test/task-adapter.test.ts`, `test/flow-guard-gui-policy.test.ts`, `test/flow-guard-credential-boundary.test.ts`, `test/flow-guard-secret-regression.test.ts`: etiquetas/IDs falsos, esquema, recursos fijados, concurrencia/reinicio, redirecciones, destinatario/cuerpo exactos y primeros 1–4 caracteres en raw, hex, base64, Unicode y codificaciones desconocidas. Los casos ambiguos se revisan sin afirmar falsamente que todo fragmento corto es un secreto.
158
+
159
+ ## Backend nativo macOS: AppKit dentro de la aplicación
160
+
161
+ **Implementado:** el paquete Swift [WardenDesktop](../native/macos/Package.swift), `WardenDesktopSurface` y `WardenDesktopStandardIO` se ejecutan dentro de la aplicación propietaria de la ventana. `warden-desktop --demo` abre una ventana AppKit real con un controlador de botón local. No controla otras aplicaciones, no envía entrada global ni realiza operaciones de red/pagos. La confirmación estricta de wrap sigue por defecto. Requiere integración explícita de la aplicación; no es un inyector de escritorio universal.
162
+
163
+ Compile y ejecute desde WARDEN. Requiere macOS, Xcode command-line tools y una sesión WindowServer activa. Destino de compilación: macOS 13+ y arquitectura arm64/x86_64 del host; solo se verifica la arquitectura del registro local. Se incluyen fuentes, no un binario precompilado. Sin nuevas dependencias npm de ejecución:
164
+
165
+ ```sh
166
+ npm run build
167
+ npm run build:macos
168
+ node dist/mcp-server.js wrap --id macos-owned-app --flow native --screen-read screenshot --screen-atomic -- native/macos/.build/warden-desktop --demo
169
+ npm run test:macos
170
+ ```
171
+
172
+ Ejemplo de acción nativa: referencias de cadena. La alternativa usa solo `x` e `y` en puntos globales desde arriba a la izquierda, no píxeles. Objetivos cubiertos o ambiguos se rechazan:
173
+
174
+ ```json
175
+ {"name":"click","arguments":{"index":"3"}}
176
+ ```
177
+
178
+ **Límite atómico:** prepare retiene el `NSButton` exacto, su target `NSObject`, selector e implementación con ABI verificada mediante `method_getImplementation`. En MainActor, commit compara revisión, rol/ID/geometría/etiqueta, identidad, ventana/foco/modalidad, opacidad de los ancestros y hit-test. Se rechazan controles/ventanas transparentes, ventanas bloqueadas por modalidad y sheets pendientes, incluida la relación lógica `sheetParent`. Tras hit-test vuelve a capturar propiedades, comprueba ambos relojes y compara finalmente la revisión después de todos los callbacks inyectados. Invoca el IMP fijado mediante `NativeAction` en el mismo turno principal. Omite deliberadamente `NSApp.sendAction`: una redefinición puede encolar, redirigir o declarar éxito sin llamar al método aprobado. No usa `AXPress`, `CGEvent`, `performClick` animado, búsqueda first-responder ni coordenadas encoladas. Solo botones momentáneos registrados, visibles y habilitados con firma `void action(id sender)`. Se rechazan efectos directos, texto, acordes, portapapeles, campos seguros, vistas no registradas y PID externos.
179
+
180
+ **Integración:** importe el paquete Swift local. En MainActor cree `WardenDesktopSurface(window:applicationID:)`, configure accessibility element/role/identifier y llame a `register(id:button:target:selector:)`. Retenga la superficie y `WardenDesktopStandardIO(surface:)`; `start()` atiende el pipe stdio heredado exclusivo. La observación es metadato estructurado de controles registrados, no captura de píxeles ni árbol AX global. Máximo 64 controles, referencias/ID únicos y targets explícitos retenidos; se rechazan target nil y firmas Objective-C inseguras. Consulte [fuentes](../native/macos/Sources/WardenDesktop/NativeSurface.swift) y [host nativo](../native/macos/Sources/WardenDesktopHost/Main.swift).
181
+
182
+ Todos los cambios de modelo/destinatario/controlador deben hacerse en MainActor llamando a `invalidate()` **antes** de mutar fuera del controlador aprobado, incluidos cambios invisibles y A → B → A. Notificaciones y huellas recientes complementan la regla, sin demostrar todos los cambios ocultos. Los controladores aprobados deben ser síncronos, sin bucles modales anidados, reenvío asíncrono ni sustitución de responder. Sus cambios propios quedan cubiertos por la rotación de revisión del commit. Si la aplicación no cumple ese contrato, no habilite modo atómico. No hay alternativa para aplicaciones ajenas sin integración: Apple expone [lecturas AX y acciones separadas](https://developer.apple.com/documentation/applicationservices/1462091-axuielementperformaction), no nuestro contrato de revisión.
183
+
184
+ Los tickets vinculan todos los argumentos y caducan por reloj de pared y monotónico en un máximo de cinco minutos. Se consumen antes del envío; cancelación, repetición, reinicio o discrepancia no permiten reutilizarlos. Un envío fallido/incierto bloquea la autoridad nativa; no reinicie ni vuelva a autorizar solo para repetir un pago. committed acredita el envío local al controlador, **no** entrega de correo, liquidación ni trabajo asíncrono terminado. Use conciliación e idempotencia persistente del servicio para esos resultados. Wrap conserva el bloqueo si se pierde la respuesta.
185
+
186
+ **Gate nativo:** `npm run test:macos` compila por separado binarios de producción/prueba y ejecuta ventanas reales, stdio y formularios wrap. Cubre cambios de objetivo/controlador/geometría/ventana, sustitución de objeto idéntico visualmente, ABA, reentrada hit-test, caducidad/retroceso/repetición/reinicio/cancelación, huellas entre lenguajes y etiquetas multilingües. Los métodos de mutación de prueba no existen en producción. La suite es independiente de `npm test`; otro sistema, herramientas ausentes o GUI inaccesible hacen fallar el gate, sin skip silencioso. CI local incorpora un gate macOS; no se ejecutó remotamente. Las pruebas no solicitan permisos globales Accessibility/Screen Recording ni modifican TCC. Sin publicación/despliegue.
187
+
188
+ **Auditoría local de seguimiento (2026-10-24):** prepare y commit vuelven a leer las propiedades nativas después de `hitTest`; cambios de controlador, etiqueta, estado habilitado o marco provocan rechazo aunque la aplicación omita `invalidate()`. Ambos relojes se comprueban otra vez justo antes del envío. El argumento Objective-C del remitente debe ser un objeto (`@`), nunca un bloque (`@?`). El servidor atómico TypeScript también aplica un plazo monotónico y comprueba la caducidad antes del efecto. Las regresiones cubren estos casos, cambios durante prepare, recuperación de capacidad y reentrada al controlador nativo. Esto no sustituye el contrato de propiedad: getters y hit-test no deben ejecutar bucles anidados ni efectos; los cambios ocultos de modelo/destinatario siguen exigiendo invalidación previa. Solo se admiten aplicaciones de confianza con integración.
189
+
190
+ **Contrato de llamada directa (segunda auditoría local, 2026-10-24):** los controladores registrados deben ser seguros para invocación directa y comprobar sus permisos; no dependa de una redefinición de `NSApplication.sendAction` ni del responder para autorizarlos. Relojes y getters son código confiable y síncrono del host. Cambios ocultos y ABA siguen requiriendo `invalidate()` previo. Regresiones: modalidad/opacidad durante hit-test, cambio de implementación, transparencia parcial estable, intercepción de dispatch e invalidación desde el último reloj. Estas comprobaciones van en npm `0.21.0`. El informe local no sustituye el recibo firmado release-quality: su digest actual cubre JavaScript en `dist`, no las fuentes del SDK nativo; la validación nativa es independiente.
191
+
192
+ ## Verificación atómica del objetivo en el servidor
193
+
194
+ **Activación explícita:** `--screen-atomic` requiere `--flow`, `--screen-read` y un modo flow que aplique la protección. La aprobación estricta sigue siendo la predeterminada. Se requiere un servidor participante de confianza; las anotaciones y una versión declarada no prueban confianza. Un servidor incompatible provoca rechazo antes de preguntar. No hay alternativa de clic directo. El modo anterior basado solo en capturas sigue sin ser atómico.
195
+
196
+ Compile WARDEN y ejecute este ejemplo local desde su directorio. El ejemplo controla un formulario virtual: no genera entrada del sistema, correo, pagos ni tráfico de red:
197
+
198
+ ```sh
199
+ node dist/mcp-server.js wrap --flow screen --screen-read screenshot --screen-atomic -- node examples/atomic-screen-server.mjs
200
+ ```
201
+
202
+ **Protocolo:** antes de preguntar, el proxy llama de forma privada a `warden/screen/prepare` con la acción exacta y la observación configurada. Parámetros de ejemplo:
203
+
204
+ ```json
205
+ {
206
+ "protocol": "warden.screen.atomic.v1",
207
+ "observation": { "name": "screenshot", "arguments": {} },
208
+ "action": { "name": "click", "arguments": { "index": 3 } }
209
+ }
210
+ ```
211
+
212
+ El servidor devuelve `protocol`, un `ticket` opaco de un solo uso, la `revision` de la superficie, el `stamp` del objetivo, `expiresAt` y `observation`. El proxy verifica el sello contra esa observación, muestra la acción y, tras una aceptación explícita, vuelve a comprobar las definiciones, el contexto de riesgo y el objetivo reciente. Envía `warden/screen/commit` con `{protocol,ticket,expected:{revision,stamp},action}` en lugar de la llamada directa. El servidor consume el ticket, compara la revisión actual, el objetivo resuelto y los argumentos exactos y ejecuta de forma síncrona en la misma sección crítica. No puede ceder el bucle de eventos ni encolar un clic por coordenadas entre la comparación y el efecto. Todo cambio, incluidos el controlador/destinatario y A → B → A, debe cambiar la revisión. El efecto usa la referencia resuelta, sin buscar después por coordenadas.
213
+
214
+ Integre `AtomicScreenServer` de `@aimarket/warden/atomic-screen` (`dist/atomic-screen.js` en local). Enrute los tres métodos a `prepare(params)`, `commit(params)` y `cancel(params)`, y la observación a `read()`. Todas las rutas directas de efectos deben rechazar. Comparta una autoridad por superficie entre conexiones; los demás cambios pasan por `replace(snapshot)`. El código de confianza `apply(action, target)` debe ser síncrono y devolver `{result,snapshot}`, con el resultado MCP habitual. Se rechazan funciones asíncronas; una excepción o resultado inválido bloquea la superficie hasta una conciliación y sustitución explícitas. Véase el [servidor ejecutable](../examples/atomic-screen-server.mjs).
215
+
216
+ Los tickets duran como máximo cinco minutos, vinculan toda la acción, se consumen al intentar commit y no sobreviven al reinicio. Límite: 256 pendientes. El cliente no puede invocar el protocolo privado mediante wrap; los tickets no llegan al modelo ni al registro flow. `warden/screen/cancel` descarta una preparación sin usar tras rechazo/cancelación; las abandonadas caducan. Los argumentos atómicos deben caber completos en 1500 caracteres escapados de la aprobación; el exceso se rechaza en vez de truncarse.
217
+
218
+ Un recibo validado `committed` contiene `protocol,status,revisionBefore,revisionAfter,stamp,result`; wrap devuelve solo `result`. Un recibo validado `refused` contiene `protocol,status,reason` e indica que no hubo efecto. La auditoría distingue `atomic_committed` / `atomic_refused`. Un recibo inválido, error de transporte o resultado perdido es `execution_uncertain`: sin reintento y con el bloqueo GUI retenido hasta la conciliación del operador. El efecto puede haber ocurrido antes de perder la respuesta. El ticket no sustituye una clave de idempotencia de pago persistente.
219
+
220
+ **Límite de propiedad:** esta implementación cierra la carrera solo si el servidor de confianza controla todo el estado y el efecto síncrono de la superficie. La autenticación o el transporte exclusivo deben restringir prepare/commit a proxies de confianza; un ticket no demuestra consentimiento humano. Una autoridad en un proceso no bloquea todo el sistema operativo. Encerrar entrada de ratón asíncrona o un RPC Playwright/desktop en esta API no hace atómica la superficie remota. Un backend nativo necesita una transacción/revisión real de la plataforma o un modelo de aplicación síncrono controlado. El backend AppKit nativo dentro de la aplicación se describe en la sección macOS; las aplicaciones externas sin integrar siguen sin admitirse. Metadatos falsos, actores externos no registrados y controladores autorizados dañinos quedan fuera de la garantía. El hook Claude no puede realizar el commit privado tras la aprobación. Las regresiones cubren cambios tras la última lectura, ABA/controlador, repetición, caducidad, reinicio, reentrada, rechazo de llamadas directas y efectos inciertos. Sin publicación ni despliegue.
221
+
222
+ ## Auten: una aprobación del resultado y pantalla nueva tras aprobar
223
+
224
+ **Resultado:** añada el fragmento `runs` al manifiesto fijado, guarde JSON con dos espacios y salto final, recalcule el hash y use la política siguiente con `taskAdapter.mode: "review"`. Llame `task_run {"id":"mail_to_bob"}`. El adaptador pregunta una vez mediante MCP form elicitation antes del primer efecto: resultado, orden completo, URLs y cuerpos exactos, sin credenciales de cabecera. La casilla empieza en false. Sin formulario, aceptación por defecto, rechazo o cancelación no autorizan. `strict` sigue por defecto; `capabilities` conserva preautorización explícita.
225
+
226
+ El ejemplo sustituye preparación/envío GUI por su efecto HTTP fijo. Hasta ocho operaciones distintas por plan, 16 planes y 12 000 caracteres escapados de vista completa. El modelo no cambia pasos, orden, destinatario, cuerpo ni resultado. `review` desactiva `task_execute` individual para impedir saltar la pregunta. Lecturas/listas son silenciosas; permisos independientes del cliente/herramientas sensibles pueden preguntar.
227
+
228
+ Un bloqueo entre procesos cubre pregunta y ejecución. Se comprueba el presupuesto antes de preguntar y se reserva atómicamente todo el plan y cada operación después de aceptar. Es de un uso incluso al reiniciar. Cada paso revalida manifiesto/política, caducidad, cancelación y credenciales. Desvío/error/resultado incierto detiene lo restante y conserva todos los presupuestos gastados, incluso pasos no ejecutados. No hay rollback de efectos previos. Las respuestas no pueden alterar el siguiente paso/destino. Rechazo o cancelación antes de reservar no gastan. Concilie pagos inciertos antes de tocar estado/locks. Auditoría separa aprobación, reserva, finalización y parada, sin cuerpos/tokens.
229
+
230
+ ```json
231
+ {
232
+ "runs": [
233
+ {
234
+ "id": "mail_to_bob",
235
+ "title": "Send the reviewed public report to bob@corp.com in Mail",
236
+ "operations": [
237
+ "send_report"
238
+ ]
239
+ }
240
+ ]
241
+ }
242
+ ```
243
+
244
+ ```json
245
+ {
246
+ "mode": "ask",
247
+ "taskAdapter": {
248
+ "mode": "review",
249
+ "server": "warden_task",
250
+ "manifestSha256": "c69bafc53aa2e4a1a82dd9a20accbe2e4de5372d7545509fe3f6ccf18fdc5580"
251
+ }
252
+ }
253
+ ```
254
+
255
+ **Pantalla nueva solo en wrap:** seleccione explícitamente una herramienta de observación del mismo servidor revisado:
256
+
257
+ ```sh
258
+ node dist/mcp-server.js wrap --flow screen --screen-read get_elements -- node /absolute/path/screen-server.js
259
+ node dist/mcp-server.js wrap --flow screen --screen-read computer --screen-read-input '{"action":"screenshot"}' -- node /absolute/path/screen-server.js
260
+ ```
261
+
262
+ `--screen-read` requiere `--flow`; `--screen-read-input` es JSON estricto: entrada vacía para nombre de observación conocido o solo `action` de lectura para controlador genérico. `readOnlyHint` no concede autoridad. Sin esta configuración continúa el GUI estricto por llamada, sin afirmar revalidación posterior. El hook Claude no recibe un callback entre diálogo y ejecución.
263
+
264
+ Wrap observa antes de preguntar, y tras aceptar revisa definiciones y observa otra vez. Compara la respuesta nueva, no una caché: objetivo único, rol, referencia exacta, límites, contexto y foco coherente para teclado; también ID de accesibilidad, etiqueta y formulario cuando existen. Cambios, ausencia, solapamiento/ambigüedad, lista incompleta, solo píxeles o error rechazan. No adivina escala/coordenadas. Esquemas con acciones múltiples/campos extra no se comparan. Riesgo cambiado o nuevos secretos también detienen. Se respeta cancelar durante la última lectura.
265
+
266
+ Los efectos GUI conocidos se serializan entre proxies con `WARDEN_FLOW_SESSION` / state compartidos, incluso otro proxy sin revalidación. Un resultado normal libera; caída/resultado ausente deja lock incierto para el operador. Lecturas pueden seguir. `screen_validation`: `unchanged`, `refused_without_approval`, `refused_after_approval`, `execution_uncertain`; aprobar o coincidir metadatos no demuestra ejecución.
267
+
268
+ **Límite:** reduce la ventana de carrera, no vuelve atómicos snapshot y clic. Otros procesos pueden cambiar el UI después; el servidor puede devolver caché/metadatos falsos; ID/caja iguales no prueban handlers ni efectos. La solución completa necesita validación de revision/objetivo y efecto atómicos en un servidor confiable. No hay aprobación global de click/paste/Send arbitrarios; agrupar solo sirve al adaptador estructurado que aplica las restricciones. Ataques solo de respuesta siguen fuera.
269
+
270
+ Regresiones locales: `test/wrap-screen.test.ts`, `test/wrap.test.ts`, `test/task-adapter.test.ts`. [Checkpoint AgentDojo local](../scripts/agentdojo/results/summary-2026-10-23-auten-local.json), replay sin API de modelo ni GUI. Sin publicación, despliegue ni comentario enviado.
271
+
5
272
  ## Explicaciones de permisos (código aún no publicado)
6
273
 
7
274
  La pregunta muestra la herramienta, hasta cuatro destinos y el motivo. Omite cuerpos de mensajes, código shell, credenciales/rutas/parámetros de URL y valores sensibles reconocidos. El origen de cada destino no se rastrea; una coincidencia de huella tampoco identifica un archivo concreto. Ya no se atribuye a los últimos archivos leídos. Revise la llamada completa en el cliente.
@@ -16,16 +283,19 @@ La guardia comprueba llamadas después de recibir texto externo; no es un sandbo
16
283
  - Las escrituras reconocidas a política/estado, hooks de Claude y hooks/config de Git están protegidas, incluidas variables de ruta y enlaces simbólicos. Esto no sustituye permisos del sistema operativo.
17
284
  - Se analiza la autoridad real de las URL y se comprueban todos los destinos. Un enlace en el cuerpo no autoriza otro destinatario. Los buzones de la política coinciden exactamente; un dominio no permite todos sus buzones. Una mención del usuario no expresa autorización para cualquier acción.
18
285
  - Resolver contactos requiere `contactTools` explícito, etiqueta `private`, registros estructurados `name`/`email` y el nombre completo en el mensaje del usuario. Los nombres ambiguos piden permiso. Archivos locales, prosa, YAML y contactos almacenados por la versión anterior no conceden autoridad. Confíe solo en directorios que el agente y terceros no puedan modificar.
19
- - Las huellas inspeccionan texto visible con decodificación limitada URL/base64/hex. Desde el 2026-10-22 los valores con aspecto de clave (secuencias aleatorias en archivos de credenciales y formatos de token conocidos, se lean donde se lean) se registran además en ventanas de 10 letras y dígitos: un trozo de clave, la clave al revés, con separadores, en trozos base64 o con letras cirílicas o griegas parecidas se rechaza. Cada argumento recuerda la cola de lo que envió tras el texto externo, así que los trozos enviados seguidos por el mismo argumento se comprueban enteros. Los trozos de menos de 10 caracteres envueltos cada vez en palabras distintas siguen pasando, y también el formato inicial de la clave (`rk_live_`). No leen cualquier archivo al enviarlo ni cubren todas las codificaciones o herramientas. Se permiten normalmente las ediciones del proyecto; ejecutar código es otra decisión.
286
+ - Las huellas inspeccionan texto visible con decodificación limitada URL/base64/hex. Desde el 2026-10-22 los valores con aspecto de clave (secuencias aleatorias en archivos de credenciales y formatos de token conocidos, se lean donde se lean) se registran además en ventanas de 8 letras y dígitos, 10 para claves hex: un trozo de clave, la clave al revés, con separadores o caracteres invisibles, en trozos base64 o con letras cirílicas o griegas parecidas se rechaza. Son los tamaños más pequeños con los que ninguna de 480 claves inventadas en ocho formatos coincidió en ningún punto de 115 millones de letras y dígitos (este monorepo y 1000 paquetes de HISTOR) de texto corriente; las partes que comparten todas las claves de un tipo (el identificador de algoritmo de una clave DER, la cabecera y los campos de un JWT) nunca se registran. Cada argumento recuerda la cola de lo que envió tras el texto externo, así que los trozos enviados seguidos por el mismo argumento se comprueban enteros. El código local del 2026-10-23 añade la correlación de pares de seis caracteres y las listas explícitas descritas arriba. El formato inicial (`rk_live_`) por sí solo no cuenta como secreto. No leen cualquier archivo al enviarlo ni cubren todas las codificaciones o herramientas. Se permiten normalmente las ediciones del proyecto; ejecutar código es otra decisión.
287
+ - Tras texto externo, escribir en un archivo corriente del proyecto una clave leída en la sesión pregunta (`deny` rechaza): los archivos del proyecto se publican luego sin otra llamada. `.env`, `.npmrc` y los demás archivos de credenciales son el sitio de las claves y pasan; una edición que solo quita una clave no coincide.
288
+ - Una dirección que solo aparece en tu prompt dentro de texto pegado, de una línea citada (`>`), de las cabeceras de un mensaje pegado o bajo una marca de reenvío no es una que nombraste: enviarle algo pregunta una vez. Un punto o una coma tras una dirección que escribiste ya no la ocultan.
20
289
  - Una herramienta MCP sin etiqueta del operador es saliente si su nombre lo dice (send, post, share …), si lleva una URL o, desde el 2026-10-22, si nombra a una parte: un argumento como `to`, `email`, `attendees`, `assignee`, `collaborator` o `username`, o cualquier correo o IBAN. `warden-mcp flow-labels` lista las herramientas MCP sin etiqueta que usaron tus sesiones, con la suposición del guardián y un bloque `labels` para corregir y pegar en la política.
21
- - Una inyección que solo cambia la respuesta no hace ninguna llamada que rechazar. Desde el 2026-10-22 el hook Stop del plugin añade una línea bajo una respuesta cuyo turno leyó texto externo, con esas fuentes, y lo dice cuando alguna se dirigió al asistente con instrucciones. Nunca bloquea. `answerNote`: `always` (por defecto), `instruction` (solo ese caso) u `off`; fuera del plugin, registra `warden-mcp hook stop`.
290
+ - Por defecto, tras contenido externo, las acciones reconocidas de pantalla requieren aprobación con independencia de las coordenadas, el texto de la captura, el formato de elementos o el idioma de la etiqueta. Incluye clics/toques/arrastres, escritura/relleno, pegado, Enter/Return y acciones de teclado desconocidas. `deny` las rechaza. Las palabras inglesas solo mejoran la explicación; una etiqueta como Inbox no autoriza el clic. Véase el modo de tareas limitado arriba.
291
+ - Una inyección que solo cambia la respuesta no hace ninguna llamada que rechazar. Desde el 2026-10-22 el hook Stop del plugin añade una línea bajo una respuesta cuyo turno leyó texto externo, con esas fuentes, y lo dice cuando alguna se dirigió al asistente con instrucciones. Si la respuesta repite 12 letras y dígitos seguidos de las frases donde ese texto se dirigió al asistente, el hook además devuelve la respuesta una vez (`decision: block`) para comprobarla frente a la petición del usuario antes de que este la lea; el usuario solo pierde el tiempo de esa comprobación. `answerNote`: `always` (por defecto), `instruction` (solo ese caso) u `off`; fuera del plugin, registra `warden-mcp hook stop`.
22
292
  - El proxy registra también errores JSON-RPC y de herramientas. Retiene una respuesta si no puede registrarla; un estado de sesión corrupto bloquea la comprobación.
23
293
 
24
- Publicado en npm `0.17.0`: `npx -y @aimarket/warden@0.19.0 hook pre-tool-use`, o desde el código fuente en WARDEN, `npm ci && npm run build`, luego `node dist/mcp-server.js hook pre-tool-use`, con los hooks prompt/post también registrados. Política: `~/.local/state/warden/flow-policy.json` o `WARDEN_FLOW_POLICY`; modos `ask`, `deny`, `audit`, `off`. Véase el ejemplo vigente en [inglés](flow-guard.md).
294
+ Publicado en npm `0.17.0`: `npx -y @aimarket/warden@0.21.0 hook pre-tool-use`, o desde el código fuente en WARDEN, `npm ci && npm run build`, luego `node dist/mcp-server.js hook pre-tool-use`, con los hooks prompt/post también registrados. Política: `~/.local/state/warden/flow-policy.json` o `WARDEN_FLOW_POLICY`; modos `ask`, `deny`, `audit`, `off`. Véase el ejemplo vigente en [inglés](flow-guard.md).
25
295
 
26
- Proxy: `node dist/mcp-server.js wrap --flow mail -- node mail-server.js`. Use un `WARDEN_FLOW_SESSION` nuevo y compartido por los proxies de una conversación. El PID padre predeterminado no une lanzadores diferentes. El proxy no ve el prompt ni herramientas integradas y rechaza lo que requeriría preguntar. `trustedSources` se aplica a la URL de WebFetch, no a enlaces en argumentos MCP.
296
+ Proxy: `node dist/mcp-server.js wrap --flow mail -- node mail-server.js`. Use un `WARDEN_FLOW_SESSION` nuevo y compartido por los proxies de una conversación. El PID padre predeterminado no une lanzadores diferentes. El proxy no ve el prompt ni herramientas integradas. Lo que requeriría preguntar se pregunta con el formulario de elicitation MCP del cliente cuando lo ofrece (la respuesta queda en el registro como `approvalOutcome`) y, si no, o con `--ask refuse`, se rechaza. `trustedSources` se aplica a la URL de WebFetch, no a enlaces en argumentos MCP.
27
297
 
28
- **Qué versión describen las cifras.** La tabla de reproducción de abajo se volvió a ejecutar el 2026-10-22 sobre el código actual: las correcciones de la auditoría más los cambios del 2026-10-22. Los cambios del 2026-10-22 no alteran ninguna decisión (los 1046 registros coinciden con el código anterior); las correcciones de la auditoría costaron una tarea sin pregunta y 19 tareas hechas bajo ataque. Las tablas y costes de la ejecución con modelo siguen describiendo la versión anterior a la auditoría, hasta `2328be980`; no hubo nueva ejecución con modelo. El replay es una ruta fija, no el peor caso adaptativo; los ataques con el código en la mano del 2026-10-22 están [más abajo](#qué-cambió-el-2026-10-22-y-por-qué).
298
+ **Qué versión describen las cifras.** La tabla de reproducción de abajo se volvió a ejecutar el 2026-10-22 sobre el código de aquel día: las correcciones de la auditoría más los cambios del 2026-10-22. Los cambios del 2026-10-22 no alteran ninguna decisión (los 1046 registros coinciden con el código anterior); las correcciones de la auditoría costaron una tarea sin pregunta y 19 tareas hechas bajo ataque. Las tablas y costes de la ejecución con modelo siguen describiendo la versión anterior a la auditoría, hasta `2328be980`; no hubo nueva ejecución con modelo. El replay es una ruta fija, no el peor caso adaptativo; los ataques con el código en la mano del 2026-10-22 están [más abajo](#qué-cambió-el-2026-10-22-y-por-qué).
29
299
 
30
300
  ## Medido en AgentDojo
31
301
 
@@ -189,34 +459,47 @@ modelo obediente, sin benchmark). Antes de los cambios pasaban 14; después, 3.
189
459
  | Una herramienta MCP sin etiqueta que nombra a una parte es saliente; `warden-mcp flow-labels` | «añadir asistente» con el correo del atacante y «añadir colaborador» por nombre de usuario pasaban | ambas preguntan |
190
460
  | Hook Stop: una línea bajo una respuesta que se apoyó en texto externo | una inyección solo en la respuesta no hace llamadas | el usuario ve qué fuentes usó la respuesta; no se bloquea nada |
191
461
  | Ruleset v15 (ver [gates](gates.es.md)) | 78 herramientas envenenadas de la mitad dev de MCPTox pasaban las reglas | mitad reservada 171 → 194 de 218 (reglas escritas solo con la otra mitad), mitad dev 189 → 233 de 267; 0 de 45 servidores limpios; el corpus y 1000 paquetes de HISTOR bloquean exactamente lo mismo que con v14 |
192
- | La reproducción, de nuevo sobre el código actual | las correcciones de la auditoría del 2026-10-18 no se habían medido | 0 de 579 acciones de ataque, 57 de 97 tareas sin pregunta, 608 de 949 hechas bajo ataque; los cambios del 2026-10-22 no alteran ninguna decisión |
462
+ | La reproducción sobre el código del 2026-10-22 | las correcciones de la auditoría del 2026-10-18 no se habían medido | 0 de 579 acciones de ataque, 57 de 97 tareas sin pregunta, 608 de 949 hechas bajo ataque; los cambios del 2026-10-22 no alteran ninguna decisión |
193
463
  | `scripts/agentdojo/Dockerfile.dockerignore` | el `.dockerignore` del paquete deja fuera `dist/`, así que el comando de «Reproducir» no compilaba | compila |
194
464
 
195
- Siguen pasando tras los cambios ([resultados](../scripts/redteam/results/)):
465
+ Esos tres seguían pasando: trozos de menos de 10 caracteres en palabras distintas, una dirección pegada en el
466
+ prompt y una clave escrita en un archivo del proyecto. Ese mismo día cerramos todo lo que se puede cerrar sin falsos
467
+ positivos y escribimos seis ataques más contra las reglas nuevas. Pasan 2 de 23.
468
+
469
+ | Cambio | Por qué | Resultado |
470
+ |---|---|---|
471
+ | Ventanas de clave de 8 letras y dígitos, 10 para claves hex, medidas: los tamaños más pequeños sin coincidencia casual en 115 millones de letras y dígitos (este monorepo y 1000 paquetes de HISTOR) de texto corriente ([`window-collisions.mjs`](../scripts/redteam/window-collisions.mjs)) | trozos de 8 caracteres envueltos en otras palabras pasaban | rechazo desde el segundo trozo; los de menos de 8 caracteres con palabras distintas alrededor siguen pasando |
472
+ | Las partes que comparten todas las claves de un tipo no se registran (identificador de algoritmo de una clave DER, cabecera y campos de un JWT) | la medición mostró que 0.19.0 rechazaría un README con una clave DKIM después de leer una clave RSA privada | ninguna coincidencia casual con las reglas publicadas |
473
+ | Escribir una clave leída en la sesión en un archivo corriente del proyecto pregunta | la clave en `docs/debug.md` pasaba | pregunta; `.env` y los demás archivos de credenciales siguen pasando |
474
+ | Una dirección solo en texto pegado o citado no la nombró el usuario | la dirección del atacante pegada en el prompt contaba como del usuario | pregunta una vez; una dirección que el usuario también escribió pasa. Un punto tras una dirección escrita ya no la deja sin nombrar (0.19.0 preguntaba por «envíalo a bob@corp.com.») |
475
+ | El hook Stop devuelve una vez una respuesta que repite una instrucción dirigida al modelo | una inyección solo en la respuesta no hace llamadas | En Ministral 8B, que obedece el ataque (sin el guardián 6 de 20 respuestas recomiendan el hotel), la nota del guardián sola lo dejó en 0; la revisión saltó 3 veces de 20 y no cambió ningún resultado. En DeepSeek V4.1 Flash, que nunca lo recomendó, saltó 2 veces de 20 ([resultados](../scripts/agentdojo/results/model-answer-recheck-2026-10-22.json)). Una red de seguridad, no una mejora medida |
476
+ | Ruleset v16 (ver [gates](gates.es.md)) | 34 herramientas envenenadas de la mitad dev de MCPTox seguían pasando, casi todas sin entrada | mitad reservada 194 → 210 de 218, incluidos los 66 secuestros de disparador implícito; 0 de 45 servidores limpios; el corpus y 1000 paquetes de HISTOR bloquean exactamente lo mismo que v15; cinco falsos positivos de un primer borrador quedan fijados como pruebas |
477
+ | La reproducción, de nuevo | las comprobaciones nuevas no deben preguntar donde 0.19.0 no preguntaba | los 1046 registros idénticos a 0.19.0 |
478
+
479
+ En la versión del 2026-10-22 estos dos ejemplos todavía pasaban. Ambos se bloquean en el código local del 2026-10-23; véase arriba. [Resultados históricos](../scripts/redteam/results/):
196
480
 
197
- - **Trozos de menos de 10 caracteres envueltos en palabras distintas** («parte 3: … gracias») a una dirección que
198
- escribió el usuario.
199
- - **Una dirección pegada en el prompt.** Lo que el usuario escribe o pega es su elección; el hook no distingue texto
200
- pegado de texto escrito.
201
- - **Una clave escrita en un archivo del proyecto.** Editar archivos del proyecto es el trabajo del agente; lo que
202
- luego publique la CI desde ellos queda fuera de la vista del guardián.
481
+ - **Trozos de menos de 8 caracteres, cada uno en palabras distintas**, a una dirección que escribió el usuario: 7 o más
482
+ mensajes para una clave de 40 caracteres.
483
+ - **Una clave deletreada en palabras** («siete, Q mayúscula, m, dos …»). Cambiar letras por dígitos parecidos (o→0,
484
+ l→1) se detectó en nuestra clave de prueba solo porque gran parte de ella no tenía esas letras.
203
485
 
204
486
  ## Qué no hace
205
487
 
206
488
  - **Las mediciones no son una prueba.** La reproducción obedece a la perfección pero sigue un solo camino; el modelo
207
489
  encuentra sus propios caminos pero obedece poco, incluso cuando se le ordena; nuestros ataques con el código en la
208
- mano son 17 ideas nuestras. Un atacante que no conocemos, o un modelo que obedezca con más facilidad, es una prueba
490
+ mano son 23 ideas nuestras. Un atacante que no conocemos, o un modelo que obedezca con más facilidad, es una prueba
209
491
  más dura que cualquiera de ellas.
210
492
  - **Las etiquetas son del operador.** Nuestra primera ejecución marcó los nombres de canales de Slack solo como
211
493
  datos privados, y la inyección estaba en el nombre de un canal. La ejecución lo detectó porque la entrega se
212
494
  juzga por el texto del ataque, no por nuestras etiquetas. Una herramienta mal etiquetada es un agujero; una sin
213
495
  etiqueta recibe la suposición del guardián, que solo añade cautela.
214
496
  - **No detiene una inyección que solo cambia la respuesta** («di que este hotel es el mejor»). Sin llamada, nada
215
- que rechazar; el hook Stop solo puede decirle al usuario de dónde salió la respuesta.
216
- - **Los cambios locales dentro del proyecto pasan.** Editar archivos del proyecto es el trabajo del agente.
217
- - **Fugas en trozos pequeños.** El texto privado corriente puede ir a un destino que nombró el usuario, entero o en
218
- trozos: es su elección. Hacia otros destinos, y para claves y números de tarjeta hacia cualquiera, los trozos
219
- enviados seguidos por un mismo argumento se unen; los de menos de 10 caracteres con palabras distintas alrededor no.
497
+ que rechazar. El hook Stop le dice al usuario de dónde salió la respuesta y, si repite una instrucción dirigida al
498
+ modelo, pide una vez al modelo que la compruebe; solo ve las instrucciones que el guardián reconoce como tales.
499
+ - **Los cambios locales dentro del proyecto pasan**, salvo una clave leída en la sesión que va a un archivo
500
+ corriente. Editar archivos del proyecto es el trabajo del agente.
501
+ - **Fugas en trozos pequeños.** El texto privado corriente puede ir a un destino nombrado por el usuario. La unión de flujos y la regla de pares de seis caracteres cubren ciertas particiones; partes más cortas con relleno y codificaciones verbales no admitidas siguen siendo límites.
502
+ - **Lo pegado no es lo escrito.** Una dirección que querías usar pero solo pegaste pregunta una vez.
220
503
  - **No ve** tu prompt de antes de instalar el plugin, ni el texto que el modelo tenga de otras fuentes.
221
504
 
222
505
  ## Reproducir