oficina-opencode 0.0.0-stage → 2.0.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 (117) hide show
  1. package/DESIGN.md +145 -0
  2. package/LICENSE +21 -0
  3. package/README.md +670 -2
  4. package/agents/oficina-architect.md +72 -0
  5. package/agents/oficina-commander.md +108 -0
  6. package/agents/oficina-critico.md +148 -0
  7. package/agents/oficina-director.md +160 -0
  8. package/agents/oficina-guardian.md +128 -0
  9. package/agents/oficina-integrator.md +98 -0
  10. package/agents/oficina-verificador.md +88 -0
  11. package/commands/oficina.md +36 -0
  12. package/dist/cli.d.ts +12 -0
  13. package/dist/cli.d.ts.map +1 -0
  14. package/dist/cli.js +394 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/core/briefing.d.ts +115 -0
  17. package/dist/core/briefing.d.ts.map +1 -0
  18. package/dist/core/briefing.js +410 -0
  19. package/dist/core/briefing.js.map +1 -0
  20. package/dist/core/bus.d.ts +132 -0
  21. package/dist/core/bus.d.ts.map +1 -0
  22. package/dist/core/bus.js +250 -0
  23. package/dist/core/bus.js.map +1 -0
  24. package/dist/core/catalog.d.ts +303 -0
  25. package/dist/core/catalog.d.ts.map +1 -0
  26. package/dist/core/catalog.js +816 -0
  27. package/dist/core/catalog.js.map +1 -0
  28. package/dist/core/dispatch.d.ts +110 -0
  29. package/dist/core/dispatch.d.ts.map +1 -0
  30. package/dist/core/dispatch.js +301 -0
  31. package/dist/core/dispatch.js.map +1 -0
  32. package/dist/core/factory.d.ts +74 -0
  33. package/dist/core/factory.d.ts.map +1 -0
  34. package/dist/core/factory.js +243 -0
  35. package/dist/core/factory.js.map +1 -0
  36. package/dist/core/fsx.d.ts +39 -0
  37. package/dist/core/fsx.d.ts.map +1 -0
  38. package/dist/core/fsx.js +113 -0
  39. package/dist/core/fsx.js.map +1 -0
  40. package/dist/core/gates.d.ts +97 -0
  41. package/dist/core/gates.d.ts.map +1 -0
  42. package/dist/core/gates.js +467 -0
  43. package/dist/core/gates.js.map +1 -0
  44. package/dist/core/leases.d.ts +46 -0
  45. package/dist/core/leases.d.ts.map +1 -0
  46. package/dist/core/leases.js +94 -0
  47. package/dist/core/leases.js.map +1 -0
  48. package/dist/core/log.d.ts +30 -0
  49. package/dist/core/log.d.ts.map +1 -0
  50. package/dist/core/log.js +43 -0
  51. package/dist/core/log.js.map +1 -0
  52. package/dist/core/mesa.d.ts +82 -0
  53. package/dist/core/mesa.d.ts.map +1 -0
  54. package/dist/core/mesa.js +178 -0
  55. package/dist/core/mesa.js.map +1 -0
  56. package/dist/core/office.d.ts +208 -0
  57. package/dist/core/office.d.ts.map +1 -0
  58. package/dist/core/office.js +618 -0
  59. package/dist/core/office.js.map +1 -0
  60. package/dist/core/paths.d.ts +67 -0
  61. package/dist/core/paths.d.ts.map +1 -0
  62. package/dist/core/paths.js +136 -0
  63. package/dist/core/paths.js.map +1 -0
  64. package/dist/core/retry.d.ts +41 -0
  65. package/dist/core/retry.d.ts.map +1 -0
  66. package/dist/core/retry.js +86 -0
  67. package/dist/core/retry.js.map +1 -0
  68. package/dist/core/roster.d.ts +47 -0
  69. package/dist/core/roster.d.ts.map +1 -0
  70. package/dist/core/roster.js +146 -0
  71. package/dist/core/roster.js.map +1 -0
  72. package/dist/core/router.d.ts +67 -0
  73. package/dist/core/router.d.ts.map +1 -0
  74. package/dist/core/router.js +186 -0
  75. package/dist/core/router.js.map +1 -0
  76. package/dist/core/state.d.ts +89 -0
  77. package/dist/core/state.d.ts.map +1 -0
  78. package/dist/core/state.js +303 -0
  79. package/dist/core/state.js.map +1 -0
  80. package/dist/core/types.d.ts +333 -0
  81. package/dist/core/types.d.ts.map +1 -0
  82. package/dist/core/types.js +27 -0
  83. package/dist/core/types.js.map +1 -0
  84. package/dist/hooks/compacting.d.ts +18 -0
  85. package/dist/hooks/compacting.d.ts.map +1 -0
  86. package/dist/hooks/compacting.js +72 -0
  87. package/dist/hooks/compacting.js.map +1 -0
  88. package/dist/hooks/scopeGuard.d.ts +39 -0
  89. package/dist/hooks/scopeGuard.d.ts.map +1 -0
  90. package/dist/hooks/scopeGuard.js +114 -0
  91. package/dist/hooks/scopeGuard.js.map +1 -0
  92. package/dist/hooks/sessionIdle.d.ts +35 -0
  93. package/dist/hooks/sessionIdle.d.ts.map +1 -0
  94. package/dist/hooks/sessionIdle.js +110 -0
  95. package/dist/hooks/sessionIdle.js.map +1 -0
  96. package/dist/hooks/systemTransform.d.ts +18 -0
  97. package/dist/hooks/systemTransform.d.ts.map +1 -0
  98. package/dist/hooks/systemTransform.js +63 -0
  99. package/dist/hooks/systemTransform.js.map +1 -0
  100. package/dist/index.d.ts +36 -0
  101. package/dist/index.d.ts.map +1 -0
  102. package/dist/index.js +103 -0
  103. package/dist/index.js.map +1 -0
  104. package/dist/tools/index.d.ts +22 -0
  105. package/dist/tools/index.d.ts.map +1 -0
  106. package/dist/tools/index.js +854 -0
  107. package/dist/tools/index.js.map +1 -0
  108. package/docs/ARCHITECTURE.md +258 -0
  109. package/docs/QUE-ES.md +337 -0
  110. package/examples/opencode.json +7 -0
  111. package/package.json +81 -4
  112. package/skills/oficina/README.md +120 -0
  113. package/skills/oficina/SKILL.md +251 -0
  114. package/skills/oficina/reference/mesh.md +345 -0
  115. package/skills/oficina/reference/protocol.md +342 -0
  116. package/skills/oficina/reference/roles.md +171 -0
  117. package/skills/oficina/reference/templates.md +278 -0
@@ -0,0 +1,467 @@
1
+ /**
2
+ * Gates: verificación ejecutable, no opinada.
3
+ *
4
+ * Un gate que dice "debería compilar" no es un gate. Acá se corren comandos
5
+ * de verdad, se captura la salida real y se decide por exit code. La salida
6
+ * se pega en el artefacto para que un humano pueda auditarla.
7
+ *
8
+ * Las 6 fases vienen de la skill `verification-loop`:
9
+ * build → typecheck → lint → test → security grep → diff review.
10
+ *
11
+ * Reglas de honestidad (no negociables):
12
+ * - Nunca PASS en una fase que no se corrió.
13
+ * - Nunca output inventado.
14
+ * - N/A siempre con motivo explícito.
15
+ * - Una fase aplicable en FAIL = gate FAIL. No se promedia.
16
+ */
17
+ import path from "node:path";
18
+ import { execFile } from "node:child_process";
19
+ import fs from "node:fs/promises";
20
+ import { nowIso } from "./fsx.js";
21
+ /** Runner real por defecto. */
22
+ export const defaultRunner = (command, args, cwd) => new Promise((resolve) => {
23
+ execFile(command, args, { cwd, timeout: 10 * 60_000, maxBuffer: 8 * 1024 * 1024, shell: false }, (err, stdout, stderr) => {
24
+ const code = err && typeof err.code === "number"
25
+ ? err.code
26
+ : err
27
+ ? 1
28
+ : 0;
29
+ resolve({ code, stdout: stdout ?? "", stderr: stderr ?? "" });
30
+ });
31
+ });
32
+ /** Detecta el tooling del proyecto leyendo sus manifiestos. */
33
+ export async function detectTooling(projectRoot) {
34
+ const detected = [];
35
+ let packageManager = null;
36
+ let hasBuild = false;
37
+ let hasTypecheck = false;
38
+ let hasLint = false;
39
+ let hasTest = false;
40
+ const pkgRaw = await readIfExists(path.join(projectRoot, "package.json"));
41
+ if (pkgRaw) {
42
+ detected.push("package.json");
43
+ const pkg = safeParse(pkgRaw);
44
+ const scripts = pkg?.scripts ?? {};
45
+ hasBuild = "build" in scripts;
46
+ hasTypecheck = "typecheck" in scripts || "type-check" in scripts || "tsc" in scripts;
47
+ hasLint = "lint" in scripts;
48
+ hasTest = "test" in scripts;
49
+ const pmField = pkg?.packageManager ?? "";
50
+ if (pmField.startsWith("pnpm"))
51
+ packageManager = "pnpm";
52
+ else if (pmField.startsWith("yarn"))
53
+ packageManager = "yarn";
54
+ else if (pmField.startsWith("bun"))
55
+ packageManager = "bun";
56
+ else
57
+ packageManager = "npm";
58
+ }
59
+ if (await existsFile(path.join(projectRoot, "pnpm-lock.yaml"))) {
60
+ packageManager = "pnpm";
61
+ detected.push("pnpm-lock.yaml");
62
+ }
63
+ else if (await existsFile(path.join(projectRoot, "yarn.lock"))) {
64
+ packageManager = "yarn";
65
+ detected.push("yarn.lock");
66
+ }
67
+ else if (await existsFile(path.join(projectRoot, "bun.lockb"))) {
68
+ packageManager = "bun";
69
+ detected.push("bun.lockb");
70
+ }
71
+ const isPython = (await existsFile(path.join(projectRoot, "pyproject.toml"))) ||
72
+ (await existsFile(path.join(projectRoot, "requirements.txt"))) ||
73
+ (await existsFile(path.join(projectRoot, "setup.py")));
74
+ if (isPython)
75
+ detected.push("python");
76
+ const isGo = await existsFile(path.join(projectRoot, "go.mod"));
77
+ if (isGo)
78
+ detected.push("go.mod");
79
+ const isRust = await existsFile(path.join(projectRoot, "Cargo.toml"));
80
+ if (isRust)
81
+ detected.push("Cargo.toml");
82
+ const hasGit = await existsFile(path.join(projectRoot, ".git"));
83
+ if (hasGit)
84
+ detected.push(".git");
85
+ return {
86
+ packageManager,
87
+ hasBuild,
88
+ hasTypecheck,
89
+ hasLint,
90
+ hasTest,
91
+ isPython,
92
+ isGo,
93
+ isRust,
94
+ hasGit,
95
+ detected,
96
+ };
97
+ }
98
+ async function readIfExists(file) {
99
+ try {
100
+ return await fs.readFile(file, "utf8");
101
+ }
102
+ catch {
103
+ return undefined;
104
+ }
105
+ }
106
+ async function existsFile(file) {
107
+ try {
108
+ await fs.stat(file);
109
+ return true;
110
+ }
111
+ catch {
112
+ return false;
113
+ }
114
+ }
115
+ function safeParse(raw) {
116
+ try {
117
+ return JSON.parse(raw);
118
+ }
119
+ catch {
120
+ return undefined;
121
+ }
122
+ }
123
+ /** Resultado de una fase que no aplica. */
124
+ function na(phase, reason) {
125
+ return { phase, command: null, outcome: "na", exitCode: null, output: "", detail: reason, reason };
126
+ }
127
+ /** Ejecuta una fase y clasifica el resultado. */
128
+ export async function runPhase(phase, command, args, cwd, runner = defaultRunner, timeoutNote) {
129
+ const started = Date.now();
130
+ const { code, stdout, stderr } = await runner(command, args, cwd);
131
+ const duration = Date.now() - started;
132
+ const output = truncate(`${stdout}\n${stderr}`.trim(), 12_000);
133
+ const outcome = code === 0 ? "pass" : "fail";
134
+ return {
135
+ phase,
136
+ command: `${command} ${args.join(" ")}`.trim(),
137
+ outcome,
138
+ exitCode: code,
139
+ output,
140
+ detail: `${outcome === "pass" ? "exit 0" : `exit ${code}`} en ${duration}ms${timeoutNote ? ` — ${timeoutNote}` : ""}`,
141
+ ...(outcome === "fail" ? { reason: `La fase ${phase} falló con exit code ${code}` } : {}),
142
+ };
143
+ }
144
+ function truncate(text, max) {
145
+ if (text.length <= max)
146
+ return text;
147
+ return `${text.slice(0, max)}\n… [salida truncada: ${text.length - max} caracteres más]`;
148
+ }
149
+ /** Ejecuta las 6 fases técnicas. */
150
+ export async function runTechnicalGates(projectRoot, tooling, runner = defaultRunner, scope = []) {
151
+ const results = [];
152
+ const pm = tooling.packageManager;
153
+ // 1. Build
154
+ if (tooling.hasBuild && pm) {
155
+ results.push(await runPhase("build", pm, ["run", "build"], projectRoot, runner));
156
+ }
157
+ else {
158
+ results.push(na("build", "el proyecto no define script `build`"));
159
+ }
160
+ // 2. Type check
161
+ if (tooling.hasTypecheck && pm) {
162
+ results.push(await runPhase("typecheck", pm, ["run", "typecheck"], projectRoot, runner));
163
+ }
164
+ else if (tooling.isPython) {
165
+ results.push(await runPhase("typecheck", "python", ["-m", "mypy", "."], projectRoot, runner));
166
+ }
167
+ else if (tooling.isGo) {
168
+ results.push(await runPhase("typecheck", "go", ["vet", "./..."], projectRoot, runner));
169
+ }
170
+ else if (tooling.isRust) {
171
+ results.push(await runPhase("typecheck", "cargo", ["check"], projectRoot, runner));
172
+ }
173
+ else {
174
+ results.push(na("typecheck", "el proyecto no tiene type checker configurado"));
175
+ }
176
+ // 3. Lint
177
+ if (tooling.hasLint && pm) {
178
+ results.push(await runPhase("lint", pm, ["run", "lint"], projectRoot, runner));
179
+ }
180
+ else if (tooling.isPython) {
181
+ results.push(await runPhase("lint", "python", ["-m", "ruff", "check", "."], projectRoot, runner));
182
+ }
183
+ else if (tooling.isRust) {
184
+ results.push(await runPhase("lint", "cargo", ["clippy"], projectRoot, runner));
185
+ }
186
+ else {
187
+ results.push(na("lint", "el proyecto no define script `lint`"));
188
+ }
189
+ // 4. Tests
190
+ if (tooling.hasTest && pm) {
191
+ results.push(await runPhase("test", pm, ["run", "test"], projectRoot, runner));
192
+ }
193
+ else if (tooling.isPython) {
194
+ results.push(await runPhase("test", "python", ["-m", "pytest", "-q"], projectRoot, runner));
195
+ }
196
+ else if (tooling.isGo) {
197
+ results.push(await runPhase("test", "go", ["test", "./..."], projectRoot, runner));
198
+ }
199
+ else if (tooling.isRust) {
200
+ results.push(await runPhase("test", "cargo", ["test"], projectRoot, runner));
201
+ }
202
+ else {
203
+ results.push(na("test", "el proyecto no define script `test`"));
204
+ }
205
+ // 5. Security grep — escaneo en código, portable a cualquier SO.
206
+ const hits = await scanForSecrets(projectRoot, scope);
207
+ if (hits.length > 0) {
208
+ results.push({
209
+ phase: "secrecy",
210
+ command: "scan:secretos",
211
+ outcome: "fail",
212
+ exitCode: 1,
213
+ output: hits.map((h) => `${h.file}:${h.line} ${h.label} ${h.redacted}`).join("\n"),
214
+ detail: `${hits.length} secreto(s) detectado(s)`,
215
+ reason: "Se detectaron secretos en el scope de la card",
216
+ });
217
+ }
218
+ else {
219
+ results.push({
220
+ phase: "secrecy",
221
+ command: "scan:secretos",
222
+ outcome: "pass",
223
+ exitCode: 0,
224
+ output: "0 hallazgos",
225
+ detail: "sin secretos en el scope",
226
+ });
227
+ }
228
+ // 6. Diff review
229
+ if (tooling.hasGit) {
230
+ results.push(await runPhase("diff", "git", ["diff", "--stat", "HEAD"], projectRoot, runner));
231
+ }
232
+ else {
233
+ results.push(na("diff", "el proyecto no está bajo control de versiones (no hay .git)"));
234
+ }
235
+ return results;
236
+ }
237
+ /**
238
+ * Patrones de detección de secretos.
239
+ *
240
+ * Se aplican EN CÓDIGO, no shelling out a `grep`.
241
+ *
242
+ * Razón: `grep` no existe en Windows (viene con Git Bash, no con el sistema),
243
+ * y un gate de seguridad que depende de una tool opcional del SO es un gate
244
+ * que falla en Windows por razones equivocadas. Además, escanear en código
245
+ * permite manejar rutas con espacios, filtrar binarios y controlar el ruido.
246
+ */
247
+ export const SECRET_PATTERNS = [
248
+ {
249
+ label: "credenciales asignadas",
250
+ pattern: /(api[_-]?key|secret|password|passwd|token|access[_-]?key)\s*[:=]\s*["'][^"'\s]{8,}["']/gi,
251
+ },
252
+ {
253
+ label: "claves de cloud",
254
+ pattern: /AKIA[0-9A-Z]{16}|-----BEGIN [A-Z ]*PRIVATE KEY-----/g,
255
+ },
256
+ {
257
+ label: "tokens de provider",
258
+ pattern: /\b(sk|pk|rk)_[a-z]{1,8}_[A-Za-z0-9]{16,}\b/g,
259
+ },
260
+ ];
261
+ /** Extensiones que se escanean. El código compilado no aporta. */
262
+ const SCANNABLE_EXTENSIONS = new Set([
263
+ ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", ".rb", ".go", ".rs",
264
+ ".java", ".kt", ".php", ".cs", ".json", ".yaml", ".yml", ".toml", ".env",
265
+ ".sh", ".bash", ".zsh", ".ps1", ".md", ".txt", ".sql", ".graphql",
266
+ ]);
267
+ /** Archivos que nunca se escanean (ruido o falsos positivos conocidos). */
268
+ const SCAN_EXCLUDES = new Set([
269
+ "node_modules", ".git", "dist", "build", ".next", "coverage", ".oficina",
270
+ "vendor", "__pycache__", ".venv", "venv", "target", ".cache",
271
+ ]);
272
+ /** Redacta un valor: solo se ven los primeros y últimos 4 caracteres. */
273
+ export function redact(value) {
274
+ if (value.length <= 10)
275
+ return "[REDACTED]";
276
+ return `${value.slice(0, 4)}${"*".repeat(Math.min(value.length - 8, 20))}${value.slice(-4)}`;
277
+ }
278
+ /**
279
+ * Escanea un directorio en busca de secretos.
280
+ *
281
+ * Devuelve los hits con el valor redactado. El valor crudo no sale de acá:
282
+ * un log de gate que imprime la credencial completa la acaba de filtrar.
283
+ */
284
+ export async function scanForSecrets(root, scope) {
285
+ const hits = [];
286
+ const roots = scope.length > 0 ? scope : ["."];
287
+ for (const scopePath of roots) {
288
+ const target = path.isAbsolute(scopePath) ? scopePath : path.join(root, scopePath);
289
+ const stat = await fs.stat(target).catch(() => undefined);
290
+ if (!stat)
291
+ continue;
292
+ const files = stat.isDirectory() ? await collectFiles(target) : [target];
293
+ for (const file of files) {
294
+ if (!SCANNABLE_EXTENSIONS.has(path.extname(file).toLowerCase()))
295
+ continue;
296
+ const content = await fs.readFile(file, "utf8").catch(() => undefined);
297
+ if (!content)
298
+ continue;
299
+ const lines = content.split("\n");
300
+ for (const [index, line] of lines.entries()) {
301
+ for (const { label, pattern } of SECRET_PATTERNS) {
302
+ pattern.lastIndex = 0;
303
+ let match;
304
+ while ((match = pattern.exec(line)) !== null) {
305
+ hits.push({
306
+ file: path.relative(root, file).replace(/\\/g, "/"),
307
+ line: index + 1,
308
+ label,
309
+ match: match[0].slice(0, 60),
310
+ redacted: redact(match[0]),
311
+ });
312
+ if (match.index === pattern.lastIndex)
313
+ pattern.lastIndex++;
314
+ }
315
+ }
316
+ }
317
+ }
318
+ }
319
+ return hits;
320
+ }
321
+ /** Recorre un directorio devolviendo los archivos escaneables. */
322
+ async function collectFiles(dir) {
323
+ const out = [];
324
+ const entries = await fs.readdir(dir, { withFileTypes: true }).catch(() => []);
325
+ for (const entry of entries) {
326
+ if (entry.name.startsWith(".") && entry.name !== ".env")
327
+ continue;
328
+ if (SCAN_EXCLUDES.has(entry.name))
329
+ continue;
330
+ const full = path.join(dir, entry.name);
331
+ if (entry.isDirectory()) {
332
+ out.push(...(await collectFiles(full)));
333
+ }
334
+ else {
335
+ out.push(full);
336
+ }
337
+ }
338
+ return out;
339
+ }
340
+ /**
341
+ * Corre el gate de seguridad.
342
+ *
343
+ * Distinto del gate técnico: audita trust boundaries y secretos. Un FAIL acá
344
+ * bloquea la card aunque el técnico esté verde.
345
+ */
346
+ export async function runSecurityGate(projectRoot, scope, _runner = defaultRunner) {
347
+ const results = [];
348
+ for (const { label, pattern } of SECRET_PATTERNS) {
349
+ const hits = await scanForSecrets(projectRoot, scope);
350
+ const relevant = hits.filter((h) => h.label === label);
351
+ if (relevant.length > 0) {
352
+ const detail = relevant
353
+ .map((h) => `${h.file}:${h.line} ${h.label} ${h.redacted}`)
354
+ .join("\n");
355
+ results.push({
356
+ phase: "secrecy",
357
+ command: `scan:${label}`,
358
+ outcome: "fail",
359
+ exitCode: 1,
360
+ output: detail,
361
+ detail: `${label} — ${relevant.length} hallazgo(s)`,
362
+ reason: `Se detectaron ${relevant.length} secretos (${label}). El valor está redactado a propósito.`,
363
+ });
364
+ }
365
+ else {
366
+ results.push({
367
+ phase: "secrecy",
368
+ command: `scan:${label}`,
369
+ outcome: "pass",
370
+ exitCode: 0,
371
+ output: "0 hallazgos",
372
+ detail: `${label} — limpio`,
373
+ });
374
+ }
375
+ }
376
+ if (scope.length === 0) {
377
+ results.push(na("diff", "sin scope definido: no hay archivos que auditar"));
378
+ }
379
+ return results;
380
+ }
381
+ /** Consolida los resultados en un veredicto. */
382
+ export function consolidate(cardId, technical, security) {
383
+ const worst = (results) => {
384
+ if (results.some((r) => r.outcome === "fail"))
385
+ return "fail";
386
+ if (results.some((r) => r.outcome === "not_run"))
387
+ return "not_run";
388
+ if (results.length === 0)
389
+ return "na";
390
+ if (results.every((r) => r.outcome === "na"))
391
+ return "na";
392
+ return "pass";
393
+ };
394
+ const technicalOutcome = worst(technical);
395
+ const securityOutcome = worst(security);
396
+ return {
397
+ cardId,
398
+ technical,
399
+ security,
400
+ technicalOutcome,
401
+ securityOutcome,
402
+ passed: technicalOutcome === "pass" && securityOutcome === "pass",
403
+ ranAt: nowIso(),
404
+ };
405
+ }
406
+ /**
407
+ * ¿La fase de tests corrió y pasó con al menos un test?
408
+ *
409
+ * Un gate que pasa con cero tests es un gate verde vacío. Los runners
410
+ * imprimen el conteo en formatos distintos ("# tests 5", "5 passed",
411
+ * "Tests: 5"), así que se buscan ambos órdenes.
412
+ */
413
+ export function testsActuallyRan(technical) {
414
+ const test = technical.find((r) => r.phase === "test");
415
+ if (!test || test.outcome !== "pass")
416
+ return false;
417
+ // "5 tests" / "5 passed" / "5 test"
418
+ const before = /\b(\d+)\s+(?:tests?|passed|pass)\b/i.exec(test.output);
419
+ if (before)
420
+ return Number.parseInt(before[1], 10) > 0;
421
+ // "tests 5" / "passed 5" / "Tests: 5"
422
+ const after = /\b(?:tests?|passed|pass)\b\D{0,3}(\d+)/i.exec(test.output);
423
+ if (after)
424
+ return Number.parseInt(after[1], 10) > 0;
425
+ // Sin conteo legible: no se puede afirmar que corrieron tests.
426
+ return false;
427
+ }
428
+ /** Renderiza el gate como markdown para el artefacto. */
429
+ export function renderGate(result) {
430
+ const lines = [];
431
+ lines.push(`# Gates — ${result.cardId}`);
432
+ lines.push("");
433
+ lines.push(`**Corredor:** ${result.ranAt}`);
434
+ lines.push("");
435
+ const table = (title, rows) => {
436
+ lines.push(`## ${title}`);
437
+ lines.push("");
438
+ lines.push("| # | Fase | Comando | Resultado | Detalle |");
439
+ lines.push("|---|------|---------|-----------|---------|");
440
+ rows.forEach((r, i) => {
441
+ lines.push(`| ${i + 1} | ${r.phase} | \`${r.command ?? "—"}\` | ${r.outcome.toUpperCase()} | ${r.detail} |`);
442
+ });
443
+ lines.push("");
444
+ };
445
+ table("Técnico", result.technical);
446
+ table("Seguridad", result.security);
447
+ lines.push("## Salida de los comandos");
448
+ lines.push("");
449
+ for (const r of [...result.technical, ...result.security]) {
450
+ if (r.outcome === "na" || !r.command)
451
+ continue;
452
+ lines.push(`### ${r.phase} — \`${r.command}\``);
453
+ lines.push("");
454
+ lines.push("```");
455
+ lines.push(r.output || "(sin salida)");
456
+ lines.push("```");
457
+ lines.push("");
458
+ }
459
+ lines.push(`**Gate técnico:** ${result.technicalOutcome.toUpperCase()}`);
460
+ lines.push("");
461
+ lines.push(`**Gate seguridad:** ${result.securityOutcome.toUpperCase()}`);
462
+ lines.push("");
463
+ lines.push(`**Veredicto:** ${result.passed ? "PASS" : "FAIL"}`);
464
+ lines.push("");
465
+ return lines.join("\n");
466
+ }
467
+ //# sourceMappingURL=gates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gates.js","sourceRoot":"","sources":["../../src/core/gates.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Leases con TTL y heartbeat.
3
+ *
4
+ * Del patrón "lease-based polling" de los workflow engines: un worker toma
5
+ * una tarea con un lease que expira; el heartbeat lo extiende; si el worker
6
+ * muere, el lease expira y la tarea se puede reasignar. Es lo que impide que
7
+ * una card quede trabada por un subagente que murió en silencio.
8
+ */
9
+ import type { Limits } from "./types.js";
10
+ export interface Lease {
11
+ taskId: string;
12
+ agentId: string;
13
+ acquiredAt: string;
14
+ expiresAt: string;
15
+ /** Timestamp del último heartbeat. */
16
+ heartbeatAt: string;
17
+ /** Cantidad de renovaciones. */
18
+ renewals: number;
19
+ }
20
+ export declare class LeaseStore {
21
+ private readonly dir;
22
+ private readonly limits;
23
+ private readonly clock;
24
+ constructor(dir: string, limits: Limits, clock?: () => number);
25
+ private file;
26
+ /**
27
+ * Intenta tomar el lease de una tarea.
28
+ *
29
+ * Devuelve `undefined` si ya hay un lease vivo de otro agente: el
30
+ * double-claim es exactamente el bug que el lease previene.
31
+ */
32
+ acquire(taskId: string, agentId: string): Promise<Lease | undefined>;
33
+ /** Renueva el lease. Devuelve `undefined` si ya no lo tenés. */
34
+ heartbeat(taskId: string, agentId: string): Promise<Lease | undefined>;
35
+ /** Libera el lease. */
36
+ release(taskId: string, agentId: string): Promise<boolean>;
37
+ /** Lee un lease. */
38
+ get(taskId: string): Promise<Lease | undefined>;
39
+ /** ¿El lease venció? */
40
+ isExpired(lease: Lease): boolean;
41
+ /** ¿Quién tiene el lease de esta tarea? `undefined` si está libre. */
42
+ holder(taskId: string): Promise<string | undefined>;
43
+ /** ¿Está la tarea tomada? */
44
+ isLocked(taskId: string): Promise<boolean>;
45
+ }
46
+ //# sourceMappingURL=leases.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"leases.d.ts","sourceRoot":"","sources":["../../src/core/leases.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAGxC,MAAM,WAAW,KAAK;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,sCAAsC;IACtC,WAAW,EAAE,MAAM,CAAA;IACnB,gCAAgC;IAChC,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,qBAAa,UAAU;IAEnB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAFL,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,KAAK,GAAE,MAAM,MAAiB;IAGjD,OAAO,CAAC,IAAI;IAIZ;;;;;OAKG;IACG,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC;IAmB1E,gEAAgE;IAC1D,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC;IAc5E,uBAAuB;IACjB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAWhE,oBAAoB;IACd,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC;IAIrD,wBAAwB;IACxB,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO;IAIhC,sEAAsE;IAChE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAMzD,6BAA6B;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAGjD"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Leases con TTL y heartbeat.
3
+ *
4
+ * Del patrón "lease-based polling" de los workflow engines: un worker toma
5
+ * una tarea con un lease que expira; el heartbeat lo extiende; si el worker
6
+ * muere, el lease expira y la tarea se puede reasignar. Es lo que impide que
7
+ * una card quede trabada por un subagente que murió en silencio.
8
+ */
9
+ import path from "node:path";
10
+ import { nowIso, readJson, writeJsonAtomic } from "./fsx.js";
11
+ export class LeaseStore {
12
+ dir;
13
+ limits;
14
+ clock;
15
+ constructor(dir, limits, clock = Date.now) {
16
+ this.dir = dir;
17
+ this.limits = limits;
18
+ this.clock = clock;
19
+ }
20
+ file(taskId) {
21
+ return path.join(this.dir, `${taskId}.json`);
22
+ }
23
+ /**
24
+ * Intenta tomar el lease de una tarea.
25
+ *
26
+ * Devuelve `undefined` si ya hay un lease vivo de otro agente: el
27
+ * double-claim es exactamente el bug que el lease previene.
28
+ */
29
+ async acquire(taskId, agentId) {
30
+ const current = await this.get(taskId);
31
+ if (current && !this.isExpired(current) && current.agentId !== agentId) {
32
+ return undefined;
33
+ }
34
+ const now = this.clock();
35
+ const lease = {
36
+ taskId,
37
+ agentId,
38
+ acquiredAt: current?.acquiredAt ?? nowIso(() => now),
39
+ expiresAt: new Date(now + this.limits.leaseTtlSec * 1000).toISOString(),
40
+ heartbeatAt: nowIso(() => now),
41
+ renewals: current?.renewals ?? 0,
42
+ };
43
+ await writeJsonAtomic(this.file(taskId), lease);
44
+ return lease;
45
+ }
46
+ /** Renueva el lease. Devuelve `undefined` si ya no lo tenés. */
47
+ async heartbeat(taskId, agentId) {
48
+ const current = await this.get(taskId);
49
+ if (!current || current.agentId !== agentId)
50
+ return undefined;
51
+ const now = this.clock();
52
+ const next = {
53
+ ...current,
54
+ expiresAt: new Date(now + this.limits.leaseTtlSec * 1000).toISOString(),
55
+ heartbeatAt: nowIso(() => now),
56
+ renewals: current.renewals + 1,
57
+ };
58
+ await writeJsonAtomic(this.file(taskId), next);
59
+ return next;
60
+ }
61
+ /** Libera el lease. */
62
+ async release(taskId, agentId) {
63
+ const current = await this.get(taskId);
64
+ if (!current)
65
+ return false;
66
+ if (current.agentId !== agentId)
67
+ return false;
68
+ await writeJsonAtomic(this.file(taskId), {
69
+ ...current,
70
+ expiresAt: nowIso(() => this.clock() - 1),
71
+ });
72
+ return true;
73
+ }
74
+ /** Lee un lease. */
75
+ async get(taskId) {
76
+ return readJson(this.file(taskId));
77
+ }
78
+ /** ¿El lease venció? */
79
+ isExpired(lease) {
80
+ return Date.parse(lease.expiresAt) <= this.clock();
81
+ }
82
+ /** ¿Quién tiene el lease de esta tarea? `undefined` si está libre. */
83
+ async holder(taskId) {
84
+ const lease = await this.get(taskId);
85
+ if (!lease || this.isExpired(lease))
86
+ return undefined;
87
+ return lease.agentId;
88
+ }
89
+ /** ¿Está la tarea tomada? */
90
+ async isLocked(taskId) {
91
+ return (await this.holder(taskId)) !== undefined;
92
+ }
93
+ }
94
+ //# sourceMappingURL=leases.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"leases.js","sourceRoot":"","sources":["../../src/core/leases.ts"],"names":[],"mappings":"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"}