oficina-opencode 2.0.1 → 2.1.1
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 +164 -14
- package/commands/oficina.md +22 -9
- 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 +6 -2
- package/skills/oficina/SKILL.md +191 -69
- package/skills/oficina/reference/mesh.md +11 -2
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Compatibilidad
|
|
2
|
+
|
|
3
|
+
Qué versiones de OpenCode y del API de plugin soporta AI-OFFICE, y **qué se
|
|
4
|
+
verificó de cada una**.
|
|
5
|
+
|
|
6
|
+
La regla de este proyecto: lo que no está probado, no está soportado. Un
|
|
7
|
+
número de versión escrito a mano sin prueba de por medio es una promesa que
|
|
8
|
+
nadie verificó.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Versiones
|
|
13
|
+
|
|
14
|
+
| Componente | Versión | Cómo se verificó |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| `@opencode-ai/plugin` | `1.15.11` | Compilación con `strict` + `noUncheckedIndexedAccess` contra los tipos reales del paquete. Un hook que no exista en el tipo **no compila**. |
|
|
17
|
+
| Node.js | `>=20` | CI corre la matriz `[20, 22]` en `ubuntu-latest` y `windows-latest`. |
|
|
18
|
+
| TypeScript | `>=5.6` | `tsc --noEmit` en modo `strict`. |
|
|
19
|
+
|
|
20
|
+
La API de plugin es la que manda: el plugin importa sus tipos, así que
|
|
21
|
+
**la compatibilidad se verifica en tiempo de compilación**, no por
|
|
22
|
+
inspección visual.
|
|
23
|
+
|
|
24
|
+
## Hooks que usa el plugin
|
|
25
|
+
|
|
26
|
+
| Hook | Para qué | Si el host no lo soporta |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| `event` (`session.idle`) | Auto-reanudación de la corrida | La corrida sigue, pero no se reanuda sola. |
|
|
29
|
+
| `event` (`message.updated`) | Contabilidad de costo | No hay registro de costo. La oficina sigue funcionando. |
|
|
30
|
+
| `tool.execute.before` | Guard de scope | **Sin protección**: el scope pasa a ser una instrucción, no una restricción. |
|
|
31
|
+
| `tool.execute.after` | Tripwire | No hay detección de escrituras coladas. |
|
|
32
|
+
| `permission.ask` | Segunda capa de enforcement | Queda solo la primera capa. |
|
|
33
|
+
| `experimental.chat.system.transform` | El estado viaja con cada llamada | El agente tiene que preguntar el estado. |
|
|
34
|
+
| `experimental.session.compacting` | El estado sobrevive a la compactación | La compactación pierde el contexto de la oficina. |
|
|
35
|
+
| `experimental.compaction.autocontinue` | Freno de emergencia | El freno frena el trabajo, pero la auto-continuación post-compactación sigue. |
|
|
36
|
+
|
|
37
|
+
## Mínimo verificable
|
|
38
|
+
|
|
39
|
+
La parte del plugin que **no degrada bien** es el guard de scope. Por eso
|
|
40
|
+
el proyecto declara un mínimo:
|
|
41
|
+
|
|
42
|
+
- **`@opencode-ai/plugin` >= 1.15.11**: a partir de esta versión existen
|
|
43
|
+
`tool.execute.before`, `tool.execute.after` y `permission.ask` con las
|
|
44
|
+
firmas que usa el plugin.
|
|
45
|
+
|
|
46
|
+
Si se instala con una versión anterior, el typecheck falla en la
|
|
47
|
+
instalación del consumidor y eso es intencional: es mejor un error de
|
|
48
|
+
compilación que una instalación que parece protegida y no lo está.
|
|
49
|
+
|
|
50
|
+
## Pruebas de integración por versión
|
|
51
|
+
|
|
52
|
+
El CI corre la matriz completa:
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
os: [ubuntu-latest, windows-latest]
|
|
56
|
+
node: [20, 22]
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
En cada combinación se corre:
|
|
60
|
+
|
|
61
|
+
1. `npm run typecheck` — la API de plugin compila.
|
|
62
|
+
2. `npm run test` — la suite completa (los tests del plugin usan un host
|
|
63
|
+
simulado con la misma forma que el real).
|
|
64
|
+
3. `npm run build` — el `dist/` se genera.
|
|
65
|
+
4. `npm pack --dry-run` — el paquete se puede armar.
|
|
66
|
+
|
|
67
|
+
Y en un job aparte, el smoke del CLI sobre el `dist/` real.
|
|
68
|
+
|
|
69
|
+
## OpenCode
|
|
70
|
+
|
|
71
|
+
| Versión de OpenCode | Soporte |
|
|
72
|
+
|---|---|
|
|
73
|
+
| La que trae el host actual | Sí. Es la que se prueba en desarrollo. |
|
|
74
|
+
| Versiones anteriores | No declaradas. El plugin usa `session.prompt` con partes `subtask`, `session.promptAsync`, `session.get` y `client.app.log`. |
|
|
75
|
+
|
|
76
|
+
El plugin **no fija** una versión de OpenCode: se instala como plugin y usa
|
|
77
|
+
el API que el host expone. Lo que sí fija es la versión del **API de
|
|
78
|
+
plugin**, que es lo que usa directamente.
|
|
79
|
+
|
|
80
|
+
## Requisitos del usuario, no del plugin
|
|
81
|
+
|
|
82
|
+
| Requisito | Por qué |
|
|
83
|
+
|---|---|
|
|
84
|
+
| Node >= 20 | `fs.promises`, `structuredClone`, ESM estable. |
|
|
85
|
+
| Un repo de `agency-agents` (opcional) | Sin él, la fábrica de roles funciona igual, pero los agentes salen de la plantilla genérica en vez del catálogo curado. |
|
|
86
|
+
| npm CLI >= 11.15 (opcional) | Solo para `npm stage publish`, el flujo de publicación por etapas de npm. La oficina no lo usa: publica directo. |
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Cómo se mantiene esta tabla
|
|
91
|
+
|
|
92
|
+
Las cifras de versiones salen del `package.json` real y se verifican en el
|
|
93
|
+
CI. El bloque de hechos de `README.md` y `docs/QUE-ES.md` se genera con
|
|
94
|
+
`npm run docs:sync` y el CI **falla** si se desincroniza:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
npm run docs:check # sale con 1 si la doc no coincide con el código
|
|
98
|
+
```
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# Garantías
|
|
2
|
+
|
|
3
|
+
Qué promete AI-OFFICE, **qué mecanismo lo hace cumplir** y **dónde termina
|
|
4
|
+
la garantía**.
|
|
5
|
+
|
|
6
|
+
La regla de este documento: si algo no se puede verificar con un test o con
|
|
7
|
+
un mecanismo de código, no se promete. Un README que vende más de lo que el
|
|
8
|
+
código hace es peor que un README modesto: hace que el usuario confíe en
|
|
9
|
+
una protección que no existe.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 1. El scope es una restricción
|
|
14
|
+
|
|
15
|
+
### Qué se garantiza
|
|
16
|
+
|
|
17
|
+
| Vector de escape | Cómo se frena | Test |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| `..` en la ruta | Se resuelve a absoluta y se verifica la contención | `sandbox.test.ts` |
|
|
20
|
+
| Path absoluto fuera del proyecto | Ídem | `sandbox.test.ts` |
|
|
21
|
+
| Symlink que apunta afuera | Se resuelve el symlink y **se revalida** la contención | `sandbox.test.ts` |
|
|
22
|
+
| Symlink roto | Se deniega (no se sabe a dónde apunta) | `sandbox.test.ts` |
|
|
23
|
+
| `mayTouch: ["**"]` como comodín | `**` significa "el proyecto", nunca "el filesystem" | `sandbox.test.ts` |
|
|
24
|
+
| Path sensível (`.env`, `.npmrc`, `.git/**`, claves) | Nunca escribible, pase lo que pase el scope | `sandbox.test.ts` |
|
|
25
|
+
| Diff multi-archivo | Se chequean **todos** los archivos del diff | `scopeGuard.test.ts` |
|
|
26
|
+
| Tool de shell escribiendo afuera | El comando se analiza antes de ejecutarse | `sandbox.test.ts` |
|
|
27
|
+
| Subagente delegado | Hereda el scope por la cadena de padres de sesión | `scopeGuard.test.ts` |
|
|
28
|
+
| Path con byte NUL | Se rechaza | `sandbox.test.ts` |
|
|
29
|
+
|
|
30
|
+
### Las tres capas
|
|
31
|
+
|
|
32
|
+
1. **`tool.execute.before`** — el guard propio. Cubre toda tool que el motor
|
|
33
|
+
reporte, incluidas las de shell.
|
|
34
|
+
2. **`permission.ask`** — el framework. Deniega a nivel del motor.
|
|
35
|
+
3. **`tool.execute.after`** — el tripwire. Detecta lo que se coló.
|
|
36
|
+
|
|
37
|
+
### Lo que NO se garantiza
|
|
38
|
+
|
|
39
|
+
- **Un shell con interpreter embebido puede escribir fuera.** `python -c`,
|
|
40
|
+
`node -e`, heredocs y `eval` esconden paths que ningún análisis previo
|
|
41
|
+
puede extraer con certeza. Cuando el comando no se puede interpretar, se
|
|
42
|
+
deja pasar y **el tripwire lo detecta después**.
|
|
43
|
+
- **El tripwire no revierte.** Detecta y reporta; no deshace. Y vigila el
|
|
44
|
+
proyecto, no el filesystem entero.
|
|
45
|
+
- **Una escritura fuera del proyecto ya formada no se revierte.** El
|
|
46
|
+
contención previene; no limpia.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 2. Las invariantes se evalúan donde corresponde
|
|
51
|
+
|
|
52
|
+
Ocho invariantes duros, en código, no en el prompt:
|
|
53
|
+
|
|
54
|
+
| Invariante | Dónde se evalúa |
|
|
55
|
+
|---|---|
|
|
56
|
+
| `reviewer-neq-owner` | Toda revisión |
|
|
57
|
+
| `gate-required-before-done` | Al cerrar la card |
|
|
58
|
+
| `evidence-required-before-done` | Al cerrar la card |
|
|
59
|
+
| `max-review-rejections` | Al revisar |
|
|
60
|
+
| `max-help-chain-hops` | Al enrutar ayuda |
|
|
61
|
+
| `max-cards` | Al planear |
|
|
62
|
+
| `max-agent-calls` | Al reclamar |
|
|
63
|
+
| `max-spawns-total` | Al arrancar un subagente |
|
|
64
|
+
|
|
65
|
+
> **Bug corregido en esta versión**: `closeCard` evaluaba los invariantes
|
|
66
|
+
> con la card en `review`, no en `done`. Como los dos invariantes de cierre
|
|
67
|
+
> solo tienen sentido en `done`, **nunca disparaban**. Ahora se validan
|
|
68
|
+
> contra el estado al que la card llegaría.
|
|
69
|
+
|
|
70
|
+
El `evidence-required-before-done` es **fail-closed**: si la evidencia no se
|
|
71
|
+
evaluó, la card no se cierra. "No lo sé" no es "sí".
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 3. El cierre exige evidencia, no solo checks verdes
|
|
76
|
+
|
|
77
|
+
Pirámide de verificación:
|
|
78
|
+
|
|
79
|
+
| Nivel | Qué es | Cómo se comprueba |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| 1 — Estático | Tipos, lint, secretos, dependencias | Los gates técnicos |
|
|
82
|
+
| 2 — Comportamiento | Unitarias, integración, regresión | El gate `test` + evidencia de artefacto |
|
|
83
|
+
| 3 — Requisitos | Cada criterio de aceptación con su evidencia | El grafo de evidencia |
|
|
84
|
+
| 4 — Auditoría | Revisión independiente, riesgos | Evidencia de auditoría (revisor ≠ owner) |
|
|
85
|
+
|
|
86
|
+
Una card se cierra solo si **los gates pasan Y cada requisito obligatorio
|
|
87
|
+
tiene evidencia suficiente**.
|
|
88
|
+
|
|
89
|
+
### El grafo requisito → evidencia
|
|
90
|
+
|
|
91
|
+
Cada criterio de aceptación es un requisito. Cada evidencia (artefacto,
|
|
92
|
+
test, gate, inspección, auditoría) se vincula a un requisito con un nivel.
|
|
93
|
+
El grafo responde: *¿qué falta para poder cerrar?*
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
T-00 · Implementar login
|
|
97
|
+
[✓] endpoint de login nivel 2 artefacto
|
|
98
|
+
[ ] sin errores de validación nivel 0 — FALTA
|
|
99
|
+
[!] cubierto por revisión independiente nivel 4 auditoría
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Niveles y tope por tipo**: un artefacto llega a nivel 2; un test, a 2; un
|
|
103
|
+
gate, a 1 (o 2 si es la fase `test`); una inspección, a 3; una auditoría
|
|
104
|
+
(revisor ≠ owner), a 4. `evidenceFromAudit` **lanza** si owner y reviewer
|
|
105
|
+
son el mismo: una auto-revisión con cara de auditoría es peor que no tener
|
|
106
|
+
auditoría.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 4. La concurrencia se sostiene
|
|
111
|
+
|
|
112
|
+
| Garantía | Mecanismo | Test |
|
|
113
|
+
|---|---|---|
|
|
114
|
+
| Un lease por task | Ciclo leer-evaluar-escribir bajo un lock de directorio | `concurrency.test.ts` |
|
|
115
|
+
| Seq único por canal | Asignación y escritura bajo el lock del canal | `concurrency.test.ts` |
|
|
116
|
+
| Idempotencia real | Check y escritura bajo el mismo lock | `concurrency.test.ts` |
|
|
117
|
+
| Un id nunca escapa del directorio | `safeId` + `safeJoin` | `ids`, `concurrency.test.ts` |
|
|
118
|
+
| Un JSON a medio escribir no se lee | `writeAtomic` (tmp + rename) | — |
|
|
119
|
+
| Un JSON vacío no se pone en cuarentena | `readJson` distingue "en curso" de "corrupto" | — |
|
|
120
|
+
|
|
121
|
+
### El bug de Windows
|
|
122
|
+
|
|
123
|
+
`fs.mkdir` sobre un directorio que otro proceso está borrando da **EPERM**,
|
|
124
|
+
no `EEXIST`. Sin manejar ese caso, dos emisores concurrentes se rompían en
|
|
125
|
+
Windows. Está cubierto y hay un test de carrera de 40 rondas.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 5. Una caída no destruye la corrida
|
|
130
|
+
|
|
131
|
+
`reconcile()` compara **estado vs artefactos vs filesystem** y reporta:
|
|
132
|
+
|
|
133
|
+
| Situación | Qué hace |
|
|
134
|
+
|---|---|
|
|
135
|
+
| Card en `review` sin `handoff.md` | El agente murió entregando. Se avisa. |
|
|
136
|
+
| Card en `done` sin `gates.md` | Se cerró sin gate. **Crítico.** |
|
|
137
|
+
| Card en progreso sin owner | Nadie la va a terminar. **Crítico.** |
|
|
138
|
+
| Lease de una card que no existe | Se libera. |
|
|
139
|
+
| Lease de una card terminada | Se libera. |
|
|
140
|
+
| Agente `working` sobre una card `done` | Se libera el agente. |
|
|
141
|
+
| Artefacto de una card inexistente | Se reporta. |
|
|
142
|
+
| Card bloqueada por dependencia irresoluble | Se reporta. |
|
|
143
|
+
|
|
144
|
+
**Lo que no hace**: completar cards, inventar evidencia ni "arreglar" el
|
|
145
|
+
estado. Un sistema que se auto-repara inventando evidencia es peor que uno
|
|
146
|
+
que dice "esto está roto".
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## 6. El costo se controla
|
|
151
|
+
|
|
152
|
+
- El costo real viene del motor (`AssistantMessage.cost`), no se estima.
|
|
153
|
+
- Se contabiliza por card, por agente y por fase (implementación, review,
|
|
154
|
+
reparación).
|
|
155
|
+
- Presupuestos por corrida, por card y por fase.
|
|
156
|
+
- Estimación previa a una corrida grande.
|
|
157
|
+
- Detección de trabajo redundante (releer el mismo contexto, repetir el
|
|
158
|
+
mismo comando).
|
|
159
|
+
- Freno de emergencia: `oficina_stop` detiene la corrida; el
|
|
160
|
+
auto-continuación y el despacho se negan mientras esté detenida.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 7. Lo que NO se promete
|
|
165
|
+
|
|
166
|
+
- **No es un sandbox.** Un plugin de OpenCode no puede contener el
|
|
167
|
+
filesystem entero. El guard es una política de aplicación, y su límite
|
|
168
|
+
está en la sección 1.
|
|
169
|
+
- **No garantiza que el objetivo del usuario se cumpla.** Garantiza que
|
|
170
|
+
**cada criterio de aceptación declarado** tenga evidencia. Si el criterio
|
|
171
|
+
estaba mal escrito, la evidencia lo va a reflejar.
|
|
172
|
+
- **No mide la calidad de la evidencia.** Un `handoff.md` que dice "listo"
|
|
173
|
+
es evidencia de nivel 2. Que sea verdad o no depende de la revisión.
|
|
174
|
+
- **No reemplaza al reviewer.** El invariante `reviewer-neq-owner` impide
|
|
175
|
+
que el owner se apruebe a sí mismo, pero no evalúa el juicio de quien
|
|
176
|
+
revisa.
|
|
177
|
+
- **El benchmark mide selección, no resultado.** Dice que el sistema elige
|
|
178
|
+
bien al rol. No dice que el agente haga bien el trabajo.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Cómo se verifica todo esto
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
npm test # 385 tests: adversariales, de carrera y de recuperación
|
|
186
|
+
npm run typecheck # strict + noUncheckedIndexedAccess
|
|
187
|
+
npm run coverage # cobertura por línea
|
|
188
|
+
npm run docs:check # la documentación coincide con el código
|
|
189
|
+
npm run bench:selection # el matcher le gana a las alternativas simples
|
|
190
|
+
```
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Plan de cierre — AI-OFFICE v2.1
|
|
2
|
+
|
|
3
|
+
Fecha: 2026-10-22 · Base: `oficina-opencode@2.0.1` · Commit base: `5232fe3`
|
|
4
|
+
|
|
5
|
+
## Contexto
|
|
6
|
+
|
|
7
|
+
El review externoidentified 5 prioridades. Contra el snapshot viejo (decía 201 tests,
|
|
8
|
+
pesos 40/35/20/5, 4 commits) algunas cosas **ya están resueltas**:
|
|
9
|
+
|
|
10
|
+
| Punto del review | Estado real |
|
|
11
|
+
|---|---|
|
|
12
|
+
| Pesos 40/35/20/5 | YA RESUELTO — ahora 35/28/18/10/5 + recall/focus de misión |
|
|
13
|
+
| "201 tests" | YA RESUELTO — 213 tests, 14 archivos |
|
|
14
|
+
| "4 commits" | YA RESUELTO — 6 commits |
|
|
15
|
+
| README clone con URL vieja | YA RESUELTO — 13 referencias corregidas, 2.0.1 publicado |
|
|
16
|
+
|
|
17
|
+
El resto **es real y verificado contra el código**:
|
|
18
|
+
|
|
19
|
+
| # | Hallazgo verificado | Dónde |
|
|
20
|
+
|---|---|---|
|
|
21
|
+
| 1 | `path.relative` sin realpath → **escape por symlink** | `scopeGuard.ts:95-98` |
|
|
22
|
+
| 2 | `mayTouch: ["**"]` autoriza **cualquier** path, incluso `../../etc/passwd` | `scopeGuard.ts:53` |
|
|
23
|
+
| 3 | Solo cubre `edit/write/patch/multiedit/apply_patch` — **no** `bash` ni `task` | `scopeGuard.ts:23` |
|
|
24
|
+
| 4 | `extractPath` de `patch` solo lee el **primer** archivo del diff | `scopeGuard.ts:67-74` |
|
|
25
|
+
| 5 | Sin protección de archivos sensibles (`.npmrc`, `.git/**`, `*.pem`) | — |
|
|
26
|
+
| 6 | Subagente delegado **no hereda scope**: sin binding de sesión, el guard sale temprano | `scopeGuard.ts:88-90` |
|
|
27
|
+
| 7 | `LeaseStore.acquire` es **TOCTOU**: read → check → write. Doble claim posible | `leases.ts:42-57` |
|
|
28
|
+
| 8 | `Bus.nextSeq` es TOCTOU → **seq duplicados** entre emisores concurrentes | `bus.ts:88-99` |
|
|
29
|
+
| 9 | Idempotencia del bus es check-then-write → **mensajes duplicados** | `bus.ts:108-111` |
|
|
30
|
+
| 10 | `cardId`/`agentId`/`messageId` sin sanitizar → **path traversal** al escribir | `paths.ts:153-165`, `bus.ts:211` |
|
|
31
|
+
| 11 | `spawnsUsed` nunca se incrementa → invariante `max-spawns-total` **nunca dispara** | `retry.ts:83` sin callers |
|
|
32
|
+
| 12 | `transitionRun` importado y **nunca llamado**; el status se muta directo | `office.ts:28` |
|
|
33
|
+
| 13 | `state.pendingGates` nunca se puebla; `state.iteration` nunca se incrementa | `types.ts:352` |
|
|
34
|
+
| 14 | `savePlan` no valida `maxCards` → se pueden planear 50 cards con límite 20 | `office.ts:467` |
|
|
35
|
+
| 15 | `LeaseStore.heartbeat` existe y **nadie lo llama** → los leases expiran duro | `leases.ts:62` |
|
|
36
|
+
| 16 | No hay grafo de requisitos→evidencia | — |
|
|
37
|
+
| 17 | No hay control de costo, tokens, tiempo ni parada de emergencia | — |
|
|
38
|
+
| 18 | No hay benchmark de selección de agentes | — |
|
|
39
|
+
| 19 | Cifras de la documentación hardcodeadas, sin fuente única | `README.md` |
|
|
40
|
+
| 20 | Sin matriz de compatibilidad de versiones | — |
|
|
41
|
+
|
|
42
|
+
## Hallazgo nuevo (no estaba en el review)
|
|
43
|
+
|
|
44
|
+
La API del plugin tiene **3 hooks sin usar** que resuelven partes del problema mejor
|
|
45
|
+
de lo que había planeado:
|
|
46
|
+
|
|
47
|
+
- `permission.ask` → **denegar a nivel framework**, no solo en nuestro hook.
|
|
48
|
+
- `tool.execute.after` → **tripwire**: detectar escrituras que pasaron el hook.
|
|
49
|
+
- `chat.message` / `chat.params` → **nombre del agente y modelo por sesión**
|
|
50
|
+
(clave para P5 y para resolver el scope de subagentes).
|
|
51
|
+
- `event` con `message.updated` → `AssistantMessage.cost` + `tokens{input,output,reasoning,cache}`
|
|
52
|
+
+ `modelID` + `providerID` + latencia. **El costo ya viene del motor.**
|
|
53
|
+
- `experimental.compaction.autocontinue` → **freno de emergencia** (desactivar el auto-continuar).
|
|
54
|
+
|
|
55
|
+
## Diseño
|
|
56
|
+
|
|
57
|
+
### P1 — Scope guard real (3 capas)
|
|
58
|
+
|
|
59
|
+
| Capa | Mecanismo | Cubre |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| 1. Framework | `permission.ask` → `deny` | todas las tools, **incluidas las de subagentes** |
|
|
62
|
+
| 2. Propio | `tool.execute.before` → `sandbox.check` | resolución real, symlinks, sensibles, shell |
|
|
63
|
+
| 3. Detección | `tool.execute.after` → tripwire | lo que se coló (writes por shell) |
|
|
64
|
+
|
|
65
|
+
`src/core/sandbox.ts` (nuevo):
|
|
66
|
+
- `resolveTarget(projectRoot, raw)` → verifica NUL, normaliza con `path.resolve`,
|
|
67
|
+
**camina cada componente** con `lstat` para detectar symlinks, resuelve con
|
|
68
|
+
`realpath` el ancestro existente más profundo, y **re-valida la contención**.
|
|
69
|
+
- `SENSITIVE_PATTERNS` → `.env*`, `.npmrc`, `.netrc`, `.git/**`, `*.pem|key|p12`,
|
|
70
|
+
`id_rsa*`, `.ssh/**`, `.aws/**`, `.gnupg/**`, `credentials.json`, `service-account*.json`.
|
|
71
|
+
- `analyzeShellCommand(cmd)` → extrae paths de escritura (`>`, `>>`, `tee`, `cp`,
|
|
72
|
+
`mv`, `rm`, `sed -i`, `dd`, `install`, `truncate`, `touch`, `python -c`, `node -e`).
|
|
73
|
+
- `checkScope(...)` → veredicto único con motivo legible.
|
|
74
|
+
|
|
75
|
+
Resolución de scope de subagentes: mapa `sessionID → agent` construido con
|
|
76
|
+
`chat.message` + `message.updated`, y `agent → card` desde el roster. El scope
|
|
77
|
+
**hereda por el árbol de padres** de la sesión.
|
|
78
|
+
|
|
79
|
+
### P2 — Requirement-to-Evidence Graph
|
|
80
|
+
|
|
81
|
+
`src/core/evidence.ts` (nuevo):
|
|
82
|
+
- Cada `acceptance[]` de una card es un **requisito** (nivel 1 = obligatorio).
|
|
83
|
+
- Cada requisito se enlaza a: artefactos, tests citados, gates, y veredicto manual.
|
|
84
|
+
- Niveles: 1 estático, 2 comportamiento, 3 validación de requisitos, 4 auditoría.
|
|
85
|
+
- Invariante nuevo: `evidence-required-before-done` — sin evidencia de todos los
|
|
86
|
+
requisitos obligatorios, la card **no** pasa a `done`.
|
|
87
|
+
- Gate nuevo: fase `verify`.
|
|
88
|
+
|
|
89
|
+
### P3 — Documentación generada
|
|
90
|
+
|
|
91
|
+
- `src/core/docsync.ts` → única fuente de verdad: escanea el catálogo real y
|
|
92
|
+
emite `{ agents, divisions, divisionsList, tools, hooks, tests }`.
|
|
93
|
+
- `scripts/sync-docs.ts` → regenera las cifras en README y docs; **falla** si hay drift.
|
|
94
|
+
- CI: paso que corre el script y falla si el diff no está vacío.
|
|
95
|
+
- `docs/COMPATIBILITY.md` → matriz OpenCode × `@opencode-ai/plugin` generada.
|
|
96
|
+
|
|
97
|
+
### P4 — Concurrencia y recuperación
|
|
98
|
+
|
|
99
|
+
`src/core/atomic.ts` (nuevo):
|
|
100
|
+
- `putOnce(path, content)` → `open(path, "wx")`: winner único, losers leen el existente.
|
|
101
|
+
- `allocateSeq(channelDir)` → asignacion atomica con `wx` + reintento incremental.
|
|
102
|
+
- `withLock(lockPath, fn)` → mutex por directorio (`mkdir` atómico).
|
|
103
|
+
|
|
104
|
+
Aplicado a: lease acquire (O_EXCL + takeover de expirado), seq del bus,
|
|
105
|
+
idempotencia del bus, y `safeId()` para todos los ids que se usan como nombre de archivo.
|
|
106
|
+
|
|
107
|
+
`src/core/recovery.ts` (nuevo): reconciliación al arrancar — estado vs artefactos
|
|
108
|
+
vs filesystem. Detecta: card en `review` sin artefactos, lease huérfano, artefacto
|
|
109
|
+
sin card, `done` sin gate.
|
|
110
|
+
|
|
111
|
+
### P5 — Costo y control
|
|
112
|
+
|
|
113
|
+
`src/core/cost.ts` (nuevo): ledger por corrida/card/agente/fase.
|
|
114
|
+
- Entrada: evento `message.updated` → `AssistantMessage.cost` + `tokens` + modelo.
|
|
115
|
+
- Presupuestos: por corrida (tokens/USD), por card, y separados por fase
|
|
116
|
+
(implementación / review / reparación).
|
|
117
|
+
- Estimación previa a una corrida grande.
|
|
118
|
+
- Métricas: costo por requisito completado y verificado, tokens por card,
|
|
119
|
+
lecturas repetidas del mismo path (trabajo redundante).
|
|
120
|
+
- Freno: `oficina_stop` + `experimental.compaction.autocontinue` desactivado +
|
|
121
|
+
el hook de idle respeta el flag.
|
|
122
|
+
|
|
123
|
+
## Orden de ejecución
|
|
124
|
+
|
|
125
|
+
1. `types.ts` — contratos nuevos (todo lo demás depende de esto).
|
|
126
|
+
2. `sandbox.ts` + `scopeGuard.ts` + tests adversariales. **(P1)**
|
|
127
|
+
3. `atomic.ts` + `leases.ts` + `bus.ts` + `paths.ts` + tests de concurrencia. **(P4)**
|
|
128
|
+
4. `evidence.ts` + gate `verify` + invariante. **(P2)**
|
|
129
|
+
5. `cost.ts` + tools + hook de costo. **(P5)**
|
|
130
|
+
6. `recovery.ts` + hook de arranque. **(P4)**
|
|
131
|
+
7. `docsync.ts` + `scripts/sync-docs.ts` + CI + `COMPATIBILITY.md`. **(P3)**
|
|
132
|
+
8. Benchmark de selección.
|
|
133
|
+
9. Verificación total: typecheck, tests, cobertura, build, e2e, npm pack, publish.
|
|
134
|
+
|
|
135
|
+
## Criterio de terminado
|
|
136
|
+
|
|
137
|
+
- [ ] typecheck strict limpio
|
|
138
|
+
- [ ] todos los tests pasan (base: 213)
|
|
139
|
+
- [ ] cobertura ≥ 87% stmts
|
|
140
|
+
- [ ] build limpio
|
|
141
|
+
- [ ] e2e verde contra el paquete instalado
|
|
142
|
+
- [ ] `npm pack` sin warnings
|
|
143
|
+
- [ ] documentación generada coincide con el código (CI lo verifica)
|
|
144
|
+
- [ ] publicado en npm
|
package/docs/QUE-ES.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# ¿Qué es AI-OFFICE?
|
|
2
2
|
|
|
3
|
+
<!-- BEGIN GENERATED: facts -->
|
|
4
|
+
| | |
|
|
5
|
+
|---|---|
|
|
6
|
+
| **Paquete** | `oficina-opencode` v2.1.1 |
|
|
7
|
+
| **Tools** | 16 |
|
|
8
|
+
| **Hooks** | 7 |
|
|
9
|
+
| **API de plugin** | `@opencode-ai/plugin@1.15.11` |
|
|
10
|
+
| **Node** | >=20 |
|
|
11
|
+
| **Catálogo** | 258 agentes resolubles en 18 divisiones |
|
|
12
|
+
| **Divisiones** | academic, design, engineering, finance, game-development, gis, healthcare, marketing, paid-media, product, project-management, research, sales, spatial-computing, specialized, strategy, support, testing |
|
|
13
|
+
| **Límites** | 20 cards · 40 llamadas · 20 spawns · 10 agentes |
|
|
14
|
+
| **Cobertura mínima** | 60% |
|
|
15
|
+
<!-- END GENERATED: facts -->
|
|
16
|
+
|
|
17
|
+
|
|
3
18
|
> Una oficina de agentes de IA para OpenCode. Le das un objetivo, y el equipo lo trabaja, lo revisa, lo verifica y no para hasta terminarlo.
|
|
4
19
|
|
|
5
20
|
Este documento explica el sistema sin jerga. Si querés saber **cómo está construido por dentro**, está [`docs/ARCHITECTURE.md`](ARCHITECTURE.md). Si querés **usarlo**, está el [README](../README.md).
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agents": 258,
|
|
3
|
+
"divisions": 18,
|
|
4
|
+
"divisionList": [
|
|
5
|
+
"academic",
|
|
6
|
+
"design",
|
|
7
|
+
"engineering",
|
|
8
|
+
"finance",
|
|
9
|
+
"game-development",
|
|
10
|
+
"gis",
|
|
11
|
+
"healthcare",
|
|
12
|
+
"marketing",
|
|
13
|
+
"paid-media",
|
|
14
|
+
"product",
|
|
15
|
+
"project-management",
|
|
16
|
+
"research",
|
|
17
|
+
"sales",
|
|
18
|
+
"spatial-computing",
|
|
19
|
+
"specialized",
|
|
20
|
+
"strategy",
|
|
21
|
+
"support",
|
|
22
|
+
"testing"
|
|
23
|
+
],
|
|
24
|
+
"source": "C:\\Users\\Marcos\\agency-agents",
|
|
25
|
+
"generatedAt": "2026-10-22T02:57:59.378Z",
|
|
26
|
+
"sourceCommit": "4e3ac3c"
|
|
27
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oficina-opencode",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "Oficina de Agents — malla de coordinadores + peer mesh para OpenCode. Orquestación durable de subagentes con bus de mensajes en disco, roster de roles, leases, gates de calidad y escalamiento por invariantes.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -68,7 +68,10 @@
|
|
|
68
68
|
"test": "vitest run",
|
|
69
69
|
"test:watch": "vitest",
|
|
70
70
|
"coverage": "npm run build && vitest run --coverage",
|
|
71
|
-
"prepublishOnly": "npm run typecheck && npm run test && npm run build"
|
|
71
|
+
"prepublishOnly": "npm run typecheck && npm run test && npm run build && npm run docs:check",
|
|
72
|
+
"docs:sync": "tsx scripts/sync-docs.ts",
|
|
73
|
+
"docs:check": "tsx scripts/sync-docs.ts --check",
|
|
74
|
+
"bench:selection": "tsx benchmarks/selection/run.ts"
|
|
72
75
|
},
|
|
73
76
|
"dependencies": {
|
|
74
77
|
"@opencode-ai/plugin": "^1.15.11",
|
|
@@ -76,6 +79,7 @@
|
|
|
76
79
|
},
|
|
77
80
|
"devDependencies": {
|
|
78
81
|
"@types/node": "^24.12.2",
|
|
82
|
+
"tsx": "^4.20.3",
|
|
79
83
|
"typescript": "^5.8.2",
|
|
80
84
|
"vitest": "^3.2.4",
|
|
81
85
|
"@vitest/coverage-v8": "^3.2.4"
|