oficina-opencode 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +174 -24
- package/dist/core/atomic.d.ts +85 -0
- package/dist/core/atomic.d.ts.map +1 -0
- package/dist/core/atomic.js +186 -0
- package/dist/core/atomic.js.map +1 -0
- package/dist/core/bus.d.ts +76 -56
- package/dist/core/bus.d.ts.map +1 -1
- package/dist/core/bus.js +141 -100
- package/dist/core/bus.js.map +1 -1
- package/dist/core/catalog.d.ts +27 -0
- package/dist/core/catalog.d.ts.map +1 -1
- package/dist/core/catalog.js +41 -6
- package/dist/core/catalog.js.map +1 -1
- package/dist/core/cost.d.ts +275 -0
- package/dist/core/cost.d.ts.map +1 -0
- package/dist/core/cost.js +724 -0
- package/dist/core/cost.js.map +1 -0
- package/dist/core/docsync.d.ts +124 -0
- package/dist/core/docsync.d.ts.map +1 -0
- package/dist/core/docsync.js +191 -0
- package/dist/core/docsync.js.map +1 -0
- package/dist/core/evidence.d.ts +314 -0
- package/dist/core/evidence.d.ts.map +1 -0
- package/dist/core/evidence.js +602 -0
- package/dist/core/evidence.js.map +1 -0
- package/dist/core/fsx.d.ts.map +1 -1
- package/dist/core/fsx.js +7 -1
- package/dist/core/fsx.js.map +1 -1
- package/dist/core/gates.d.ts +9 -2
- package/dist/core/gates.d.ts.map +1 -1
- package/dist/core/gates.js +19 -3
- package/dist/core/gates.js.map +1 -1
- package/dist/core/ids.d.ts +48 -0
- package/dist/core/ids.d.ts.map +1 -0
- package/dist/core/ids.js +87 -0
- package/dist/core/ids.js.map +1 -0
- package/dist/core/leases.d.ts +45 -0
- package/dist/core/leases.d.ts.map +1 -1
- package/dist/core/leases.js +85 -17
- package/dist/core/leases.js.map +1 -1
- package/dist/core/office.d.ts +72 -1
- package/dist/core/office.d.ts.map +1 -1
- package/dist/core/office.js +173 -6
- package/dist/core/office.js.map +1 -1
- package/dist/core/paths.d.ts +11 -1
- package/dist/core/paths.d.ts.map +1 -1
- package/dist/core/paths.js +28 -4
- package/dist/core/paths.js.map +1 -1
- package/dist/core/recovery.d.ts +87 -0
- package/dist/core/recovery.d.ts.map +1 -0
- package/dist/core/recovery.js +325 -0
- package/dist/core/recovery.js.map +1 -0
- package/dist/core/sandbox.d.ts +179 -0
- package/dist/core/sandbox.d.ts.map +1 -0
- package/dist/core/sandbox.js +546 -0
- package/dist/core/sandbox.js.map +1 -0
- package/dist/core/state.d.ts +7 -0
- package/dist/core/state.d.ts.map +1 -1
- package/dist/core/state.js +15 -1
- package/dist/core/state.js.map +1 -1
- package/dist/core/stores.d.ts +65 -0
- package/dist/core/stores.d.ts.map +1 -0
- package/dist/core/stores.js +115 -0
- package/dist/core/stores.js.map +1 -0
- package/dist/core/tripwire.d.ts +87 -0
- package/dist/core/tripwire.d.ts.map +1 -0
- package/dist/core/tripwire.js +203 -0
- package/dist/core/tripwire.js.map +1 -0
- package/dist/core/types.d.ts +33 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +1 -0
- package/dist/core/types.js.map +1 -1
- package/dist/hooks/scopeGuard.d.ts +86 -13
- package/dist/hooks/scopeGuard.d.ts.map +1 -1
- package/dist/hooks/scopeGuard.js +296 -62
- package/dist/hooks/scopeGuard.js.map +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +185 -2
- package/dist/index.js.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +158 -0
- package/dist/tools/index.js.map +1 -1
- package/docs/COMPATIBILITY.md +98 -0
- package/docs/GARANTIAS.md +190 -0
- package/docs/PLAN-V2.1.md +144 -0
- package/docs/QUE-ES.md +15 -0
- package/docs/catalog-facts.json +27 -0
- package/package.json +10 -6
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandbox: resolución y validación de rutas.
|
|
3
|
+
*
|
|
4
|
+
* ## Por qué existe
|
|
5
|
+
*
|
|
6
|
+
* El guard de scope comparaba strings:
|
|
7
|
+
*
|
|
8
|
+
* const relative = path.relative(projectRoot, absolute)
|
|
9
|
+
* if (!matchesScope(relative, mayTouch)) throw ...
|
|
10
|
+
*
|
|
11
|
+
* Eso tiene tres agujeros, y los tres son reales:
|
|
12
|
+
*
|
|
13
|
+
* 1. **Symlinks.** `src/link.ts` -> `../../etc/secrets.ts`. El path relativo
|
|
14
|
+
* es `src/link.ts`, que matchea `src/**`. El guard autoriza. La
|
|
15
|
+
* escritura ocurre afuera. Verificar el path **después** de resolver los
|
|
16
|
+
* symlinks es la diferencia entre "está en el scope" y "termina en el
|
|
17
|
+
* scope".
|
|
18
|
+
*
|
|
19
|
+
* 2. **`..` con `mayTouch: ["**"]`.** `**` matcheaba cualquier string,
|
|
20
|
+
* incluido `../../etc/passwd`. El `**` tiene que significar "todo el
|
|
21
|
+
* proyecto", nunca "todo el filesystem".
|
|
22
|
+
*
|
|
23
|
+
* 3. **Paths absolutos.** `/etc/passwd` o `C:\Windows\...` tienen un
|
|
24
|
+
* relativo que empieza en `..`, y con `**` pasaban.
|
|
25
|
+
*
|
|
26
|
+
* ## La regla
|
|
27
|
+
*
|
|
28
|
+
* Ninguna ruta se autoriza por comparación de strings. Se resuelve primero
|
|
29
|
+
* (absoluta → normalizada → symlinks resueltos → realpath) y recién ahí se
|
|
30
|
+
* decide. Y la decisión es **fail-closed**: ante cualquier duda, se deniega.
|
|
31
|
+
*
|
|
32
|
+
* ## Qué NO promete
|
|
33
|
+
*
|
|
34
|
+
* Un sandbox a nivel de aplicación no puede⬇️ contener un `bash` que el modelo
|
|
35
|
+
* redacta con un interpreter embebido. Eso sí se cubre, pero con otra
|
|
36
|
+
* técnica: se analiza el comando antes de ejecutarlo y se instalan
|
|
37
|
+
* *tripwires* después. Ver `analyzeShellCommand` y el modulo de deteccion.
|
|
38
|
+
*/
|
|
39
|
+
import fs from "node:fs/promises";
|
|
40
|
+
import path from "node:path";
|
|
41
|
+
/* ────────────────────────────────────────────────────────────────────────────
|
|
42
|
+
* Glob a regex
|
|
43
|
+
* ──────────────────────────────────────────────────────────────────────────── */
|
|
44
|
+
/**
|
|
45
|
+
* Glob mínimo a regex. Soporta `*`, `**` y `?`.
|
|
46
|
+
*
|
|
47
|
+
* El orden de los reemplazos importa: el `?` se convierte ANTES de insertar
|
|
48
|
+
* los patrones de `**`, porque el grupo insertado contiene un `?` literal
|
|
49
|
+
* que, si se convierte después, se rompe y deja de matchear.
|
|
50
|
+
*/
|
|
51
|
+
export function globToRegex(glob) {
|
|
52
|
+
const escaped = glob
|
|
53
|
+
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
|
|
54
|
+
// `?` primero: los patrones insertados después contienen `?` literal.
|
|
55
|
+
.replace(/\?/g, ".")
|
|
56
|
+
.replace(/\*\*\//g, "\u0000")
|
|
57
|
+
.replace(/\*\*/g, "\u0001")
|
|
58
|
+
.replace(/\*/g, "[^/]*")
|
|
59
|
+
.replace(/\u0000/g, "(?:.*/)?")
|
|
60
|
+
.replace(/\u0001/g, ".*");
|
|
61
|
+
return new RegExp(`^${escaped}$`);
|
|
62
|
+
}
|
|
63
|
+
/** Normaliza un glob: sin `./` inicial, sin `/` inicial, con `/` como separador. */
|
|
64
|
+
export function normalizeGlob(glob) {
|
|
65
|
+
return glob.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "");
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* ¿El path cae dentro de alguno de los globs?
|
|
69
|
+
*
|
|
70
|
+
* Ojo con `**`: matchea todo lo que se le pase. Por eso `checkScope` nunca
|
|
71
|
+
* llama a esta función antes de verificar la contención en el proyecto.
|
|
72
|
+
*/
|
|
73
|
+
export function matchesScope(relativePath, globs) {
|
|
74
|
+
const normalized = relativePath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
75
|
+
return globs.some((glob) => {
|
|
76
|
+
if (glob === "**" || glob === "***")
|
|
77
|
+
return true;
|
|
78
|
+
const g = normalizeGlob(glob);
|
|
79
|
+
if (g === "**")
|
|
80
|
+
return true;
|
|
81
|
+
return globToRegex(g).test(normalized);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/* ────────────────────────────────────────────────────────────────────────────
|
|
85
|
+
* Contención
|
|
86
|
+
* ──────────────────────────────────────────────────────────────────────────── */
|
|
87
|
+
const IS_WINDOWS = process.platform === "win32";
|
|
88
|
+
/** Normaliza para comparar: en Windows las rutas son case-insensitive. */
|
|
89
|
+
function comparable(p) {
|
|
90
|
+
const resolved = path.resolve(p);
|
|
91
|
+
return IS_WINDOWS ? resolved.toLowerCase() : resolved;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* ¿`target` está dentro de `root`?
|
|
95
|
+
*
|
|
96
|
+
* La comparación es sobre paths ya resueltos, y el separador final se exige
|
|
97
|
+
* explícitamente: `/proj-evil` NO está dentro de `/proj`.
|
|
98
|
+
*/
|
|
99
|
+
export function isInsideRoot(root, target) {
|
|
100
|
+
const r = comparable(root);
|
|
101
|
+
const t = comparable(target);
|
|
102
|
+
if (t === r)
|
|
103
|
+
return true;
|
|
104
|
+
return t.startsWith(r.endsWith(path.sep) ? r : r + path.sep);
|
|
105
|
+
}
|
|
106
|
+
/** Relativo con `/` como separador. */
|
|
107
|
+
export function toPosix(p) {
|
|
108
|
+
return p.replace(/\\/g, "/");
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* `realpath` del ancestro existente mas profundo, con la cola re-enganchada.
|
|
112
|
+
*
|
|
113
|
+
* `fs.realpath` falla si el archivo no existe todavia. Pero un archivo nuevo
|
|
114
|
+
* en un directorio symlinkeado tiene un padre real distinto: hay que
|
|
115
|
+
* resolver el padre y re-enganchar el nombre.
|
|
116
|
+
*/
|
|
117
|
+
async function realpathBestEffort(target) {
|
|
118
|
+
let current = path.resolve(target);
|
|
119
|
+
const tail = [];
|
|
120
|
+
for (;;) {
|
|
121
|
+
try {
|
|
122
|
+
const real = await fs.realpath(current);
|
|
123
|
+
const resolved = tail.length > 0 ? path.join(real, ...tail) : real;
|
|
124
|
+
return { resolved, exists: tail.length === 0 };
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
const parent = path.dirname(current);
|
|
128
|
+
if (parent === current) {
|
|
129
|
+
// Llegamos a la raiz del sistema y no existe: devolvemos el target.
|
|
130
|
+
return { resolved: path.resolve(target), exists: false };
|
|
131
|
+
}
|
|
132
|
+
tail.unshift(path.basename(current));
|
|
133
|
+
current = parent;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Recorre los componentes del camino buscando symlinks.
|
|
139
|
+
*
|
|
140
|
+
* Un symlink que apunta fuera del root es un escape, se detecte o no el
|
|
141
|
+
* archivo final.
|
|
142
|
+
*/
|
|
143
|
+
async function findSymlinks(root, absolute) {
|
|
144
|
+
const symlinks = [];
|
|
145
|
+
let escapes = false;
|
|
146
|
+
if (!isInsideRoot(root, absolute)) {
|
|
147
|
+
return { symlinks, escapes: true };
|
|
148
|
+
}
|
|
149
|
+
const rel = path.relative(path.resolve(root), absolute);
|
|
150
|
+
const parts = rel.split(path.sep).filter((p) => p.length > 0);
|
|
151
|
+
let current = path.resolve(root);
|
|
152
|
+
for (const part of parts) {
|
|
153
|
+
current = path.join(current, part);
|
|
154
|
+
let stat;
|
|
155
|
+
try {
|
|
156
|
+
stat = await fs.lstat(current);
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
// El componente no existe todavia: no hay symlink que seguir.
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
if (!stat.isSymbolicLink())
|
|
163
|
+
continue;
|
|
164
|
+
symlinks.push(current);
|
|
165
|
+
let target;
|
|
166
|
+
try {
|
|
167
|
+
target = await fs.realpath(current);
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
// Symlink roto: no se puede resolver. Se trata como escapatorio
|
|
171
|
+
// conservador, porque un link roto puede apuntar a cualquier lado
|
|
172
|
+
// en cuanto exista el destino.
|
|
173
|
+
escapes = true;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (!isInsideRoot(root, target))
|
|
177
|
+
escapes = true;
|
|
178
|
+
}
|
|
179
|
+
return { symlinks, escapes };
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Resuelve un path contra el root, deshaciendo symlinks.
|
|
183
|
+
*
|
|
184
|
+
* Devuelve toda la información que `checkScope` necesita para decidir, y
|
|
185
|
+
* no decide: la decision vive en un solo lugar.
|
|
186
|
+
*/
|
|
187
|
+
export async function resolveTarget(root, rawPath) {
|
|
188
|
+
const projectRoot = path.resolve(root);
|
|
189
|
+
const absolute = path.resolve(projectRoot, rawPath);
|
|
190
|
+
const { resolved, exists } = await realpathBestEffort(absolute);
|
|
191
|
+
const { symlinks, escapes } = await findSymlinks(projectRoot, absolute);
|
|
192
|
+
const insideLexically = isInsideRoot(projectRoot, absolute);
|
|
193
|
+
const insidePhysically = insideLexically && !escapes && isInsideRoot(projectRoot, resolved);
|
|
194
|
+
return {
|
|
195
|
+
absolute,
|
|
196
|
+
resolved,
|
|
197
|
+
relative: toPosix(path.relative(projectRoot, resolved)),
|
|
198
|
+
lexicalRelative: toPosix(path.relative(projectRoot, absolute)),
|
|
199
|
+
symlinks,
|
|
200
|
+
exists,
|
|
201
|
+
insideLexically,
|
|
202
|
+
insidePhysically,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
/* ────────────────────────────────────────────────────────────────────────────
|
|
206
|
+
* Archivos sensibles
|
|
207
|
+
* ──────────────────────────────────────────────────────────────────────────── */
|
|
208
|
+
/**
|
|
209
|
+
* Archivos que NUNCA se pueden escribir, pase lo que pase.
|
|
210
|
+
*
|
|
211
|
+
* No son negociables por el scope de la card: un `mayTouch: ["**"]` no
|
|
212
|
+
* habilita a tocar el `.npmrc` de alguien ni una clave privada.
|
|
213
|
+
*/
|
|
214
|
+
export const SENSITIVE_PATTERNS = [
|
|
215
|
+
"**/.env",
|
|
216
|
+
"**/.env.*",
|
|
217
|
+
"**/.npmrc",
|
|
218
|
+
"**/.netrc",
|
|
219
|
+
"**/.git/**",
|
|
220
|
+
"**/.git-credentials",
|
|
221
|
+
"**/.htpasswd",
|
|
222
|
+
"**/*.pem",
|
|
223
|
+
"**/*.key",
|
|
224
|
+
"**/*.p12",
|
|
225
|
+
"**/*.pfx",
|
|
226
|
+
"**/*.crt",
|
|
227
|
+
"**/id_rsa*",
|
|
228
|
+
"**/id_ed25519*",
|
|
229
|
+
"**/id_ecdsa*",
|
|
230
|
+
"**/.ssh/**",
|
|
231
|
+
"**/.aws/**",
|
|
232
|
+
"**/.gnupg/**",
|
|
233
|
+
"**/.config/gcloud/**",
|
|
234
|
+
"**/.kube/**",
|
|
235
|
+
"**/credentials.json",
|
|
236
|
+
"**/service-account*.json",
|
|
237
|
+
"**/.docker/config.json",
|
|
238
|
+
"**/.opencode/opencode.json",
|
|
239
|
+
];
|
|
240
|
+
/**
|
|
241
|
+
* Excepciones: archivos que matchean un patrón sensible pero son inocuos.
|
|
242
|
+
*
|
|
243
|
+
* `.env.example` es un template sin secretos; bloquearlo obliga a los
|
|
244
|
+
* agentes a hacer un rodeo por el `.env` real.
|
|
245
|
+
*/
|
|
246
|
+
export const SENSITIVE_EXCEPTIONS = [
|
|
247
|
+
"**/.env.example",
|
|
248
|
+
"**/.env.sample",
|
|
249
|
+
"**/.env.template",
|
|
250
|
+
"**/*.key.example",
|
|
251
|
+
"**/credentials.example.json",
|
|
252
|
+
];
|
|
253
|
+
/** ¿El path relativo cae en un patrón sensible y no es una excepción? */
|
|
254
|
+
export function isSensitive(relativePath) {
|
|
255
|
+
const normalized = normalizeGlob(relativePath);
|
|
256
|
+
if (SENSITIVE_EXCEPTIONS.some((ex) => matchesScope(normalized, [ex])))
|
|
257
|
+
return false;
|
|
258
|
+
return SENSITIVE_PATTERNS.some((pattern) => matchesScope(normalized, [pattern]));
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Rutas que la propia oficina usa como canal de entrega.
|
|
262
|
+
*
|
|
263
|
+
* El agente tiene que poder escribir `handoff.md`, `evidence.md`,
|
|
264
|
+
* `review.md` y `gates.md`: son la evidencia de su trabajo. El scope de la
|
|
265
|
+
* card es `src/**`, asi que sin esta excepcion el agente no podria
|
|
266
|
+
* entregar nunca.
|
|
267
|
+
*
|
|
268
|
+
* OJO: solo los directorios de entrega. `state.json`, `roster.json`, el bus
|
|
269
|
+
* y los leases son del motor — que un agente pueda escribir ahi seria
|
|
270
|
+
* falsear la verdad de la corrida.
|
|
271
|
+
*/
|
|
272
|
+
export const OFFICE_WRITABLE = [
|
|
273
|
+
".oficina/artifacts/**",
|
|
274
|
+
".oficina/fichas/**",
|
|
275
|
+
];
|
|
276
|
+
/**
|
|
277
|
+
* Decide si un path puede escribirse.
|
|
278
|
+
*
|
|
279
|
+
* El orden de las comprobaciones es intencionado: primero lo que invalida
|
|
280
|
+
* la ruta, después lo que la vuelve peligrosa, y recién al final si entra
|
|
281
|
+
* en el scope permitido. Así el error que recibe el modelo dice la verdad
|
|
282
|
+
* mas específica posible.
|
|
283
|
+
*/
|
|
284
|
+
export async function checkScope(root, rawPath, policy, options = {}) {
|
|
285
|
+
const projectRoot = path.resolve(root);
|
|
286
|
+
if (typeof rawPath !== "string" || rawPath.length === 0) {
|
|
287
|
+
return deny("invalid", "Ruta vacía.", projectRoot, "");
|
|
288
|
+
}
|
|
289
|
+
if (rawPath.includes("\0")) {
|
|
290
|
+
return deny("invalid", "La ruta contiene un byte NUL.", projectRoot, "");
|
|
291
|
+
}
|
|
292
|
+
const target = await resolveTarget(projectRoot, rawPath);
|
|
293
|
+
// 1. Fuera del proyecto (por `..` o por path absoluto).
|
|
294
|
+
if (!target.insideLexically) {
|
|
295
|
+
return {
|
|
296
|
+
...deny("outside-project", `\`${rawPath}\` resuelve fuera del proyecto (\`${target.absolute}\`). ` +
|
|
297
|
+
`La oficina solo escribe dentro de \`${projectRoot}\`.`, projectRoot, target.relative),
|
|
298
|
+
resolved: target.resolved,
|
|
299
|
+
symlinks: target.symlinks,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
// 2. Escape por symlink: el path lexical esta adentro, el real no.
|
|
303
|
+
if (!target.insidePhysically) {
|
|
304
|
+
const detalle = target.symlinks.length > 0
|
|
305
|
+
? `El camino atraviesa el symlink \`${toPosix(target.symlinks[0])}\` que apunta afuera del proyecto.`
|
|
306
|
+
: "El camino resuelve a una ubicación fuera del proyecto.";
|
|
307
|
+
return {
|
|
308
|
+
...deny("symlink-escape", `\`${rawPath}\` escapa del proyecto por symlink. ${detalle} ` +
|
|
309
|
+
`Resuelve a \`${target.resolved}\`, que está fuera de \`${projectRoot}\`.`, projectRoot, target.relative),
|
|
310
|
+
resolved: target.resolved,
|
|
311
|
+
symlinks: target.symlinks,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
// 3. Archivos sensibles: no se pueden habilitar con el scope.
|
|
315
|
+
if (!options.ignoreSensitive && isSensitive(target.relative)) {
|
|
316
|
+
return {
|
|
317
|
+
...deny("sensitive", `\`${target.relative}\` es un archivo sensible (credenciales, claves o configuración de git). ` +
|
|
318
|
+
`La oficina nunca escribe esos archivos, pase lo que pase el scope. ` +
|
|
319
|
+
`Si de verdad hace falta, pedile al usuario que lo haga él.`, projectRoot, target.relative),
|
|
320
|
+
resolved: target.resolved,
|
|
321
|
+
symlinks: target.symlinks,
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
// 4. Forbidden de la card.
|
|
325
|
+
if (matchesScope(target.relative, policy.forbidden)) {
|
|
326
|
+
return {
|
|
327
|
+
...deny("forbidden", `\`${target.relative}\` está en el scope forbidden de la card. ` +
|
|
328
|
+
`Forbidden: ${policy.forbidden.join(", ")}. No lo toques. ` +
|
|
329
|
+
`Si realmente hace falta, pedí que se amplíe el scope.`, projectRoot, target.relative),
|
|
330
|
+
resolved: target.resolved,
|
|
331
|
+
symlinks: target.symlinks,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
// 5. Canal de entrega de la oficina: siempre escribible.
|
|
335
|
+
const officeWritable = options.officeWritable ?? OFFICE_WRITABLE;
|
|
336
|
+
if (matchesScope(target.relative, [...officeWritable])) {
|
|
337
|
+
return {
|
|
338
|
+
allowed: true,
|
|
339
|
+
kind: "allowed",
|
|
340
|
+
reason: "ok",
|
|
341
|
+
resolved: target.resolved,
|
|
342
|
+
relative: target.relative,
|
|
343
|
+
symlinks: target.symlinks,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
// 6. mayTouch.
|
|
347
|
+
if (!matchesScope(target.relative, policy.mayTouch)) {
|
|
348
|
+
return {
|
|
349
|
+
...deny("out-of-scope", `\`${target.relative}\` está fuera del scope de la card. ` +
|
|
350
|
+
`Podés tocar: ${policy.mayTouch.join(", ")}. ` +
|
|
351
|
+
`Si el archivo es necesario, pedí por el bus que se amplíe el scope, ` +
|
|
352
|
+
`o pedí ayuda a un peer que lo cubra.`, projectRoot, target.relative),
|
|
353
|
+
resolved: target.resolved,
|
|
354
|
+
symlinks: target.symlinks,
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
return {
|
|
358
|
+
allowed: true,
|
|
359
|
+
kind: "allowed",
|
|
360
|
+
reason: "ok",
|
|
361
|
+
resolved: target.resolved,
|
|
362
|
+
relative: target.relative,
|
|
363
|
+
symlinks: target.symlinks,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
function deny(kind, reason, _root, relative) {
|
|
367
|
+
return {
|
|
368
|
+
allowed: false,
|
|
369
|
+
kind,
|
|
370
|
+
reason,
|
|
371
|
+
resolved: "",
|
|
372
|
+
relative,
|
|
373
|
+
symlinks: [],
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
/** Quita comillas y comillas invertidas de un token de shell. */
|
|
377
|
+
function unquote(token) {
|
|
378
|
+
const t = token.trim();
|
|
379
|
+
if ((t.startsWith('"') && t.endsWith('"') && t.length >= 2) ||
|
|
380
|
+
(t.startsWith("'") && t.endsWith("'") && t.length >= 2)) {
|
|
381
|
+
return t.slice(1, -1);
|
|
382
|
+
}
|
|
383
|
+
return t;
|
|
384
|
+
}
|
|
385
|
+
/** El ultimo token no-vacio de una lista de argumentos. */
|
|
386
|
+
function lastToken(args) {
|
|
387
|
+
const list = Array.isArray(args) ? args : args.split(/\s+/);
|
|
388
|
+
for (let i = list.length - 1; i >= 0; i -= 1) {
|
|
389
|
+
const t = (list[i] ?? "").trim();
|
|
390
|
+
if (!t)
|
|
391
|
+
continue;
|
|
392
|
+
if (t.startsWith("-"))
|
|
393
|
+
continue;
|
|
394
|
+
return unquote(t);
|
|
395
|
+
}
|
|
396
|
+
return undefined;
|
|
397
|
+
}
|
|
398
|
+
/** Todos los tokens no-opcion de una lista de argumentos. */
|
|
399
|
+
function plainTokens(args) {
|
|
400
|
+
const list = Array.isArray(args) ? args : args.split(/\s+/);
|
|
401
|
+
return list
|
|
402
|
+
.filter((t) => t.trim().length > 0 && !t.trim().startsWith("-"))
|
|
403
|
+
.map(unquote);
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Extrae los paths que un comando de shell puede escribir.
|
|
407
|
+
*
|
|
408
|
+
* ## Por qué no es un parser de shell
|
|
409
|
+
*
|
|
410
|
+
* Un parser completo de shell (con here-docs, substituciones, funciones,
|
|
411
|
+
* `eval`, interpreters embebidos) es un proyecto entero y sigue siendo
|
|
412
|
+
* evadible. Lo que hace falta aca es otra cosa: **no dejar pasar en
|
|
413
|
+
* silencio** una escritura que no entendimos.
|
|
414
|
+
*
|
|
415
|
+
* Por eso la funcion devuelve tambien los targets `uncertain`: cuando
|
|
416
|
+
* vemos una escritura pero no sabemos a donde apunta, el comando no se
|
|
417
|
+
* autoriza a ciegas — se deja pasar con el tripwire armed, y el
|
|
418
|
+
* tripwire es el que detecta la escribir fuera del scope despues.
|
|
419
|
+
*/
|
|
420
|
+
export function analyzeShellCommand(command) {
|
|
421
|
+
const out = [];
|
|
422
|
+
if (typeof command !== "string" || command.trim().length === 0)
|
|
423
|
+
return out;
|
|
424
|
+
const add = (target, op, uncertain = false) => {
|
|
425
|
+
if (target === undefined) {
|
|
426
|
+
out.push({ target: "", op, uncertain: true });
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
const clean = unquote(target).trim();
|
|
430
|
+
if (!clean || clean === "-" || clean === "&1")
|
|
431
|
+
return;
|
|
432
|
+
out.push({ target: clean, op, uncertain: false });
|
|
433
|
+
};
|
|
434
|
+
// Redirecciones: > >> 2> &> |>
|
|
435
|
+
const redirect = /(?:^|[^0-9>|&])(?:\d*>>?|&>>?|\|>)\s*("[^"]*"|'[^']*'|[^\s;|&>]+)/g;
|
|
436
|
+
for (const m of command.matchAll(redirect)) {
|
|
437
|
+
add(m[1], "redirect");
|
|
438
|
+
}
|
|
439
|
+
// tee
|
|
440
|
+
for (const m of command.matchAll(/\btee\b((?:\s+-{1,2}\S+)*)\s+("[^"]*"|'[^']*'|[^\s;|&]+)/g)) {
|
|
441
|
+
add(m[2], "tee");
|
|
442
|
+
}
|
|
443
|
+
// cp / mv / install -> destino = ultimo argumento
|
|
444
|
+
for (const m of command.matchAll(/\b(cp|mv|install)\s+((?:[^\s;|&]|\s)+?)(?:\s*[;|&]|$)/g)) {
|
|
445
|
+
const args = plainTokens(m[2] ?? "");
|
|
446
|
+
if (args.length === 0) {
|
|
447
|
+
add(undefined, m[1] ?? "cp", true);
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
// `cp -r a b c` -> el destino es `c`; `cp a b` -> `b`.
|
|
451
|
+
const dest = args.length >= 2 ? args[args.length - 1] : args[0];
|
|
452
|
+
add(dest, m[1] ?? "cp");
|
|
453
|
+
}
|
|
454
|
+
// rm -> todos los argumentos son blancos
|
|
455
|
+
for (const m of command.matchAll(/\brm\s+((?:[^\s;|&]|\s)+?)(?:\s*[;|&]|$)/g)) {
|
|
456
|
+
const args = plainTokens(m[1] ?? "");
|
|
457
|
+
if (args.length === 0)
|
|
458
|
+
add(undefined, "rm", true);
|
|
459
|
+
else
|
|
460
|
+
for (const a of args)
|
|
461
|
+
add(a, "rm");
|
|
462
|
+
}
|
|
463
|
+
// mkdir / touch / truncate / mktemp
|
|
464
|
+
for (const m of command.matchAll(/\b(mkdir|touch|truncate)\s+((?:[^\s;|&]|\s)+?)(?:\s*[;|&]|$)/g)) {
|
|
465
|
+
const args = plainTokens(m[2] ?? "");
|
|
466
|
+
if (args.length === 0)
|
|
467
|
+
add(undefined, m[1] ?? "mkdir", true);
|
|
468
|
+
else
|
|
469
|
+
for (const a of args)
|
|
470
|
+
add(a, m[1] ?? "mkdir");
|
|
471
|
+
}
|
|
472
|
+
// sed -i
|
|
473
|
+
for (const m of command.matchAll(/\bsed\s+((?:[^\s;|&]|\s)+?)(?:\s*[;|&]|$)/g)) {
|
|
474
|
+
const raw = m[1] ?? "";
|
|
475
|
+
if (!/(^|\s)-[a-zA-Z]*i/.test(raw))
|
|
476
|
+
continue;
|
|
477
|
+
const args = plainTokens(raw.replace(/(^|\s)-[a-zA-Z]*i(\S*)/, " "));
|
|
478
|
+
const target = lastToken(args);
|
|
479
|
+
add(target, "sed -i");
|
|
480
|
+
}
|
|
481
|
+
// dd of=
|
|
482
|
+
for (const m of command.matchAll(/\bdd\b[^\n;|&]*?\bof=("[^"]*"|'[^']*'|\S+)/g)) {
|
|
483
|
+
add(m[1], "dd");
|
|
484
|
+
}
|
|
485
|
+
// curl -o / wget -O
|
|
486
|
+
for (const m of command.matchAll(/\b(curl|wget)\b[^\n;|&]*?\s-[oO]\s*("[^"]*"|'[^']*'|\S+)/g)) {
|
|
487
|
+
add(m[2], m[1] ?? "curl");
|
|
488
|
+
}
|
|
489
|
+
// git checkout / restore / clean
|
|
490
|
+
for (const m of command.matchAll(/\bgit\s+(?:checkout|restore|clean)\b([^\n;|&]*)/g)) {
|
|
491
|
+
const rest = (m[1] ?? "").trim();
|
|
492
|
+
const afterDash = /--\s+(.+)$/.exec(rest);
|
|
493
|
+
if (afterDash) {
|
|
494
|
+
for (const p of plainTokens(afterDash[1] ?? ""))
|
|
495
|
+
add(p, "git");
|
|
496
|
+
continue;
|
|
497
|
+
}
|
|
498
|
+
const args = plainTokens(rest);
|
|
499
|
+
if (args.length === 0) {
|
|
500
|
+
add(undefined, "git", true);
|
|
501
|
+
continue;
|
|
502
|
+
}
|
|
503
|
+
for (const p of args)
|
|
504
|
+
add(p, "git");
|
|
505
|
+
}
|
|
506
|
+
// chmod / chown
|
|
507
|
+
for (const m of command.matchAll(/\b(chmod|chown)\s+((?:[^\s;|&]|\s)+?)(?:\s*[;|&]|$)/g)) {
|
|
508
|
+
const target = lastToken(plainTokens(m[2] ?? ""));
|
|
509
|
+
add(target, m[1] ?? "chmod");
|
|
510
|
+
}
|
|
511
|
+
// Interpreters embebidos: python -c / node -e / ruby -e / perl -e.
|
|
512
|
+
// OJO: el flag es `-c` en python y `-e` en node/ruby/perl. Un regex con
|
|
513
|
+
// `-e?` matcheaba el guion suelto y nunca llegaba al codigo.
|
|
514
|
+
const INTERPRETER = /\b(python3?|node|ruby|perl)\b\s+(?:-[ec]\s+|--eval\s+)?("[^"]*"|'[^']*')/g;
|
|
515
|
+
for (const m of command.matchAll(INTERPRETER)) {
|
|
516
|
+
const code = unquote(m[2] ?? "");
|
|
517
|
+
const literales = [...code.matchAll(/["']([^"']{2,})["']/g)].map((x) => x[1] ?? "");
|
|
518
|
+
const paths = literales.filter((s) => s.includes("/") || /\.[A-Za-z0-9]{1,6}$/.test(s));
|
|
519
|
+
if (paths.length === 0) {
|
|
520
|
+
add(undefined, `${m[1] ?? "python"} -c`, true);
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
for (const p of paths)
|
|
524
|
+
add(p, `${m[1] ?? "python"} -c`);
|
|
525
|
+
}
|
|
526
|
+
// Heredoc a un interpreter: el codigo entra por stdin, no se puede ver.
|
|
527
|
+
for (const m of command.matchAll(/\b(python3?|node|ruby|perl)\b\s*<<-?\s*['"]?\w+['"]?/g)) {
|
|
528
|
+
add(undefined, `${m[1] ?? "python"} heredoc`, true);
|
|
529
|
+
}
|
|
530
|
+
// Escrituras que no sabemos interpretar: el verbo esta pero no el target.
|
|
531
|
+
if (/\b(python3?|node|ruby|perl)\b\s+(?:-[ec]\s+|--eval\s+|<<)/.test(command)) {
|
|
532
|
+
const yaCubierto = out.some((c) => c.op.includes("-c") || c.op.includes("heredoc"));
|
|
533
|
+
if (!yaCubierto)
|
|
534
|
+
out.push({ target: "", op: "interpreter", uncertain: true });
|
|
535
|
+
}
|
|
536
|
+
// Deduplicar por (op, target).
|
|
537
|
+
const seen = new Set();
|
|
538
|
+
return out.filter((c) => {
|
|
539
|
+
const key = `${c.op}::${c.target}`;
|
|
540
|
+
if (seen.has(key))
|
|
541
|
+
return false;
|
|
542
|
+
seen.add(key);
|
|
543
|
+
return true;
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
//# sourceMappingURL=sandbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../src/core/sandbox.ts"],"names":[],"mappings":"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"}
|
package/dist/core/state.d.ts
CHANGED
|
@@ -40,6 +40,13 @@ export interface InvariantContext {
|
|
|
40
40
|
limits: Limits;
|
|
41
41
|
/** Veredicto de los gates de la card, si se corrieron. */
|
|
42
42
|
gatesPassed?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Veredicto de la verificación por requisitos.
|
|
45
|
+
*
|
|
46
|
+
* `undefined` significa "no se evaluó" (por ejemplo, en un flujo que no
|
|
47
|
+
* toca el cierre). `false` significa "se evaluó y le faltan requisitos".
|
|
48
|
+
*/
|
|
49
|
+
evidenceComplete?: boolean;
|
|
43
50
|
}
|
|
44
51
|
/**
|
|
45
52
|
* Verifica los invariantes duros.
|
package/dist/core/state.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/core/state.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACzE,OAAO,EAAkB,KAAK,MAAM,EAAE,MAAM,YAAY,CAAA;AAGxD,uEAAuE;AACvE,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,SAAS,EAAE,SAAS,SAAS,EAAE,CAWpE,CAAA;AAED,sCAAsC;AACtC,eAAO,MAAM,oBAAoB,EAAE,SAAS,SAAS,EAAa,CAAA;AAElE,+CAA+C;AAC/C,eAAO,MAAM,oBAAoB,EAAE,SAAS,SAAS,EAA6B,CAAA;AAElF,qBAAa,sBAAuB,SAAQ,KAAK;IAE7C,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,QAAQ,CAAC,EAAE,EAAE,MAAM;gBAFV,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM;CAKtB;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAE9C,QAAQ,CAAC,SAAS,EAAE,MAAM;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM;gBADd,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM;CAK1B;AAED,qCAAqC;AACrC,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,GAAG,OAAO,CAErE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,SAAS,EACb,KAAK,GAAE,OAAO,CAAC,IAAI,CAAM,GACxB,IAAI,CAUN;AAMD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,IAAI,CAAA;IACX,KAAK,EAAE,WAAW,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,0DAA0D;IAC1D,WAAW,CAAC,EAAE,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/core/state.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACzE,OAAO,EAAkB,KAAK,MAAM,EAAE,MAAM,YAAY,CAAA;AAGxD,uEAAuE;AACvE,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,SAAS,EAAE,SAAS,SAAS,EAAE,CAWpE,CAAA;AAED,sCAAsC;AACtC,eAAO,MAAM,oBAAoB,EAAE,SAAS,SAAS,EAAa,CAAA;AAElE,+CAA+C;AAC/C,eAAO,MAAM,oBAAoB,EAAE,SAAS,SAAS,EAA6B,CAAA;AAElF,qBAAa,sBAAuB,SAAQ,KAAK;IAE7C,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,QAAQ,CAAC,EAAE,EAAE,MAAM;gBAFV,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM;CAKtB;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAE9C,QAAQ,CAAC,SAAS,EAAE,MAAM;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM;gBADd,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM;CAK1B;AAED,qCAAqC;AACrC,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,GAAG,OAAO,CAErE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,SAAS,EACb,KAAK,GAAE,OAAO,CAAC,IAAI,CAAM,GACxB,IAAI,CAUN;AAMD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,IAAI,CAAA;IACX,KAAK,EAAE,WAAW,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,0DAA0D;IAC1D,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,gBAAgB,GAAG,IAAI,CA8E5D;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,MAAuB,GAAG,WAAW,GAAG,SAAS,CAkFnG;AAED,8BAA8B;AAC9B,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,GAAE,MAAuB,GAAG,WAAW,CA2BxG;AAED,kCAAkC;AAClC,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,SAAS,EAAE,SAAS,SAAS,EAAE,CAQnE,CAAA;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,GAAG,OAAO,CAExE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,SAAS,GAAG,IAAI,CAMrE;AAMD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,IAAI,GAAG,IAAI,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,cAAc,CA2CjE;AAED,oDAAoD;AACpD,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAGzD;AAED,oCAAoC;AACpC,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,EAAE,CAExD"}
|
package/dist/core/state.js
CHANGED
|
@@ -77,7 +77,7 @@ export function transitionCard(state, cardId, to, patch = {}) {
|
|
|
77
77
|
* y "el modelo no puede romperlo".
|
|
78
78
|
*/
|
|
79
79
|
export function assertInvariants(ctx) {
|
|
80
|
-
const { card, state, limits, gatesPassed } = ctx;
|
|
80
|
+
const { card, state, limits, gatesPassed, evidenceComplete } = ctx;
|
|
81
81
|
if (card) {
|
|
82
82
|
// Invariante duro: el revisor nunca es el owner.
|
|
83
83
|
if (limits.reviewerNeqOwner && card.owner && card.reviewer && card.owner === card.reviewer) {
|
|
@@ -89,6 +89,18 @@ export function assertInvariants(ctx) {
|
|
|
89
89
|
throw new InvariantViolationError("gate-required-before-done", `${card.id}: marcada DONE sin gates en PASS`);
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
|
+
// Invariante duro: sin evidencia de los requisitos obligatorios no hay
|
|
93
|
+
// DONE.
|
|
94
|
+
//
|
|
95
|
+
// Los gates dicen que el codigo esta sano. No dicen que el objetivo del
|
|
96
|
+
// usuario este cumplido. Un proyecto puede compilar, pasar todos sus
|
|
97
|
+
// tests y tener la autenticacion mal porque ningun test contemplaba ese
|
|
98
|
+
// escenario. Por eso los dos veredictos son obligatorios.
|
|
99
|
+
if (limits.evidenceRequiredBeforeDone && card.state === "done") {
|
|
100
|
+
if (evidenceComplete !== true) {
|
|
101
|
+
throw new InvariantViolationError("evidence-required-before-done", `${card.id}: marcada DONE sin evidencia de todos los requisitos obligatorios`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
92
104
|
// Invariante duro: una card no puede reventar el presupuesto de reviews.
|
|
93
105
|
if (card.rejections > limits.maxReviewRejections) {
|
|
94
106
|
throw new InvariantViolationError("max-review-rejections", `${card.id}: ${card.rejections} rechazos exceden el máximo ${limits.maxReviewRejections}`);
|
|
@@ -175,6 +187,7 @@ export function migrateState(raw, limits = DEFAULT_LIMITS) {
|
|
|
175
187
|
createdAt: input.createdAt ?? input.created ?? new Date().toISOString(),
|
|
176
188
|
updatedAt: input.updatedAt ?? input.updated ?? new Date().toISOString(),
|
|
177
189
|
status: (input.status ?? "planning"),
|
|
190
|
+
halt: input.halt ?? null,
|
|
178
191
|
cards,
|
|
179
192
|
order,
|
|
180
193
|
budget: {
|
|
@@ -225,6 +238,7 @@ export function createRun(objective, runId, limits = DEFAULT_LIMITS) {
|
|
|
225
238
|
iteration: 0,
|
|
226
239
|
pendingGates: [],
|
|
227
240
|
sessions: {},
|
|
241
|
+
halt: null,
|
|
228
242
|
};
|
|
229
243
|
}
|
|
230
244
|
/** Transiciones de la corrida. */
|