ratacode 0.2.5

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 (58) hide show
  1. package/CREDITS.md +80 -0
  2. package/LICENSE +21 -0
  3. package/README.md +289 -0
  4. package/apreton/README.md +54 -0
  5. package/apreton/handshake.md +33 -0
  6. package/apreton/headless.md +58 -0
  7. package/apreton/mcp.md +221 -0
  8. package/apreton/navegador.md +58 -0
  9. package/bin/instalacion.js +148 -0
  10. package/bin/ratacode.js +1251 -0
  11. package/fabrica/settings.yaml +247 -0
  12. package/mcp/README.md +253 -0
  13. package/mcp/bin/ratacode-mcp.js +215 -0
  14. package/mcp/lib/actividad.js +49 -0
  15. package/mcp/lib/casa.js +126 -0
  16. package/mcp/lib/claves.js +148 -0
  17. package/mcp/lib/espacios.js +117 -0
  18. package/mcp/lib/http.js +184 -0
  19. package/mcp/lib/lectura.js +210 -0
  20. package/mcp/lib/modelos.js +235 -0
  21. package/mcp/lib/nucleo.js +332 -0
  22. package/mcp/lib/registro.js +21 -0
  23. package/mcp/lib/seguridad.js +283 -0
  24. package/mcp/lib/servidor.js +397 -0
  25. package/mcp/lib/tareas.js +406 -0
  26. package/mcp/package.json +22 -0
  27. package/mcp/tunel.mjs +209 -0
  28. package/modos/arquitecto/agent.cordis.yml +120 -0
  29. package/modos/arquitecto/preset.yml +3 -0
  30. package/modos/capataz/agent.cordis.yml +120 -0
  31. package/modos/capataz/preset.yml +3 -0
  32. package/modos/faro/agent.cordis.yml +120 -0
  33. package/modos/faro/preset.yml +3 -0
  34. package/modos/gepeto/agent.cordis.yml +191 -0
  35. package/modos/gepeto/preset.yml +3 -0
  36. package/modos/hero/agent.cordis.yml +198 -0
  37. package/modos/hero/preset.yml +3 -0
  38. package/modos/modo-rata/agent.cordis.yml +198 -0
  39. package/modos/modo-rata/preset.yml +3 -0
  40. package/modos/nex/agent.cordis.yml +213 -0
  41. package/modos/nex/preset.yml +3 -0
  42. package/modos/nex/skills/cordis-plugin-development/SKILL.md +420 -0
  43. package/modos/nex/skills/editing-cordis-compositions/SKILL.md +165 -0
  44. package/modos/pix/agent.cordis.yml +207 -0
  45. package/modos/pix/preset.yml +3 -0
  46. package/modos/tirita/agent.cordis.yml +130 -0
  47. package/modos/tirita/preset.yml +3 -0
  48. package/package.json +49 -0
  49. package/piel/activos/ratacode-emblema.svg +14 -0
  50. package/piel/activos/ratacode-es.js +1331 -0
  51. package/piel/activos/ratacode-identidad.css +50 -0
  52. package/piel/activos/ratacode-piel.css +217 -0
  53. package/piel/activos/ratacode-piel.js +487 -0
  54. package/piel/activos/ratacode-vida.js +424 -0
  55. package/piel/cordis.patch.yml +6 -0
  56. package/piel/lib/cliente.js +1020 -0
  57. package/piel/lib/index.js +1230 -0
  58. package/piel/package.json +36 -0
@@ -0,0 +1,247 @@
1
+ # RATACODE · Settings de fábrica.
2
+ # Lo copia `bin/ratacode.js` a `<casa>/settings.yaml` la PRIMERA vez que se
3
+ # estrena una casa; si la casa ya existe, no se toca lo que haya puesto el usuario.
4
+ #
5
+ # 0 claves dentro. El usuario pone las suyas la primera vez y quedan en
6
+ # <casa>\.credentials.yaml (la web Ajustes > Models las escribe) o bien
7
+ # exportando la variable de entorno que declara cada proveedor.
8
+ #
9
+ # Proveedores: en Ajustes > Models salen las 8 APIs con clave (B.AI, OpenRouter,
10
+ # DeepSeek, Groq, Google Gemini, NVIDIA NIM, SambaNova y Cloudflare Workers AI).
11
+ # Los DOS LOCALES sin clave y sin coste — Ollama (http://127.0.0.1:11434/v1) y
12
+ # LM Studio (http://127.0.0.1:1234/v1) — siguen declarados AQUÍ (para que sus
13
+ # modelos salgan en el selector de modelos de la caja), pero la piel los esconde
14
+ # de esa lista: tienen su propia pestaña, Ajustes > «Modelos locales» (R18).
15
+ # Los locales no piden clave: no llevan `apiKeyEnv`.
16
+ # B.AI y OpenRouter se declaran aquí (llm-pi-ai) y los cinco gratuitos que
17
+ # recomendó el informe R11 también. Cada baseURL y cada id de modelo está
18
+ # comprobado en su documentación oficial (enlaces en los comentarios).
19
+ #
20
+ # DeepSeek NO se declara a propósito: DSH ya trae su adaptador nativo
21
+ # (@deepseek-ai/dsh-llm-deepseek), que registra la ruta "deepseek-official"
22
+ # contra https://api.deepseek.com y lee DEEPSEEK_API_KEY. Declarar además un
23
+ # "deepseek" en llm-pi-ai.providers no choca por id (el nativo no se llama
24
+ # "deepseek"), pero duplicaría el proveedor en el selector y sustituiría su
25
+ # catálogo real por una lista de modelos escrita a mano. Se usa el nativo.
26
+
27
+ agent-presets:
28
+ default: modo-rata
29
+
30
+ permission:
31
+ defaultPreset: danger-full-access
32
+
33
+ agent-default-model:
34
+ provider: b-ai
35
+ model: "deepseek-v4.1-flash"
36
+
37
+ ui-onboarding:
38
+ welcomeNoticeVersion: 2026-08-13.1
39
+
40
+ llm-pi-ai:
41
+ providers:
42
+ # ── B.AI ──────────────────────────────────────────────────────────
43
+ b-ai:
44
+ displayName: B.AI
45
+ api: openai-completions
46
+ baseURL: https://api.b.ai/v1
47
+ apiKeyEnv: B_AI_API_KEY
48
+ timeoutMs: 120000
49
+ models:
50
+ - id: deepseek-v4.1-flash
51
+ name: deepseek-v4.1-flash
52
+ contextWindow: 1000000
53
+ maxTokens: 32000
54
+ input:
55
+ - text
56
+ - image
57
+ compat:
58
+ maxTokensField: max_tokens
59
+ chatTemplateKwargs: {}
60
+ chatTemplateArgs: {}
61
+ - id: hy3
62
+ name: hy3
63
+ - id: glm-5.3-flash
64
+ name: glm-5.3-flash
65
+ - id: mimo-v2.5
66
+ name: mimo-v2.5
67
+ - id: qwen3.8-flash
68
+ name: qwen3.8-flash
69
+
70
+ # ── OpenRouter ────────────────────────────────────────────────────
71
+ openrouter:
72
+ displayName: OpenRouter
73
+ api: openai-completions
74
+ baseURL: https://openrouter.ai/api/v1
75
+ apiKeyEnv: OPENROUTER_API_KEY
76
+ timeoutMs: 120000
77
+ models:
78
+ - id: upstage/solar-mini4
79
+ name: Solar Mini 4
80
+ contextWindow: 524288
81
+ maxTokens: 131072
82
+ input:
83
+ - text
84
+ - id: qwen/qwen3.8-flash
85
+ name: Qwen3.8 Flash
86
+ contextWindow: 1000000
87
+ maxTokens: 131072
88
+ input:
89
+ - text
90
+ - image
91
+ - id: z-ai/glm-5.3-flash
92
+ name: GLM 5.3 Flash
93
+ contextWindow: 1310720
94
+ maxTokens: 131072
95
+ input:
96
+ - text
97
+ - image
98
+ - id: deepseek/deepseek-v4-flash-vision-exp
99
+ name: DeepSeek V4 Flash Vision
100
+ contextWindow: 1048576
101
+ maxTokens: 131072
102
+ input:
103
+ - text
104
+ - image
105
+
106
+ # ── Groq · gratis 30 RPM / 1.000 RPD / 200K TPD ─────────────────────
107
+ # baseURL: https://console.groq.com/docs/openai
108
+ # modelos: https://console.groq.com/docs/models
109
+ groq:
110
+ displayName: Groq
111
+ api: openai-completions
112
+ baseURL: https://api.groq.com/openai/v1
113
+ apiKeyEnv: GROQ_API_KEY
114
+ timeoutMs: 120000
115
+ models:
116
+ - id: openai/gpt-oss-120b
117
+ name: GPT-OSS 120B (Groq)
118
+ contextWindow: 131072
119
+ maxTokens: 65536
120
+ - id: qwen/qwen3.8-27b
121
+ name: Qwen3.8 27B (Groq, preview)
122
+ contextWindow: 131072
123
+ maxTokens: 16384
124
+
125
+ # ── Google Gemini (AI Studio) · gratis, pero ENTRENA CON TUS DATOS ──
126
+ # baseURL: https://ai.google.dev/gemini-api/docs/openai
127
+ # modelos: https://ai.google.dev/gemini-api/docs/models
128
+ gemini:
129
+ displayName: "Google Gemini (gratis: entrena con tus datos)"
130
+ api: openai-completions
131
+ baseURL: https://generativelanguage.googleapis.com/v1beta/openai/
132
+ apiKeyEnv: GEMINI_API_KEY
133
+ timeoutMs: 120000
134
+ models:
135
+ - id: gemini-3.8-flash
136
+ name: Gemini 3.8 Flash
137
+ contextWindow: 1048576
138
+ maxTokens: 65536
139
+ - id: gemini-3.7-flash
140
+ name: Gemini 3.7 Flash
141
+
142
+ # ── NVIDIA NIM · gratis sólo para prototipar (NO uso comercial) ─────
143
+ # URL y endpoint: https://docs.api.nvidia.com/nim/docs/models
144
+ # («URL: https://integrate.api.nvidia.com · Endpoint: POST /v1/chat/completions»)
145
+ # ids: https://docs.api.nvidia.com/nim/reference/llm-apis
146
+ # No publica ventana de contexto: por eso no se declara contextWindow.
147
+ nvidia-nim:
148
+ displayName: NVIDIA NIM (gratis para prototipar, no comercial)
149
+ api: openai-completions
150
+ baseURL: https://integrate.api.nvidia.com/v1
151
+ apiKeyEnv: NVIDIA_API_KEY
152
+ timeoutMs: 120000
153
+ models:
154
+ - id: deepseek-ai/deepseek-v4-flash
155
+ name: DeepSeek V4 Flash (NIM)
156
+ - id: deepseek-ai/deepseek-v4-pro
157
+ name: DeepSeek V4 Pro (NIM)
158
+
159
+ # ── SambaNova · gratis permanente 20 RPM / 20 RPD / 200K TPD ────────
160
+ # baseURL: https://docs.sambanova.ai/docs/en/get-started/api-keys-urls
161
+ # modelos: https://docs.sambanova.ai/docs/en/models/sambacloud-models
162
+ sambanova:
163
+ displayName: SambaNova
164
+ api: openai-completions
165
+ baseURL: https://api.sambanova.ai/v1
166
+ apiKeyEnv: SAMBANOVA_API_KEY
167
+ timeoutMs: 120000
168
+ models:
169
+ - id: MiniMax-M2.7
170
+ name: MiniMax M2.7 (SambaNova)
171
+ contextWindow: 196608
172
+ - id: DeepSeek-V3.1
173
+ name: DeepSeek V3.1 (SambaNova)
174
+ contextWindow: 131072
175
+
176
+ # ── Cloudflare Workers AI · lleva tu account_id EN LA URL ───────────
177
+ # baseURL: https://developers.cloudflare.com/workers-ai/configuration/open-ai-compatibility/
178
+ # modelos: https://developers.cloudflare.com/workers-ai/models/
179
+ # OJO: {account_id} NO es un hueco que rellene RATACODE. Hay que cambiarlo
180
+ # a mano en Ajustes > Models > este proveedor > «Customized settings > Base URL»,
181
+ # poniendo el id de cuenta de Cloudflare (el token va en su campo API key).
182
+ cloudflare-workers-ai:
183
+ displayName: "Cloudflare Workers AI (cambia {account_id} en «Customized settings > Base URL»)"
184
+ api: openai-completions
185
+ baseURL: https://api.cloudflare.com/client/v4/accounts/{account_id}/ai/v1
186
+ apiKeyEnv: CLOUDFLARE_API_KEY
187
+ timeoutMs: 120000
188
+ models:
189
+ - id: '@cf/openai/gpt-oss-120b'
190
+ name: GPT-OSS 120B (Cloudflare)
191
+ contextWindow: 128000
192
+ - id: '@cf/qwen/qwen2.5-coder-32b-instruct'
193
+ name: Qwen2.5 Coder 32B (Cloudflare)
194
+ contextWindow: 32768
195
+
196
+ # ── Ollama · LOCAL, sin clave y sin coste ───────────────────────────
197
+ # El motor de tu ordenador. No lleva `apiKeyEnv` A PROPÓSITO: sin esa
198
+ # línea la ruta queda sin autenticar (`dsh-llm-pi-ai`: `namesCredential`
199
+ # sólo es true si el perfil nombra una credencial), que es justo lo que
200
+ # hace Ollama por defecto en http://127.0.0.1:11434.
201
+ # Runtime: https://ollama.com/download · modelo: `ollama pull qwen3:8b`
202
+ # Los ids de aquí son los que recomienda el informe R15 (tabla del README:
203
+ # «qué modelo local según tu tarjeta»). Si tu modelo se llama de otra
204
+ # forma, se cambia aquí o en Ajustes > Models.
205
+ # OJO: `qwen2.5-coder:7b` devuelve las herramientas como TEXTO dentro del
206
+ # mensaje y NO funciona como agente (medido en R15): no lo pongas.
207
+ ollama:
208
+ displayName: Ollama (local, sin clave)
209
+ api: openai-completions
210
+ baseURL: http://127.0.0.1:11434/v1
211
+ timeoutMs: 600000
212
+ models:
213
+ - id: "qwen3:8b"
214
+ name: Qwen3 8B (local)
215
+ contextWindow: 40960
216
+ maxTokens: 8192
217
+ compat:
218
+ supportsDeveloperRole: false
219
+ supportsReasoningEffort: false
220
+ - id: "lfm2.5:8b"
221
+ name: LFM2.5 8B (local, 125K)
222
+ contextWindow: 32768
223
+ maxTokens: 8192
224
+ compat:
225
+ supportsDeveloperRole: false
226
+ supportsReasoningEffort: false
227
+
228
+ # ── LM Studio · LOCAL, sin clave ────────────────────────────────────
229
+ # Arranca su servidor ANTES de usar este proveedor: `lms server start`
230
+ # (escucha en 1234). Tampoco lleva `apiKeyEnv`, por lo mismo que Ollama.
231
+ # El id de modelo lo pone LM Studio: míralo con
232
+ # curl http://127.0.0.1:1234/v1/models
233
+ # y escribe ese id AQUÍ o en Ajustes > Models (el que trae puesto es un
234
+ # hueco que hay que cambiar: ningún modelo se llama así).
235
+ lmstudio:
236
+ displayName: LM Studio (local, sin clave)
237
+ api: openai-completions
238
+ baseURL: http://127.0.0.1:1234/v1
239
+ timeoutMs: 600000
240
+ models:
241
+ - id: "<el id que devuelva GET http://127.0.0.1:1234/v1/models>"
242
+ name: LM Studio (cambia el id por el de tu modelo)
243
+ contextWindow: 32768
244
+ maxTokens: 8192
245
+ compat:
246
+ supportsDeveloperRole: false
247
+ supportsReasoningEffort: false
package/mcp/README.md ADDED
@@ -0,0 +1,253 @@
1
+ # RATACODE-MCP · el servidor MCP de RATACODE
2
+
3
+ Deja que **el agente que ya usas** (ChatGPT web, Codex, Claude Code, Rowboat,
4
+ OpenClaw, o cualquier cliente MCP) mande trabajo a **los modelos que ya tienes
5
+ configurados en RATACODE**.
6
+
7
+ ```
8
+ ChatGPT / Codex / Claude Code / Rowboat / OpenClaw
9
+ ↓ MCP
10
+ RATACODE-MCP (esta capa)
11
+ ↓ JSON-RPC stdio (el protocolo que DSH ya publica)
12
+ core DSH (mismo binario, misma casa, mismos proveedores y claves)
13
+ ↓
14
+ proveedor elegido → modelo elegido → resultado
15
+ ```
16
+
17
+ RATACODE **no es otro agente** y no se reescribe nada del núcleo: esto es una
18
+ capa fina encima. El trabajo lo hace el core por medio del perfil `sdk` de DSH,
19
+ que es su entrada oficial para conductores externos.
20
+
21
+ ## Arrancarlo
22
+
23
+ Es un **subcomando del binario principal**, así que no hay que buscar rutas:
24
+
25
+ ```sh
26
+ ratacode mcp # habla MCP por stdio (lo que espera un cliente)
27
+ ratacode mcp --status # estado y sale, sin arrancar el motor
28
+ ratacode mcp --http # además, Streamable HTTP en 127.0.0.1:<puerto>/mcp/<clave>
29
+ ratacode mcp --help # la ayuda del MCP
30
+ ```
31
+
32
+ El puerto por defecto del HTTP es **3778** (se cambia con `--port`). `--http` exige
33
+ `mcp.workspaces` declarado. Con `--nueva-clave` se estrena una clave nueva en vez de
34
+ reutilizar la guardada.
35
+
36
+ Si `ratacode` no está en el PATH (o trabajas desde el repositorio), vale la ruta
37
+ directa: `node <ruta>\bin\ratacode.js mcp`.
38
+
39
+ Por stdio, **stdout es del protocolo**: todo lo que contamos va a stderr.
40
+
41
+ ## Conectar un cliente
42
+
43
+ Cualquier cliente MCP local (stdio):
44
+
45
+ ```json
46
+ {
47
+ "mcpServers": {
48
+ "ratacode": {
49
+ "command": "ratacode",
50
+ "args": [
51
+ "mcp",
52
+ "--home",
53
+ "C:\\Users\\tu-usuario\\.ratacode"
54
+ ]
55
+ }
56
+ }
57
+ }
58
+ ```
59
+
60
+ Las sintaxis exactas por app (Claude Code, Codex, OpenClaw, Rowboat y el genérico)
61
+ están en [`..\apreton\mcp.md`](../apreton/mcp.md).
62
+
63
+ > **Las claves están en UN solo sitio: RATACODE › Ajustes › Models.** Este servidor
64
+ > no mira las variables de entorno del cliente ni abre ficheros de claves: le
65
+ > pregunta al motor si la credencial de esa ruta está puesta en la casa
66
+ > (`<casa>\.credentials.yaml`). Si no lo está, **se para y lo dice**:
67
+ > `Falta la clave de B.AI. Pégala en RATACODE › Ajustes › Models.`
68
+ >
69
+ > Por eso su alta es `ratacode mcp` y nada más: no hay ninguna variable que
70
+ > pasarle, y la clave no queda escrita en la configuración de tu app.
71
+ >
72
+ > En ningún caso la clave sale por MCP: el servidor la usa, no la cuenta.
73
+
74
+ ## ChatGPT web: el túnel (`mcp/tunel.mjs`)
75
+
76
+ ChatGPT web (y cualquier app que hable MCP por URL) necesita HTTP. El túnel es **sólo
77
+ transporte**: expone el MCP local con Cloudflare mientras corre y no cambia nada del servidor.
78
+
79
+ ```sh
80
+ ratacode mcp --http # el MCP por HTTP (local)
81
+ node mcp/tunel.mjs --home <casa> # el túnel, en otra ventana
82
+ ```
83
+
84
+ `tunel.mjs` imprime la **URL pública completa** (dominio + `/mcp/<clave>`) para pegar en el
85
+ cliente. Al abrirlo estrena clave (`--misma-clave` para reutilizar la que había); el servidor
86
+ **adopta la clave nueva sin reiniciar** (relee `<casa>\mcp\http-secret.txt` cada dos segundos) y
87
+ reescribe la URL en `<casa>\mcp\http-url.txt`. A cloudflared se le pasa sólo el origen: la clave
88
+ no viaja en los argumentos de ningún proceso. Si el túnel nombrado `mcp.mod-rat.com` existe en tu
89
+ Cloudflare, lo usa con hostname fijo mediante un fichero de configuración
90
+ (`<casa>\mcp\cloudflared.yml`); si no, un quick tunnel con URL efímera. Ctrl+C lo cierra.
91
+
92
+ Y **ya no hay nada que aceptar**: cada tarea va encerrada en las carpetas de
93
+ `mcp.workspaces` (lee y escribe sólo ahí, sin terminal y sin red), así que la
94
+ bandera `--acepto-lectura-total` de antes es un no-op: se acepta para no romper
95
+ los comandos viejos, y no hace nada (más abajo, «La LECTURA, encerrada»).
96
+
97
+ ## Las herramientas
98
+
99
+ | Herramienta | Para qué |
100
+ |---|---|
101
+ | `list_providers` | Proveedores configurados y si tienen la credencial puesta en la casa (Ajustes → Models). Nunca la clave. |
102
+ | `list_models` | Modelos con proveedor, id, contexto, capacidades, coste declarado y estado. Se llama **antes** de `run_task`. |
103
+ | `run_task` | Lanza el encargo. Sin `esperar_segundos`, devuelve `task_id` al momento y el trabajo sigue en segundo plano. Con `esperar_segundos` (1-600), la llamada **espera y devuelve el resultado completo** en esa misma respuesta. |
104
+ | `get_task_status` | `queued` · `running` · `completed` · `failed` · `cancelled`. |
105
+ | `get_task_result` | Respuesta, modelo, proveedor, tokens, coste (si está declarado), duración y errores. |
106
+ | `cancel_task` | Detiene la tarea de inmediato (se mata el proceso que la ejecuta). |
107
+ | `ratacode_status` | Estado del propio servidor: casa, motor, tareas vivas, clientes y últimas líneas del cuaderno. |
108
+
109
+ `run_task` acepta: `prompt`, `esperar_segundos`, `provider`, `model`,
110
+ `working_directory`, `context`, `max_tokens`, `timeout`, `allow_dangerous`.
111
+
112
+ **Esperar dentro de la llamada (tropiezo 17).** Por stdio, la tarea vive lo que
113
+ vive el cliente: si el cliente se cierra, el servidor se va y el fichero se queda
114
+ a medias. Un cliente de una sola vuelta (`claude -p`, `codex exec`, una llamada
115
+ suelta) no puede volver a preguntar por el estado, así que tiene dos salidas:
116
+ `run_task` con `esperar_segundos` (máximo 600 s) y el resultado en la misma
117
+ respuesta, o no terminar el turno hasta que `get_task_status` diga `completed` o
118
+ `failed`. Si el plazo se agota, la respuesta lo dice (`espera.agotada: true`) y
119
+ queda el `task_id` para seguir preguntando.
120
+
121
+ **Sin routing oculto.** Si no dices modelo, se usa el `agent-default-model` de
122
+ la casa **y se te dice cuál** (`ruta_elegida: "por_defecto"`). El humano o el
123
+ agente deciden el modelo; esta capa no elige por nadie.
124
+
125
+ ## Seguridad
126
+
127
+ - **Espacio cerrado, para LEER y para ESCRIBIR.** Una tarea trabaja sólo dentro de las
128
+ raíces autorizadas (`mcp.workspaces`). Si la casa no las declara, la única raíz
129
+ permitida es el espacio por defecto (o la carpeta desde la que arrancó el servidor).
130
+ Cualquier otra ruta se rechaza, y el agente ve una línea: **«Fuera de la carpeta
131
+ autorizada: `<ruta>`».**
132
+ - **La LECTURA, encerrada con un gancho.** El motor no sabe acotar la lectura (`read-only`
133
+ deniega toda MUTACIÓN, no toda lectura: `dsh-fs-sandbox/lib/types/index.d.ts:7-8`,
134
+ «Reads pass through untouched: every mode permits reading»; y la única lista de raíces
135
+ que existe es la de ESCRITURA, `dsh-sandbox/lib/types/roots.d.ts:28-36`). Se acota con
136
+ el gancho de permiso por herramienta `tools/pre-execute` (`dsh-tools`), que ve cada
137
+ llamada antes de ejecutarse y puede denegarla. El plugin que lo engancha es
138
+ `mcp/lib/lectura.js`, y el MCP lo monta en cada tarea por parche. Normaliza de verdad:
139
+ rutas relativas, `..`, mayúsculas/minúsculas de Windows, enlaces (realpath del trozo que
140
+ existe), UNC y el prefijo `\\?\`.
141
+ - **Sin vías de escape.** En la tarea no hay terminal (`tool-pwsh`, `tool-bash`), ni
142
+ trabajos en segundo plano (`tool-jobs`), ni red (`tool-web`), ni subagentes
143
+ (`tool-subagent*`), ni guiones (`tool-workflow`) ni bucles de agentes (`tool-ralph`).
144
+ Se apagan una a una en el parche de cada tarea, con su motivo escrito al lado.
145
+ - **El sandbox lo impone el core.** Cada tarea arranca en `workspace-write` con su cwd
146
+ como frontera de escritura, y el MCP le pasa al hijo un parche que **fija** el modo.
147
+ Ojo con el detalle que costó una ronda: la casa de fábrica trae
148
+ `permission.defaultPreset: danger-full-access` (es lo que el panel necesita), y ese
149
+ ajuste se aplica AL CREAR la sesión, así que ganaba al parche. Por eso el parche apaga
150
+ la fila `permission` en el hijo del MCP: sin ese servicio, manda el modo del MCP. El
151
+ panel del usuario no se toca: sigue con el preset que él elija.
152
+ - **Lo peligroso se pide dos veces.** `allow_dangerous` en la llamada **no
153
+ basta**: hace falta que el humano haya puesto `mcp.permitir_peligroso: true`.
154
+ Si no, se deniega y se explica. Nunca se queda esperando una aprobación que
155
+ en un servidor MCP no existe: falla cerrado. Y aun con `allow_dangerous`, la
156
+ lectura sigue encerrada en `mcp.workspaces`: lo que se abre es la ESCRITURA.
157
+ - **Claves.** Nunca se devuelven, nunca se escriben en el cuaderno, nunca van
158
+ al agente cliente. El servidor las usa y hace la llamada. Además, el propio
159
+ DSH lava el entorno de los shells de sus agentes
160
+ (`dsh-subprocess`: `/KEY|PASSWORD|SECRET|TOKEN/i`), así que la tarea
161
+ delegada tampoco las ve desde dentro. **De dónde salen:** de la casa —las que
162
+ guardaste con Ajustes → Models—, que es la única fuente; el servidor le
163
+ pregunta al motor si están puestas (con su `describe`: configurada sí/no, sin
164
+ leer ningún valor) y el hijo del motor arranca SIN las variables de claves,
165
+ para que resuelva las de la casa y no una vieja del entorno.
166
+
167
+ ## Ajustes (en `<casa>\settings.yaml`)
168
+
169
+ ```yaml
170
+ mcp:
171
+ workspaces: # obligatorio en modo HTTP (--http/túnel)
172
+ - C:\Users\tu-usuario\Projects\mi-app
173
+ workspace_por_defecto: C:\Users\tu-usuario\Projects\mi-app
174
+ permitir_peligroso: false
175
+ timeout_por_defecto_ms: 1800000 # 30 min: lo que dura una tarea si no dices otra cosa
176
+ timeout_maximo_ms: 3600000 # 1 h: techo, aunque el cliente pida más
177
+ tareas_a_la_vez: 3 # cuántas pueden estar en marcha a la vez
178
+ prompt_max_caracteres: 100000 # tope del encargo
179
+ precios: # opcional: el core no trae precios de texto
180
+ b-ai:
181
+ deepseek-v4.1-flash:
182
+ entrada_por_millon: 0.14
183
+ salida_por_millon: 0.28
184
+ moneda: EUR
185
+ ```
186
+
187
+ El coste sólo aparece si lo declaras aquí. **RATACODE no se inventa precios**:
188
+ si no está, devuelve `null` y lo dice.
189
+
190
+ ## Qué deja escrito
191
+
192
+ ```
193
+ <casa>\mcp\
194
+ estado.json estado del servidor (para el panel y para --status)
195
+ actividad.jsonl una línea por tarea: hora, cliente, modelo, proveedor,
196
+ tarea (recortada), duración, tokens, coste, estado
197
+ marcas.json las marcas del tope de tareas por hora (sobreviven al reinicio)
198
+ http-secret.txt la clave del MCP por HTTP (sólo dueño)
199
+ http-url.txt la URL completa con la clave (sólo dueño)
200
+ cloudflared.yml la config del túnel nombrado, si se usa (sólo dueño)
201
+ tareas\<id>.json el registro completo de cada tarea
202
+ tmp\ parches de política de una tarea (se borran al terminar)
203
+ ```
204
+
205
+ ## Las pruebas
206
+
207
+ Estas pruebas viven en el **repositorio** (`mcp/prueba/` viaja excluido del `.tgz`,
208
+ así que en una instalación desde el paquete no las encontrarás).
209
+
210
+ ```sh
211
+ # el camino entero: list_models → run_task → get_task_result, con PONG
212
+ node mcp/prueba/cliente-prueba.mjs --home <casa> --provider b-ai --model deepseek-v4.1-flash
213
+
214
+ # UNA sola llamada: run_task con esperar_segundos, y el resultado (PONG) en esa
215
+ # misma respuesta, sin get_task_status.
216
+ node mcp/prueba/cliente-espera.mjs --home <casa> --provider b-ai --model deepseek-v4.1-flash --esperar 300
217
+
218
+ # las promesas de seguridad: credencial sin enseñarla, espacio cerrado,
219
+ # allow_dangerous denegado y cancelación de verdad
220
+ node mcp/prueba/prueba-cancelar-y-espacio.mjs --home <casa> --provider b-ai --model deepseek-v4.1-flash
221
+ ```
222
+
223
+ `cliente-prueba.mjs` arranca el servidor **como lo haría un cliente real** (con
224
+ el entorno recortado del SDK), lanza `Responde solamente PONG`, vigila el estado
225
+ y recoge el resultado. Sale con código 0 sólo si la respuesta trae PONG.
226
+
227
+ `cliente-espera.mjs` es la prueba del tropiezo 17: enseña `run_task` tal como lo
228
+ lista el servidor (con `esperar_segundos` y su descripción), lanza el encargo UNA
229
+ vez con `esperar_segundos` y exige que esa misma respuesta traiga el resultado
230
+ completo con PONG. Se le puede pasar un entorno o no: da igual, porque las claves
231
+ salen de la casa.
232
+
233
+ `prueba-cancelar-y-espacio.mjs` comprueba cuatro cosas por escrito: que
234
+ `list_providers` no enseña ninguna clave, que una carpeta fuera de los espacios
235
+ autorizados se rechaza, que `allow_dangerous` sin permiso de la casa se deniega
236
+ y que `cancel_task` deja la tarea en `cancelled` de verdad.
237
+
238
+ > Las dos pruebas escriben su carpeta de trabajo en `mcp/prueba/espacio` salvo
239
+ > que les pases `--espacio`. No tocan la casa salvo el cuaderno (`<casa>\mcp\`).
240
+
241
+ ## Lo que falta (a propósito)
242
+
243
+ - **Del MCP:** nada de transporte. El stdio y el Streamable HTTP están hechos, y el túnel
244
+ (`mcp/tunel.mjs`) también; las siete herramientas y sus topes, en marcha.
245
+ - **Del lanzador:** arranque/parada del MCP desde el panel con `MCP: ON/OFF`, la sección
246
+ **Connections** en Ajustes (puerto, clientes, última actividad) y el botón que explique el MCP
247
+ dentro de la web. Hoy eso se hace por línea de órdenes y se mira en `<casa>\mcp\`.
248
+ - **Y una frontera que ya SÍ está puesta (R25):** la LECTURA de las tareas. DSH no la
249
+ sabe acotar (mira «La LECTURA, encerrada con un gancho»), así que RATACODE la encierra
250
+ con el gancho `tools/pre-execute`: cada herramienta con una ruta fuera de
251
+ `mcp.workspaces` se para y lo dice. Lo que queda fuera de nuestras manos es lo que DSH
252
+ no exponga por una herramienta (no hay ninguna: sin terminal, sin red y sin subagentes,
253
+ no hay puerta). Si algún día DSH trae raíces de lectura, esto se aprieta todavía más.