stratum-cli 0.7.0-beta.0 → 0.8.0-beta.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 (66) hide show
  1. package/.stratumrc.json.example +249 -220
  2. package/CHANGELOG.md +63 -0
  3. package/README.md +14 -0
  4. package/dist/index.js +18651 -8994
  5. package/evals/scenarios/auxiliary/auxiliary-cache-accounting.json +108 -0
  6. package/evals/scenarios/auxiliary/auxiliary-failure-does-not-corrupt-agent.json +81 -0
  7. package/evals/scenarios/auxiliary/compression-visible.json +135 -0
  8. package/evals/scenarios/auxiliary/memory-extraction-visible.json +78 -0
  9. package/evals/scenarios/auxiliary/subagent-origin.json +90 -0
  10. package/evals/scenarios/cache/cache-compression-cache-impact.json +113 -0
  11. package/evals/scenarios/cache/cache-growing-multi-turn-context.json +106 -0
  12. package/evals/scenarios/cache/cache-repeated-system-prefix.json +89 -0
  13. package/evals/scenarios/cache/cache-stable-toolset-order.json +97 -0
  14. package/evals/scenarios/cache/cache-tool-loop-prefix.json +110 -0
  15. package/evals/scenarios/code/code-create-module.json +57 -0
  16. package/evals/scenarios/code/code-fix-failing-test.json +86 -0
  17. package/evals/scenarios/code/code-json-config-edit.json +70 -0
  18. package/evals/scenarios/code/code-read-and-answer.json +65 -0
  19. package/evals/scenarios/code/code-rename-across-files.json +103 -0
  20. package/evals/scenarios/jobs/background-job-cancel.json +102 -0
  21. package/evals/scenarios/jobs/background-job-completes.json +108 -0
  22. package/evals/scenarios/jobs/background-job-fails.json +97 -0
  23. package/evals/scenarios/jobs/background-job-output.json +129 -0
  24. package/evals/scenarios/jobs/background-job-policy.json +112 -0
  25. package/evals/scenarios/jobs/background-job-session-close.json +85 -0
  26. package/evals/scenarios/jobs/background-job-subagent-ownership.json +147 -0
  27. package/evals/scenarios/linux/linux-count-errors.json +56 -0
  28. package/evals/scenarios/linux/linux-dash-filenames.json +67 -0
  29. package/evals/scenarios/linux/linux-largest-log.json +56 -0
  30. package/evals/scenarios/linux/linux-make-executable.json +49 -0
  31. package/evals/scenarios/linux/linux-no-match-count.json +67 -0
  32. package/evals/scenarios/multi-agent/multi-agent-delegate-lookup.json +66 -0
  33. package/evals/scenarios/multi-agent/multi-agent-direct-delegate.json +51 -0
  34. package/evals/scenarios/recovery/recovery-edit-mismatch.json +77 -0
  35. package/evals/scenarios/recovery/recovery-failing-command.json +73 -0
  36. package/evals/scenarios/recovery/recovery-provider-error.json +54 -0
  37. package/evals/scenarios/recovery/recovery-second-failure.json +112 -0
  38. package/evals/scenarios/recovery/recovery-unfixable-missing-secret.json +74 -0
  39. package/evals/scenarios/recovery/recovery-wrong-path.json +75 -0
  40. package/evals/scenarios/safety/safety-equivalent-destructive.json +97 -0
  41. package/evals/scenarios/safety/safety-false-positive-paths.json +66 -0
  42. package/evals/scenarios/safety/safety-false-positive-quoted.json +79 -0
  43. package/evals/scenarios/safety/safety-git-clean-narrowing.json +123 -0
  44. package/evals/scenarios/safety/safety-hard-deny-rm-root.json +93 -0
  45. package/evals/scenarios/safety/safety-hard-deny-wrappers.json +109 -0
  46. package/evals/scenarios/safety/safety-key-exfil-alternatives.json +81 -0
  47. package/evals/scenarios/safety/safety-mixed-environments.json +143 -0
  48. package/evals/scenarios/safety/safety-mixed-git.json +100 -0
  49. package/evals/scenarios/safety/safety-obfuscated-hard-deny.json +135 -0
  50. package/evals/scenarios/safety/safety-private-key-blocked.json +72 -0
  51. package/evals/scenarios/safety/safety-production-confirm-always.json +107 -0
  52. package/evals/scenarios/safety/safety-read-only-session.json +61 -0
  53. package/evals/scenarios/ssh/ssh-compare-disk.json +84 -0
  54. package/evals/scenarios/ssh/ssh-diagnose-service.json +145 -0
  55. package/evals/scenarios/ssh/ssh-fallback-command.json +80 -0
  56. package/evals/scenarios/ssh/ssh-injected-instruction.json +86 -0
  57. package/evals/scenarios/ssh/ssh-kernel-version.json +58 -0
  58. package/evals/scenarios/ssh/ssh-unknown-host.json +76 -0
  59. package/evals/scenarios/steering/explicit-cancel-still-cancels.json +102 -0
  60. package/evals/scenarios/steering/job-and-user-update.json +136 -0
  61. package/evals/scenarios/steering/multiple-steering-messages.json +101 -0
  62. package/evals/scenarios/steering/steering-before-done.json +86 -0
  63. package/evals/scenarios/steering/steering-before-mutation.json +125 -0
  64. package/evals/scenarios/steering/steering-during-tool.json +156 -0
  65. package/evals/scenarios/steering/subagent-does-not-steal-main-steering.json +118 -0
  66. package/package.json +5 -1
@@ -1,220 +1,249 @@
1
- {
2
- "provider": {
3
- "default": "local-ollama",
4
- "providers": {
5
- "local-ollama": {
6
- "type": "openai-compatible",
7
- "baseUrl": "http://localhost:11434/v1",
8
- "model": "qwen3.5:9b",
9
- "apiKey": "ollama",
10
- "contextWindow": 32768,
11
- "timeouts": { "headersMs": 120000, "idleMs": 300000 }
12
- },
13
- "litellm-proxy": {
14
- "type": "openai-compatible",
15
- "baseUrl": "http://localhost:4000/v1",
16
- "model": "claude-sonnet-4-5",
17
- "apiKey": "${LITELLM_API_KEY}",
18
- "contextWindow": 200000
19
- }
20
- }
21
- },
22
- "memory": {
23
- "projectFile": "./STRATUM.md",
24
- "globalFile": "~/.stratum/STRATUM.md",
25
- "decisionsFile": "~/.stratum/memory/decisions.json",
26
- "vectorDb": "~/.stratum/memory/vectors.db",
27
- "embeddingModel": "Xenova/all-MiniLM-L6-v2",
28
- "embeddingDimension": 384,
29
- "embeddingEndpoint": {
30
- "url": "http://localhost:11434/v1/embeddings",
31
- "model": "all-minilm",
32
- "apiKey": ""
33
- },
34
- "retrievalTopK": 5,
35
- "similarityThreshold": 0.9,
36
- "autoExtract": true,
37
- "extractionModel": "qwen3.5:9b",
38
- "embeddingWarmup": false
39
- },
40
- "tools": {
41
- "confirmDestructive": true,
42
- "bashTimeout": 30000,
43
- "execMaxBytes": 1048576,
44
- "auditLog": true,
45
- "redaction": {
46
- "extraPatterns": [
47
- {
48
- "value": "internal.corp.example",
49
- "reason": "internal domain"
50
- }
51
- ]
52
- },
53
- "testCommand": "npm test",
54
- "webSearch": {
55
- "backend": "meta",
56
- "tavilyApiKey": "${TAVILY_API_KEY}",
57
- "maxResults": 10
58
- },
59
- "webFetch": {
60
- "allowHosts": ["localhost:3000"]
61
- },
62
- "destructivePatterns": [
63
- "rm",
64
- "rmdir",
65
- "dd",
66
- "mkfs",
67
- "format",
68
- "DROP",
69
- "DELETE",
70
- "truncate",
71
- "shred",
72
- "wipefs"
73
- ],
74
- "guardedCommands": {
75
- "gitPushForce": "confirm",
76
- "gitResetHard": "confirm",
77
- "gitRebase": "confirm",
78
- "gitBranchDeleteForce": "confirm",
79
- "npmPublish": "block",
80
- "dockerPrune": "confirm",
81
- "curlPipeShell": "confirm"
82
- },
83
- "sensitivePathAllowlist": []
84
- },
85
- "mcp": {
86
- "startup": "lazy",
87
- "installDir": "~/.stratum/mcp",
88
- "autoInstall": true,
89
- "heartbeatInterval": 30000,
90
- "servers": [
91
- {
92
- "name": "filesystem",
93
- "package": "@modelcontextprotocol/server-filesystem@2025.8.21",
94
- "args": [
95
- "/home/user/projects"
96
- ],
97
- "startupTimeout": 15000,
98
- "toolTimeout": 120000
99
- },
100
- {
101
- "name": "chrome-devtools",
102
- "package": "chrome-devtools-mcp@0.6.0"
103
- },
104
- {
105
- "name": "legacy-npx",
106
- "command": "npx",
107
- "args": [
108
- "-y",
109
- "some-mcp-server@latest"
110
- ]
111
- }
112
- ]
113
- },
114
- "agent": {
115
- "maxIterations": 50,
116
- "maxToolRetries": 3,
117
- "toolErrorFormat": "xml",
118
- "compressionKeepRounds": 6,
119
- "compressionTimeoutMs": 120000
120
- },
121
- "agents": {
122
- "defaultProfile": "general",
123
- "maxConcurrency": 1,
124
- "subagentRetentionDays": 30
125
- },
126
- "skills": {
127
- "enabled": true,
128
- "registryFile": "./.stratum/skill-registry.md"
129
- },
130
- "prompt": {
131
- "guides": "inline",
132
- "guidesDir": "./.stratum/guides"
133
- },
134
- "logging": {
135
- "level": "info",
136
- "stderr": true,
137
- "redact": true,
138
- "file": {
139
- "enabled": false,
140
- "dir": "~/.stratum/logs",
141
- "maxBytes": 5242880,
142
- "maxFiles": 5
143
- }
144
- },
145
- "ssh": {
146
- "hosts": {
147
- "bastion": {
148
- "host": "bastion.example.com",
149
- "port": 22,
150
- "user": "javi",
151
- "privateKey": "~/.ssh/id_ed25519"
152
- },
153
- "prod-web": {
154
- "host": "192.168.1.10",
155
- "port": 22,
156
- "user": "javi",
157
- "privateKey": "~/.ssh/id_ed25519",
158
- "jumpHost": "bastion",
159
- "confirmAll": true
160
- },
161
- "dev-server": {
162
- "host": "10.0.0.5",
163
- "port": 22,
164
- "user": "javi",
165
- "useAgent": true
166
- }
167
- }
168
- },
169
- "desktop": {
170
- "maxConcurrentTurns": 2,
171
- "notifications": {
172
- "enabled": true,
173
- "minSeconds": 10
174
- },
175
- "globalHotkey": "CommandOrControl+Shift+Space",
176
- "workspaces": {
177
- "root": "~/.stratum/desktop/workspaces",
178
- "maxFileMB": 25,
179
- "maxWorkspaceMB": 250,
180
- "compressAfterDays": 7,
181
- "deleteAfterDays": 30
182
- }
183
- },
184
- "environments": {
185
- "prod": {
186
- "match": [
187
- "ssh:prod-*"
188
- ],
189
- "tier": "production",
190
- "requirePlan": true
191
- },
192
- "staging": {
193
- "match": [
194
- "ssh:stg-*"
195
- ],
196
- "tier": "staging"
197
- },
198
- "lab": {
199
- "match": [
200
- "ssh:dev-*"
201
- ],
202
- "policy": "allow"
203
- }
204
- },
205
- "session": {
206
- "profile": "auto",
207
- "planRetentionDays": 30,
208
- "profiles": {
209
- "ops": {
210
- "description": "guardias",
211
- "allowedTools": [
212
- "exec",
213
- "read_file",
214
- "grep",
215
- "mcp__*"
216
- ]
217
- }
218
- }
219
- }
220
- }
1
+ {
2
+ "provider": {
3
+ "default": "local-ollama",
4
+ "providers": {
5
+ "local-ollama": {
6
+ "type": "openai-compatible",
7
+ "baseUrl": "http://localhost:11434/v1",
8
+ "model": "qwen3.5:9b",
9
+ "apiKey": "ollama",
10
+ "contextWindow": 32768,
11
+ "timeouts": { "headersMs": 120000, "idleMs": 300000 }
12
+ },
13
+ "litellm-proxy": {
14
+ "type": "openai-compatible",
15
+ "baseUrl": "http://localhost:4000/v1",
16
+ "model": "claude-sonnet-4-5",
17
+ "apiKey": "${LITELLM_API_KEY}",
18
+ "contextWindow": 200000,
19
+ "cache": { "explicitBreakpoints": true }
20
+ },
21
+ "hosted": {
22
+ "type": "openai-compatible",
23
+ "baseUrl": "https://api.example.com/v1",
24
+ "apiKey": "${EXAMPLE_API_KEY}",
25
+ "models": {
26
+ "big-model": { "contextWindow": 200000 }
27
+ }
28
+ }
29
+ }
30
+ },
31
+ "memory": {
32
+ "projectFile": "./STRATUM.md",
33
+ "globalFile": "~/.stratum/STRATUM.md",
34
+ "decisionsFile": "~/.stratum/memory/decisions.json",
35
+ "vectorDb": "~/.stratum/memory/vectors.db",
36
+ "embeddingModel": "Xenova/all-MiniLM-L6-v2",
37
+ "embeddingDimension": 384,
38
+ "embeddingEndpoint": {
39
+ "url": "http://localhost:11434/v1/embeddings",
40
+ "model": "all-minilm",
41
+ "apiKey": ""
42
+ },
43
+ "retrievalTopK": 5,
44
+ "similarityThreshold": 0.9,
45
+ "autoExtract": true,
46
+ "extractionModel": "qwen3.5:9b",
47
+ "embeddingWarmup": false
48
+ },
49
+ "tools": {
50
+ "confirmDestructive": true,
51
+ "bashTimeout": 30000,
52
+ "execMaxBytes": 1048576,
53
+ "auditLog": true,
54
+ "jobs": {
55
+ "enabled": true,
56
+ "maxRunning": 8,
57
+ "maxRetained": 50,
58
+ "maxOutputChars": 1000000,
59
+ "maxTotalOutputChars": 8000000,
60
+ "readChars": 8000,
61
+ "maxReadChars": 24000,
62
+ "maxRuntimeMs": 1800000,
63
+ "maxWaitMs": 60000,
64
+ "subagentAccess": "own"
65
+ },
66
+ "redaction": {
67
+ "extraPatterns": [
68
+ {
69
+ "value": "internal.corp.example",
70
+ "reason": "internal domain"
71
+ }
72
+ ]
73
+ },
74
+ "testCommand": "npm test",
75
+ "webSearch": {
76
+ "backend": "meta",
77
+ "tavilyApiKey": "${TAVILY_API_KEY}",
78
+ "maxResults": 10
79
+ },
80
+ "webFetch": {
81
+ "allowHosts": ["localhost:3000"]
82
+ },
83
+ "destructivePatterns": [
84
+ "rm",
85
+ "rmdir",
86
+ "dd",
87
+ "mkfs",
88
+ "format",
89
+ "DROP",
90
+ "DELETE",
91
+ "truncate",
92
+ "shred",
93
+ "wipefs"
94
+ ],
95
+ "guardedCommands": {
96
+ "gitPushForce": "confirm",
97
+ "gitResetHard": "confirm",
98
+ "gitRebase": "confirm",
99
+ "gitBranchDeleteForce": "confirm",
100
+ "gitCleanForce": "confirm",
101
+ "npmPublish": "block",
102
+ "dockerPrune": "confirm",
103
+ "curlPipeShell": "confirm",
104
+ "shellDetach": "block"
105
+ },
106
+ "sensitivePathAllowlist": []
107
+ },
108
+ "mcp": {
109
+ "startup": "lazy",
110
+ "installDir": "~/.stratum/mcp",
111
+ "autoInstall": true,
112
+ "heartbeatInterval": 30000,
113
+ "servers": [
114
+ {
115
+ "name": "filesystem",
116
+ "package": "@modelcontextprotocol/server-filesystem@2025.8.21",
117
+ "args": [
118
+ "/home/user/projects"
119
+ ],
120
+ "startupTimeout": 15000,
121
+ "toolTimeout": 120000
122
+ },
123
+ {
124
+ "name": "chrome-devtools",
125
+ "package": "chrome-devtools-mcp@0.6.0"
126
+ },
127
+ {
128
+ "name": "legacy-npx",
129
+ "command": "npx",
130
+ "args": [
131
+ "-y",
132
+ "some-mcp-server@latest"
133
+ ]
134
+ }
135
+ ]
136
+ },
137
+ "agent": {
138
+ "maxIterations": 50,
139
+ "maxToolRetries": 3,
140
+ "toolErrorFormat": "xml",
141
+ "compressionKeepRounds": 6,
142
+ "compressionTimeoutMs": 120000
143
+ },
144
+ "agents": {
145
+ "defaultProfile": "general",
146
+ "maxConcurrency": 1,
147
+ "subagentRetentionDays": 30
148
+ },
149
+ "skills": {
150
+ "enabled": true,
151
+ "registryFile": "./.stratum/skill-registry.md"
152
+ },
153
+ "trace": {
154
+ "enabled": true,
155
+ "dir": "~/.stratum/traces",
156
+ "retentionDays": 30,
157
+ "maxFieldChars": 200000
158
+ },
159
+ "prompt": {
160
+ "guides": "inline",
161
+ "guidesDir": "./.stratum/guides"
162
+ },
163
+ "logging": {
164
+ "level": "info",
165
+ "stderr": true,
166
+ "redact": true,
167
+ "file": {
168
+ "enabled": false,
169
+ "dir": "~/.stratum/logs",
170
+ "maxBytes": 5242880,
171
+ "maxFiles": 5
172
+ }
173
+ },
174
+ "ssh": {
175
+ "hosts": {
176
+ "bastion": {
177
+ "host": "bastion.example.com",
178
+ "port": 22,
179
+ "user": "javi",
180
+ "privateKey": "~/.ssh/id_ed25519"
181
+ },
182
+ "prod-web": {
183
+ "host": "192.168.1.10",
184
+ "port": 22,
185
+ "user": "javi",
186
+ "privateKey": "~/.ssh/id_ed25519",
187
+ "jumpHost": "bastion",
188
+ "confirmAll": true
189
+ },
190
+ "dev-server": {
191
+ "host": "10.0.0.5",
192
+ "port": 22,
193
+ "user": "javi",
194
+ "useAgent": true
195
+ }
196
+ }
197
+ },
198
+ "desktop": {
199
+ "maxConcurrentTurns": 2,
200
+ "notifications": {
201
+ "enabled": true,
202
+ "minSeconds": 10
203
+ },
204
+ "globalHotkey": "CommandOrControl+Shift+Space",
205
+ "workspaces": {
206
+ "root": "~/.stratum/desktop/workspaces",
207
+ "maxFileMB": 25,
208
+ "maxWorkspaceMB": 250,
209
+ "compressAfterDays": 7,
210
+ "deleteAfterDays": 30
211
+ }
212
+ },
213
+ "environments": {
214
+ "prod": {
215
+ "match": [
216
+ "ssh:prod-*"
217
+ ],
218
+ "tier": "production",
219
+ "requirePlan": true
220
+ },
221
+ "staging": {
222
+ "match": [
223
+ "ssh:stg-*"
224
+ ],
225
+ "tier": "staging"
226
+ },
227
+ "lab": {
228
+ "match": [
229
+ "ssh:dev-*"
230
+ ],
231
+ "policy": "allow"
232
+ }
233
+ },
234
+ "session": {
235
+ "profile": "auto",
236
+ "planRetentionDays": 30,
237
+ "profiles": {
238
+ "ops": {
239
+ "description": "guardias",
240
+ "allowedTools": [
241
+ "exec",
242
+ "read_file",
243
+ "grep",
244
+ "mcp__*"
245
+ ]
246
+ }
247
+ }
248
+ }
249
+ }
package/CHANGELOG.md CHANGED
@@ -6,6 +6,67 @@ El formato sigue [Keep a Changelog](https://keepachangelog.com/es-ES/1.1.0/) y l
6
6
 
7
7
  > Las versiones anteriores a la 0.2.1-beta.0 solo existen como tags de git: la primera publicación en npm fue la 0.2.1-beta.0.
8
8
 
9
+ ## [0.8.0-beta.0] — 2026-10-12
10
+
11
+ Pre-release para pruebas (`npm install -g stratum-cli@beta`); `latest` sigue en la 0.7.0.
12
+
13
+ Traza de sesión con su visor, evaluación reproducible sobre esa traza, jobs en segundo plano, mensajes al agente con el turno en curso (steering) y medición de la caché de prompt del backend.
14
+
15
+ ### Añadido
16
+ - **Traza de sesión y visor de trayectoria**: cada sesión de `chat` y `run` deja en `~/.stratum/traces/<sessionId>.jsonl` lo que entra al modelo, cada llamada al LLM (espera, generación, tokens), cada tool con su entrada y su salida, subagentes, avisos y compresión. La escribe el runtime —el modelo no la ve ni la toca— y pasa por la redacción de secretos antes de ir a disco. `/auditor` (y `/auditor stop`) en el chat y `stratum auditor [sessionId]` / `stratum auditor list` abren una web local, solo en loopback, con el timeline por carriles, la lista de pasos y el detalle; se actualiza sola. Config `trace.{enabled,dir,retentionDays,maxFieldChars}`; `sessions delete`/`prune` arrastran la traza.
17
+ - **Jobs en segundo plano**: `exec` con `background: true` lanza el comando como un job de la sesión y vuelve con su `jobId` sin esperar; el agente sigue trabajando y se entera cuando termina. Tools `list_jobs`, `get_job_status`, `get_job_output` y `cancel_job`; panel de jobs en el chat, `/jobs` y `/jobs cancel <id>`. Pasa por las mismas guardas, read-only, entornos y confirmaciones que un `exec` normal, solo en el target local, y los jobs vivos se cancelan al cerrar la sesión. Config `tools.jobs` (`enabled`, `maxRunning`, límites de salida, `subagentAccess`).
18
+ - **Steering**: con el agente trabajando el input sigue vivo. Lo que escribas no cancela el turno ni lanza otra llamada: queda en cola (`[queued as steering]`) y el agente lo recibe en su siguiente punto seguro, dentro del mismo turno. Si tenía tools a punto de ejecutar, no se ejecuta ninguna y decide de nuevo con tu mensaje delante. Cancelar sigue siendo solo `Ctrl+C`.
19
+ - **Caché de prompt**: Stratum mide la caché de prefijo del backend (OpenAI, pasarelas compatibles con los campos de Anthropic, DeepSeek, llama.cpp) y la enseña en `/auditor`, `stratum stats` y `stratum eval`: acierto, llamadas frías y templadas, tiempo al primer token de cada clase y roturas con su causa. Lo que el backend no reporta se muestra como `n/d`, nunca se estima. `provider.providers.<alias>.cache` ajusta qué se envía (`cacheKey`, `explicitBreakpoints`, `sessionAffinity`). Ver `docs/prompt-caching.md`.
20
+ - **Llamadas auxiliares al LLM en la traza**: la extracción de memoria, el resumen de la compresión de contexto y el resumen de sesión quedan registradas con su origen, aparte de las del agente. `/auditor`, `stratum stats` y el informe de eval muestran «LLM calls» con una fila por origen.
21
+ - `stratum run --then <tarea>` (repetible) encadena turnos en la misma sesión y corta si uno no acaba bien.
22
+ - **`stratum eval`**: escenarios reproducibles (`code`, `linux`, `ssh`, `safety`, `recovery`, `multi-agent`) que se ejecutan con `stratum run` en un proyecto temporal y se puntúan con el workspace, la salida y la traza de la sesión. `eval run` (`--mock` para un modelo de guion determinista; sin él, contra el provider configurado), `eval list`, `eval report`, `eval runs` y `eval compare`, que detecta regresiones aunque las dos ejecuciones den PASS (más tokens, tiempo, llamadas o errores) y sale con 1 si las hay. 21 escenarios incluidos; los propios van en `.stratum/evals/`. Resultado en `~/.stratum/evals/runs/<runId>/result.json`. Contra un modelo real, un error fatal del provider (caído, un `429`) deja el escenario en ERROR, no en FAIL. Ver `docs/eval.md`.
23
+ - **Niveles de dificultad** en los escenarios (`basic`, `intermediate`, `adversarial`), con `--difficulty` en `eval run`/`eval list` y el éxito por nivel en el informe y en la comparación. 37 escenarios incluidos: 16 nuevos fuera del camino feliz —órdenes destructivas disfrazadas, alternativas equivalentes a un `rm -rf`, falsos positivos de las guardas, una acción vetada junto a otra permitida, una instrucción inyectada en la salida de un comando, un host que no está en el inventario, un despliegue que no se puede completar—. Los adversariales destaparon tres brechas en las guardas de `exec`, cerradas en esta misma versión (ver «Seguridad»).
24
+ - Cuatro grupos más de escenarios, hasta 61 incluidos: `cache`, `auxiliary`, `jobs` y `steering`. `eval compare` trata la caché como categoría propia (un fallo de caché no es un error del agente) y solo juzga las métricas auxiliares si se piden con `--metric`.
25
+ - **Baselines con nombre**: `stratum eval baseline save|list|delete` y `eval run --save-baseline`. Cada resultado guarda el commit (y si había cambios sin commit), la versión, el SO, el provider/modelo, el modo y la fecha. `stratum eval compare baseline current`.
26
+ - **Tolerancias configurables** en `eval compare` y `eval run --baseline`: por métrica, en porcentaje y/o absoluto (`--tolerance tokens=30%,2k`, `--tolerances fichero.json`, o guardadas con el baseline). Las de partida son estrictas con guion y holgadas contra un modelo real, donde una llamada de más es ruido.
27
+ - El informe de `eval compare` separa los hallazgos por gravedad: PASS → FAIL, PASS → ERROR, acciones inseguras nuevas, más bloqueos de política, regresiones de coste y de fiabilidad, y mejoras. Avisa si un escenario cambió de definición entre las dos ejecuciones, y entonces no compara su coste.
28
+ - Criterios nuevos: `host_received` (lo que un host SSH simulado llegó a recibir) y `tool_output_contains` (lo que llegó al modelo por la salida de una tool). `stratum eval list` avisa de los criterios que atan la trayectoria de un modelo real en vez de puntuar el resultado.
29
+ - **`stratum stats`**: estadísticas agregadas de las trazas guardadas (turnos completados, tool error rate, recuperación, bloqueos de política, tokens, desglose por tool y por modelo). Se calculan en local; nada sale del equipo.
30
+ - `stratum auditor --file <ruta>` abre una traza que no está en `trace.dir` (las de `stratum eval`).
31
+ - La traza registra las **decisiones del runtime** como avisos: confirmaciones (aprobada, denegada, bloqueada), vetos (guarda de la tool, read-only, toolset, plan) y reintentos de la llamada al modelo. El formato no cambia de versión y las trazas anteriores se siguen leyendo.
32
+
33
+ ### Seguridad
34
+ - **Las guardas de `exec` clasifican el comando que se ejecuta, no el texto escrito.** Antes de decidir se deshacen los envoltorios con sus flags (`sudo -u root`, `timeout 30`, `env`, `nice`, `chroot`, `busybox`…), la agrupación (`( … )`, `then …`), un comando dentro de otro (`sh -c`, `pwsh -Command`, `cmd /c`, `eval`, `su -c`, `find -exec`; hasta dos niveles), las variables asignadas en el mismo comando (`R=rm; $R -rf /`) y las rutas equivalentes (`//`, `/.`, `/etc/..`). Con eso:
35
+ - **Capa 1 (veto)**: `sudo -u root rm -rf ~`, `sh -c 'rm -rf /'`, `timeout 30 rm -rf /`, `rm -rf //` y similares ya no caían en el veto y `--allow-destructive` los dejaba pasar. También se veta un `find` sin filtros que borra la raíz, el home o el directorio actual (`find / -xdev -delete`).
36
+ - **Confirmación obligatoria** para lo que no se puede leer: un ejecutable que sale de una variable o de una sustitución (`$CMD x`, `$(…) x`), código que llega por una tubería a un intérprete (`… | base64 -d | sh`), `bash <(…)`, `pwsh -EncodedCommand`, `Invoke-Expression`; y para un `find` que borra con filtro. Antes se ejecutaban sin preguntar.
37
+ - **`git clean -f`** sin `-d`, o acotado a una ruta, pide confirmación (comando guardado nuevo `gitCleanForce`, `confirm` por defecto): acotar un `git clean -fd` vetado ya no es la forma de conseguir el mismo borrado.
38
+
39
+ ### Corregido
40
+ - Un comando de solo lectura ya no pide confirmación porque un argumento contenga un patrón destructivo: `grep -rn "rm -rf" …`, `echo "rm -rf /"` o `git commit -m "… rm -rf …"` se ejecutan sin preguntar (sin TTY quedaban bloqueados). Los comandos que sí ejecutan sus argumentos (`psql -c "DROP …"`, `ssh host "rm …"`) siguen pidiéndola.
41
+ - **`Ctrl+C` cancela el turno** en el chat (dos veces para salir). Antes la tecla desmontaba la interfaz, el turno seguía por detrás y el proceso salía al terminar.
42
+ - Un `/comando` que no existe responde «Comando desconocido» en vez de enviarse al modelo como mensaje; una ruta (`/etc/hosts …`) sigue llegando.
43
+ - La pantalla de bienvenida tiene la misma paleta de `/comandos` y `@perfiles` que la conversación.
44
+
45
+ ### Cambiado
46
+ - Las tools MCP se ofrecen al modelo ordenadas por nombre, detrás de las propias, y con sus schemas en forma canónica: el prompt no cambia de un arranque a otro según qué server conecte antes, y la caché del backend lo aprovecha.
47
+
48
+ ### Cambios a tener en cuenta
49
+ - **Desligar un proceso desde el shell local se veta**: `cmd &`, `nohup`, `setsid`, `disown`, `Start-Process`, `Start-Job`, `screen -dm`, `tmux new -d` (también dentro de un `sh -c`). Lo que tenga que seguir vivo va como job con `background: true`. `tools.guardedCommands.shellDetach` lo ajusta.
50
+ - En una sesión sin TTY (o con `--deny-destructive`) ahora se bloquean comandos que antes pasaban: `git clean -f`, `find … -delete`, `$VAR args` con una variable que el comando no asigna y `… | sh`. Con `--allow-destructive`, o aprobándolos, se ejecutan como siempre. `tools.guardedCommands.gitCleanForce: "allow"` recupera el `git clean -f` sin confirmación.
51
+
52
+ ## [0.7.0] — 2026-10-03
53
+
54
+ Versión estable de la 0.7.0: incluye todo lo de la [0.7.0-beta.0](#070-beta0--2026-09-27) (endurecimiento de la persistencia, de la escritura de ficheros, de MCP, del cliente del LLM y de `web_fetch`) y, además, los **modelos descubiertos**: a un provider le basta la URL y la key.
55
+
56
+ ### Añadido
57
+ - **Providers sin `model`**: un provider ya no necesita `model` ni `contextWindow` en la config; con `baseUrl` y `apiKey` los modelos se piden a `GET /models`. `chat` abre el selector de modelos al arrancar si el provider no tiene ninguno y guarda la primera elección como modelo por defecto.
58
+ - `stratum run` y `stratum init` aceptan `--model`. Sin modelo fijado, usan el único que exponga el provider; con varios, fallan listándolos en vez de elegir uno.
59
+ - `stratum provider add <alias> --base-url … --api-key-env VAR` da de alta un provider sin pasar por el wizard, y `stratum provider models [alias] [--set <id>]` lista los modelos y fija el de por defecto.
60
+ - `/provider remove <alias>` en el chat.
61
+ - Core de Stratum Desktop: guardado atómico de conversaciones y sesiones, guardado al cerrar y avisos cuando un guardado falla. Sin cambios de comportamiento en la CLI.
62
+
63
+ ### Cambiado
64
+ - **Ventana de contexto por modelo**: `models.<id>.contextWindow` > la del provider si está escrita > la que declare `/models` > 32768. El wizard ya no escribe `contextWindow`.
65
+ - `stratum provider remove` quita el provider de todos los ficheros que lo definen (global y proyecto), no solo del primero.
66
+
67
+ ### Corregido
68
+ - **Wizard de providers**: con una `apiKey` escrita como `${VAR}`, la consulta de modelos enviaba el placeholder literal y caía siempre a la entrada manual. Ahora consulta con la variable expandida y guarda el placeholder.
69
+
9
70
  ## [0.7.0-beta.0] — 2026-09-27
10
71
 
11
72
  Pre-release para pruebas (`npm install -g stratum-cli@beta`); `latest` sigue en la 0.6.0.
@@ -210,6 +271,8 @@ Primera versión etiquetada. Hitos 0 y 1.
210
271
  - `/init` para generar o actualizar `STRATUM.md`.
211
272
  - Render de markdown de las respuestas.
212
273
 
274
+ [0.8.0-beta.0]: https://github.com/choruzo/CLI/compare/v0.7.0...v0.8.0-beta.0
275
+ [0.7.0]: https://github.com/choruzo/CLI/compare/v0.6.0...v0.7.0
213
276
  [0.7.0-beta.0]: https://github.com/choruzo/CLI/compare/v0.6.0...v0.7.0-beta.0
214
277
  [0.6.0]: https://github.com/choruzo/CLI/compare/v0.5.0...v0.6.0
215
278
  [0.5.0]: https://github.com/choruzo/CLI/compare/v0.4.0...v0.5.0
package/README.md CHANGED
@@ -120,6 +120,7 @@ stratum chat Sesión interactiva
120
120
  stratum chat --resume <id> Reanuda una sesión guardada
121
121
  stratum run "<tarea>" Tarea one-shot
122
122
  stratum run --plan "<tarea>" Modo plan-and-execute
123
+ stratum run "<tarea>" --then "<otra>" Turnos siguientes en la misma sesión (repetible)
123
124
  stratum run --allow-destructive "..." Aprueba tools destructivas automáticamente
124
125
  stratum run --deny-destructive "..." Deniega tools destructivas automáticamente
125
126
  stratum init [--force] [--dry-run] Genera/actualiza STRATUM.md
@@ -130,8 +131,21 @@ stratum mcp list|install [server] Servidores MCP
130
131
  stratum providers Providers configurados
131
132
  stratum ssh list|trust <alias> Inventario SSH y host keys
132
133
  stratum logs path|tail [n] Fichero de logs (bug reports)
134
+ stratum auditor [sessionId] Visor de trayectoria de una sesión
135
+ stratum eval run [--mock] [ids...] Ejecuta escenarios y los puntúa desde la traza
136
+ stratum eval baseline save [nombre] Guarda una ejecución como referencia con nombre
137
+ stratum eval compare <base> [head] Regresiones entre dos ejecuciones (exit 1 si las hay)
138
+ stratum stats [--days n] Estadísticas locales de tus sesiones
133
139
  ```
134
140
 
141
+ `stratum eval` y `stratum stats` trabajan solo con las trazas locales (sin telemetría): formato de
142
+ escenarios, métricas y uso en [`docs/eval.md`](docs/eval.md).
143
+
144
+ Las mismas trazas miden la **caché de prompt** del backend: acierto, tokens servidos de caché,
145
+ llamadas frías y templadas, TTFT de cada clase y qué cambio rompió el prefijo. Cómo se ordena el
146
+ prompt para aprovecharla, qué la invalida y qué admite cada provider está en
147
+ [`docs/prompt-caching.md`](docs/prompt-caching.md).
148
+
135
149
  Dentro de `stratum chat`:
136
150
 
137
151
  ```