oficina-opencode 0.0.0-stage → 2.0.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/DESIGN.md +145 -0
- package/LICENSE +21 -0
- package/README.md +670 -2
- package/agents/oficina-architect.md +72 -0
- package/agents/oficina-commander.md +108 -0
- package/agents/oficina-critico.md +148 -0
- package/agents/oficina-director.md +160 -0
- package/agents/oficina-guardian.md +128 -0
- package/agents/oficina-integrator.md +98 -0
- package/agents/oficina-verificador.md +88 -0
- package/commands/oficina.md +36 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +394 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/briefing.d.ts +115 -0
- package/dist/core/briefing.d.ts.map +1 -0
- package/dist/core/briefing.js +410 -0
- package/dist/core/briefing.js.map +1 -0
- package/dist/core/bus.d.ts +132 -0
- package/dist/core/bus.d.ts.map +1 -0
- package/dist/core/bus.js +250 -0
- package/dist/core/bus.js.map +1 -0
- package/dist/core/catalog.d.ts +303 -0
- package/dist/core/catalog.d.ts.map +1 -0
- package/dist/core/catalog.js +816 -0
- package/dist/core/catalog.js.map +1 -0
- package/dist/core/dispatch.d.ts +110 -0
- package/dist/core/dispatch.d.ts.map +1 -0
- package/dist/core/dispatch.js +301 -0
- package/dist/core/dispatch.js.map +1 -0
- package/dist/core/factory.d.ts +74 -0
- package/dist/core/factory.d.ts.map +1 -0
- package/dist/core/factory.js +243 -0
- package/dist/core/factory.js.map +1 -0
- package/dist/core/fsx.d.ts +39 -0
- package/dist/core/fsx.d.ts.map +1 -0
- package/dist/core/fsx.js +113 -0
- package/dist/core/fsx.js.map +1 -0
- package/dist/core/gates.d.ts +97 -0
- package/dist/core/gates.d.ts.map +1 -0
- package/dist/core/gates.js +467 -0
- package/dist/core/gates.js.map +1 -0
- package/dist/core/leases.d.ts +46 -0
- package/dist/core/leases.d.ts.map +1 -0
- package/dist/core/leases.js +94 -0
- package/dist/core/leases.js.map +1 -0
- package/dist/core/log.d.ts +30 -0
- package/dist/core/log.d.ts.map +1 -0
- package/dist/core/log.js +43 -0
- package/dist/core/log.js.map +1 -0
- package/dist/core/mesa.d.ts +82 -0
- package/dist/core/mesa.d.ts.map +1 -0
- package/dist/core/mesa.js +178 -0
- package/dist/core/mesa.js.map +1 -0
- package/dist/core/office.d.ts +208 -0
- package/dist/core/office.d.ts.map +1 -0
- package/dist/core/office.js +618 -0
- package/dist/core/office.js.map +1 -0
- package/dist/core/paths.d.ts +67 -0
- package/dist/core/paths.d.ts.map +1 -0
- package/dist/core/paths.js +136 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/retry.d.ts +41 -0
- package/dist/core/retry.d.ts.map +1 -0
- package/dist/core/retry.js +86 -0
- package/dist/core/retry.js.map +1 -0
- package/dist/core/roster.d.ts +47 -0
- package/dist/core/roster.d.ts.map +1 -0
- package/dist/core/roster.js +146 -0
- package/dist/core/roster.js.map +1 -0
- package/dist/core/router.d.ts +67 -0
- package/dist/core/router.d.ts.map +1 -0
- package/dist/core/router.js +186 -0
- package/dist/core/router.js.map +1 -0
- package/dist/core/state.d.ts +89 -0
- package/dist/core/state.d.ts.map +1 -0
- package/dist/core/state.js +303 -0
- package/dist/core/state.js.map +1 -0
- package/dist/core/types.d.ts +333 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +27 -0
- package/dist/core/types.js.map +1 -0
- package/dist/hooks/compacting.d.ts +18 -0
- package/dist/hooks/compacting.d.ts.map +1 -0
- package/dist/hooks/compacting.js +72 -0
- package/dist/hooks/compacting.js.map +1 -0
- package/dist/hooks/scopeGuard.d.ts +39 -0
- package/dist/hooks/scopeGuard.d.ts.map +1 -0
- package/dist/hooks/scopeGuard.js +114 -0
- package/dist/hooks/scopeGuard.js.map +1 -0
- package/dist/hooks/sessionIdle.d.ts +35 -0
- package/dist/hooks/sessionIdle.d.ts.map +1 -0
- package/dist/hooks/sessionIdle.js +110 -0
- package/dist/hooks/sessionIdle.js.map +1 -0
- package/dist/hooks/systemTransform.d.ts +18 -0
- package/dist/hooks/systemTransform.d.ts.map +1 -0
- package/dist/hooks/systemTransform.js +63 -0
- package/dist/hooks/systemTransform.js.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +103 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/index.d.ts +22 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +854 -0
- package/dist/tools/index.js.map +1 -0
- package/docs/ARCHITECTURE.md +258 -0
- package/docs/QUE-ES.md +337 -0
- package/examples/opencode.json +7 -0
- package/package.json +81 -4
- package/skills/oficina/README.md +120 -0
- package/skills/oficina/SKILL.md +251 -0
- package/skills/oficina/reference/mesh.md +345 -0
- package/skills/oficina/reference/protocol.md +342 -0
- package/skills/oficina/reference/roles.md +171 -0
- package/skills/oficina/reference/templates.md +278 -0
package/README.md
CHANGED
|
@@ -1,3 +1,671 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# AI-OFFICE
|
|
4
|
+
|
|
5
|
+
### Una oficina de agentes para OpenCode donde el trabajo no se detiene cuando el modelo dice «terminé».
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/oficina-opencode)
|
|
8
|
+
[](https://github.com/SOCRAMbt/AI-OFFICE/tree/main/LICENSE)
|
|
9
|
+
[](https://nodejs.org)
|
|
10
|
+
[](https://github.com/SOCRAMbt/AI-OFFICE/tree/main/tests)
|
|
11
|
+
[](https://vitest.dev)
|
|
12
|
+
[](https://github.com/SOCRAMbt/AI-OFFICE/blob/main/tsconfig.json)
|
|
13
|
+
|
|
14
|
+
**4 hooks · 13 tools · 7 agentes · 6 gates · 6 invariantes duros · 306 roles de catálogo**
|
|
15
|
+
|
|
16
|
+
> **¿Preferís la versión en humano?** → [`docs/QUE-ES.md`](https://github.com/SOCRAMbt/AI-OFFICE/blob/main/docs/QUE-ES.md) — qué es, qué hace y por qué, sin jerga.
|
|
17
|
+
> **¿Preferís el porqué técnico?** → [`docs/ARCHITECTURE.md`](https://github.com/SOCRAMbt/AI-OFFICE/blob/main/docs/ARCHITECTURE.md)
|
|
18
|
+
|
|
19
|
+
[Documentación](#arquitectura) · [Instalación](#instalación) · [Uso](#uso) · [CLI](#cli-operación)
|
|
20
|
+
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## El problema
|
|
26
|
+
|
|
27
|
+
Working con agents tiene cinco fallas concretas. No son falta de inteligencia: son falta de **estructura**.
|
|
28
|
+
|
|
29
|
+
| Falla | Qué pasa en la práctica |
|
|
30
|
+
|---|---|
|
|
31
|
+
| **El modelo decide cuándo terminar** | Dice «listo, terminado» con la mitad del trabajo sin hacer, y la sesión para ahí. |
|
|
32
|
+
| **El trabajo se hace lineal** | Todo en un solo contexto: un agente, una tarea, un hilo. Los subagentes existen pero se desaprovechan. |
|
|
33
|
+
| **Nadie orquesta** | No hay un plan, no hay prioridad, no hay un dueño por tarea. |
|
|
34
|
+
| **El contexto se pierde** | Hay que repetir lo que ya se dijo, lo ya se decidió y lo ya se encontró. |
|
|
35
|
+
| **Los subagentes son islands** | No se piden ayuda, no se coordinan, no se benefician del trabajo del otro. |
|
|
36
|
+
|
|
37
|
+
Una skill de markdown no arregla ninguna de las cinco. Un sistema con reglas escritas en código, no: porque **no dependen de que el modelo tenga ganas de seguirlas**.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Qué es
|
|
42
|
+
|
|
43
|
+
**AI-OFFICE** es un plugin de OpenCode que convierte la orquestación en **código ejecutable**, no en instrucciones.
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
Skill → "Hacé lo que corresponda" → depende de que el modelo quiera
|
|
47
|
+
Plugin → máquina de estados + hooks → no depende de la buena voluntad
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Cuatro piezas lo hacen posible:
|
|
51
|
+
|
|
52
|
+
**1. Un hook que no deja que la sesión muera.** Cuando la sesión queda `idle` y quedan cards sin terminar, el motor manda un prompt sintético y la corrida sigue. El modelo **no puede declarar que terminó**: el estado lo dice.
|
|
53
|
+
|
|
54
|
+
**2. Un guard que no deja editar fuera de scope.** `tool.execute.before` intercepta cada `edit`/`write` y lo rechaza si el archivo está fuera del scope de la card. "No toques eso" deja de ser una sugerencia.
|
|
55
|
+
|
|
56
|
+
**3. Gates que se ejecutan, no se opinan.** Build, type check, lint, tests, escaneo de secretos y diff review. El motor corre los comandos, lee los exit codes y decide. La salida real queda pegada en el artefacto.
|
|
57
|
+
|
|
58
|
+
**4. Invariantes que el código garantiza.** `gate_required_before_done` y `reviewer_neq_owner` no son recomendaciones: `assertInvariants` lanza y la transición no ocurre.
|
|
59
|
+
|
|
60
|
+
Y una quinta, que es lo que hace confiable todo lo demás:
|
|
61
|
+
|
|
62
|
+
**5. El roster se arma según el objetivo.** Los roles salen del catálogo real de agency-agents (**306 agentes, 25 divisiones**), matcheados contra lo que la card pide. Y cada agente recibe un **briefing** de 10 secciones con lo que sabe, lo que no, y qué tiene que entregar.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Arquitectura
|
|
67
|
+
|
|
68
|
+
Cuatro capas. De abajo hacia arriba.
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
┌──────────────────────────────────────────────────────────────────────┐
|
|
72
|
+
│ CAPA 3 — COORDINACIÓN │
|
|
73
|
+
│ ┌──────────────┐ ┌──────────────┐ ┌───────────────────────────┐ │
|
|
74
|
+
│ │ COMANDANTE │ │ ARQUITECTO │ │ INTEGRADOR │ │
|
|
75
|
+
│ │ scope │ │ cross-cutting│ │ DAG · leases · gates │ │
|
|
76
|
+
│ │ objetivo │ │ patrones │ │ presupuesto · anti-deadlock│ │
|
|
77
|
+
│ │ prioridad │ │ contratos │ │ │ │
|
|
78
|
+
│ └───────┬──────┘ └──────┬───────┘ └────────────┬──────────────┘ │
|
|
79
|
+
│ │ │ │ │
|
|
80
|
+
│ ════════╪════════════════╪═══════════════════════╪═════════════ │
|
|
81
|
+
│ ▼ ▼ ▼ │
|
|
82
|
+
│ CAPA 2 — BUS DE MENSAJES (append-only sobre disco) │
|
|
83
|
+
│ bus/<canal>/<seq>-<msgid>.json │
|
|
84
|
+
│ inbox/<agentId>/ · roster.json · leases/<taskId>.json │
|
|
85
|
+
│ canales: coord-command │ coord-arch │ coord-flow │ peer │
|
|
86
|
+
│ │ │
|
|
87
|
+
│ ▼ │
|
|
88
|
+
│ CAPA 1 — ESPECIALISTAS (ficha normalizada) │
|
|
89
|
+
│ E1 E2 E3 E4 E5 E6 E7 E8 E9 E10 │
|
|
90
|
+
│ briefing: ficha.md · misiones.md · protocolo.md · bus.md │
|
|
91
|
+
│ │ │
|
|
92
|
+
│ ▼ │
|
|
93
|
+
│ CAPA 0 — ROSTER / FICHAS (resolución de roles) │
|
|
94
|
+
│ agency-agents/<division>/*.md → ficha │ catálogo < 60% → factory│
|
|
95
|
+
└──────────────────────────────────────────────────────────────────────┘
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Capa 0 — De dónde sale la especialidad
|
|
101
|
+
|
|
102
|
+
El repo de agency-agents tiene **306 agentes en 25 divisiones**, y cada uno trae un briefing de 10 secciones. La especialidad real **no está en el `description`** del frontmatter: está en el cuerpo.
|
|
103
|
+
|
|
104
|
+
Un agente de statistics declara:
|
|
105
|
+
- `description`: *"Expert in quantitative research methodology..."*
|
|
106
|
+
- **Core Mission** → *Pressure-Test Quantitative Claims*, *Design Sound Studies*
|
|
107
|
+
- **Technical Deliverables** → *Claim Interrogation Framework*, *Effect Size Report*
|
|
108
|
+
- **Advanced Capabilities** → *Causal Inference*, *Experimental Design*
|
|
109
|
+
|
|
110
|
+
La `description` dice de qué se ocupa. El cuerpo dice **en qué es exactamente bueno**. Un matcher que solo mira la `description` no puede distinguir a un metodólogo de un analista de datos — aunque uno de los dos sea exactamente lo que la card necesita.
|
|
111
|
+
|
|
112
|
+
Por eso el parser extrae las secciones del cuerpo, y el score pondera cada señal por lo que aporta:
|
|
113
|
+
|
|
114
|
+
| Señal | Peso | Por qué |
|
|
115
|
+
|---|---|---|
|
|
116
|
+
| **Identidad** (slug + nombre + `description` + `vibe`) | 35% | Las dos últimas son **líneas curadas** que dicen de qué se ocupa el agente. El cuerpo son 300 líneas donde cualquier palabra genérica aparece |
|
|
117
|
+
| **Misión, overlap literal** | 28% | Lo que el agente dice que sabe hacer, con sus palabras |
|
|
118
|
+
| **Misión, overlap conceptual** | 18% | Puentea sinónimos con una taxonomía de 26 dominios |
|
|
119
|
+
| **División** | 10% | Una card de e2e testing va a la división `testing`, no a `engineering` |
|
|
120
|
+
| **Cuerpo completo** | 5% | Ruido: "review" aparece en todos |
|
|
121
|
+
|
|
122
|
+
**Sinónimos por capability**, no por concepto. La taxonomía agrupa; los sinónimos no. `"e2e"` es `"end-to-end"`, pero `"e2e"` **no** es `"playwright"`. Esa distinción separa a un especialista de un generalista que menciona la tool de pasada.
|
|
123
|
+
|
|
124
|
+
**Lematización**: `"testing"` ~ `"test"`, `"validation"` ~ `"validate"`. El Commander pide capabilities en sus palabras y los agentes usan las suyas.
|
|
125
|
+
|
|
126
|
+
**Focus de misión**: qué fracción de las misiones declaradas del agente tiene que ver con el objetivo. Un `Test Automation Engineer` tiene las 5 en el dominio → focus 1. Un PDF engine con *una* misión sobre "Playwright Browser Context Pools" y las otras 7 sobre PDF → focus 0.12. **Este es el discriminante real.**
|
|
127
|
+
|
|
128
|
+
El score final combina: cobertura (35%) + recall (30%) + focus (25%) + F1 (10%).
|
|
129
|
+
|
|
130
|
+
Resolución real contra el catálogo:
|
|
131
|
+
|
|
132
|
+
| Objetivo | Primario | Cobertura |
|
|
133
|
+
|---|---|---|
|
|
134
|
+
| Pipeline de datos con ingesta y agregación | `engineering-data-engineer` | 76% |
|
|
135
|
+
| Migración de Postgres con partitioning | `engineering-gaussdb-expert` | 60% |
|
|
136
|
+
| Formulario de login en React con accesibilidad | `engineering-uswds-developer` | 56% |
|
|
137
|
+
| CI/CD con Docker y Kubernetes | `engineering-devops-automator` | 53% |
|
|
138
|
+
| Agente de GIS y cartografía | `gis-web-gis-developer` | 52% |
|
|
139
|
+
| Tests e2e con Playwright | `testing-test-automation-engineer` | 47% |
|
|
140
|
+
| Seguridad de un endpoint API | `testing-api-tester` | 42% |
|
|
141
|
+
|
|
142
|
+
Reproducible: `npx tsx scripts/demo-roles.ts`
|
|
143
|
+
|
|
144
|
+
### El panel: primario + complementos
|
|
145
|
+
|
|
146
|
+
Un objetivo casi nunca es de un solo dominio. *"Formulario de login en React con validación y accesibilidad"* necesita un frontend (que no sabe de ARIA) **y** un especialista en accesibilidad (que no sabe de React).
|
|
147
|
+
|
|
148
|
+
Si el sistema elige uno solo, deja huecos. Si elige varios sin criterio, duplica capacidad. El panel resuelve las dos cosas:
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
OBJETIVO: Implementar un formulario de login en React con validación y accesibilidad
|
|
152
|
+
PIDE: react, form, validation, accessibility, frontend
|
|
153
|
+
|
|
154
|
+
PRIMARIO 56% engineering/engineering-uswds-developer
|
|
155
|
+
"USWDS Developer"
|
|
156
|
+
cubre: react, css, form, accessibility
|
|
157
|
+
|
|
158
|
+
+ COMPLEM. 39% engineering/engineering-section-508-specialist
|
|
159
|
+
"Section 508 Accessibility Specialist"
|
|
160
|
+
aporta: form, accessibility
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
El primario se lleva la card. Los complementos son los agentes que cubren **lo que el primario no cubre**, y son sus pares naturales en el bus. Y lo que nadie cubre **se reporta explícitamente**: es la señal de que la card debería partirse en dos.
|
|
164
|
+
|
|
165
|
+
### La ficha: un solo schema
|
|
166
|
+
|
|
167
|
+
Venga el rol de `agency-agents` o de la factory, se materializa en la **misma** ficha. Esa unificación es lo que permite que el bus, el matching de ayuda, la rotación y los coordinadores no necesiten saber el origen de nada.
|
|
168
|
+
|
|
169
|
+
```yaml
|
|
170
|
+
id: e-frontend
|
|
171
|
+
role: frontend-developer
|
|
172
|
+
source:
|
|
173
|
+
kind: agency-agents
|
|
174
|
+
division: engineering
|
|
175
|
+
path: engineering/frontend-developer.md
|
|
176
|
+
vibe: "Pixel obsessive, allergic to dead CSS."
|
|
177
|
+
missions: ["Create Modern Web Applications", "Optimize Performance and UX"]
|
|
178
|
+
deliverables: ["Component Library", "Design Token System"]
|
|
179
|
+
rules: ["Never ship a component without a test", "..."]
|
|
180
|
+
capabilities: [react, component, ui, web, accessibility, performance, ...]
|
|
181
|
+
scope:
|
|
182
|
+
mayTouch: ["src/components/**", "src/hooks/**"]
|
|
183
|
+
forbidden: ["src/server/**", "**/*.env*"]
|
|
184
|
+
can_delegate: true
|
|
185
|
+
delegate_budget: 2
|
|
186
|
+
peers: [e-architect, e-qa, e-security]
|
|
187
|
+
must_not:
|
|
188
|
+
- "no editar archivos fuera de may_touch"
|
|
189
|
+
- "no aprobar tu propio trabajo"
|
|
190
|
+
on_finish: idle-join
|
|
191
|
+
escalate_to: coordinator-flow
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### La factory: el mismo briefing, sin catálogo
|
|
195
|
+
|
|
196
|
+
Un rol fabricado tiene que ser **indistinguible en calidad** de uno del catálogo. Si el catálogo tiene agentes con briefing de 10 secciones, uno fabricado con un prompt de 5 líneas se nota: es monótono, no tiene criterio propio, y el resto de la malla lo trata como de segunda.
|
|
197
|
+
|
|
198
|
+
Por eso la factory emite **exactamente la misma estructura**: Identity & Memory, Core Mission, Critical Rules, Technical Deliverables, Workflow Process, Communication Style, Learning & Memory, Success Metrics, Advanced Capabilities. El parser no distingue el origen.
|
|
199
|
+
|
|
200
|
+
Y lo importante: el prompt fabricado **no es genérico**. Lleva las capabilities concretas, el scope, el objetivo de la corrida, los modos de fallo del rol y el contexto de la card.
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## El equipo se arma
|
|
205
|
+
|
|
206
|
+
El flujo completo es: **doy un objetivo → el jefe lo divide → crea los subagentes → arrancan a trabajar juntos**.
|
|
207
|
+
|
|
208
|
+
`oficina_dispatch` es el momento en que eso pasa:
|
|
209
|
+
|
|
210
|
+
1. **El Comandante descompone** el objetivo en cards (`oficina_plan`). Cada card dice qué se produce, cómo se sabe que está bien, y qué archivos puede tocar.
|
|
211
|
+
2. **El dispatch resuelve los roles.** Para cada card busca en el catálogo de agency-agents el agente que mejor encaja, calcula el **panel** (primario + complementos), y si la cobertura queda baja **fabrica** el rol con la card como contexto.
|
|
212
|
+
3. **Materializa los agentes** en `.opencode/agents/` del proyecto, cada uno con su briefing de 10 secciones y su prompt de especialidad. Quedan versionados con el repo: la próxima sesión ya están registrados.
|
|
213
|
+
4. **Los arranca en paralelo** vía `session.prompt` con una parte `subtask` — la vía que OpenCode provee para crear subagentes reales.
|
|
214
|
+
|
|
215
|
+
El resultado:
|
|
216
|
+
|
|
217
|
+
```
|
|
218
|
+
▸ formulario de login en React con validación y accesibilidad
|
|
219
|
+
→ `oficina-engineering-uswds-developer` card T-00
|
|
220
|
+
+ `oficina-testing-api-tester` par libre aporta: validation
|
|
221
|
+
→ `oficina-engineering-section-508-specialist` card T-01
|
|
222
|
+
+ `oficina-testing-accessibility-auditor` par libre aporta: aria
|
|
223
|
+
→ `oficina-testing-test-automation-engineer` card T-02
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Los **pares libres** son los complementos: están en la mesa, pueden recibir un pedido de ayuda, y pueden reclamar una card que quedó sin dueño.
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## La mesa
|
|
231
|
+
|
|
232
|
+
Los subagentes son procesos aislados: no se ven, no saben quién más trabaja, no saben a quién preguntarle. La **mesa** es la vista compartida — vive en disco, así que todos la leen sin memoria compartida.
|
|
233
|
+
|
|
234
|
+
`oficina_mesa` responde:
|
|
235
|
+
|
|
236
|
+
- **Quién está trabajando en qué** — y qué archivos toca cada uno
|
|
237
|
+
- **Quién está libre** y puede recibir un pedido de ayuda
|
|
238
|
+
- **Qué cards no tienen owner** — reclamables proactivamente
|
|
239
|
+
- **Qué ayuda sigue sin responder** y **qué preguntas están abiertas**
|
|
240
|
+
|
|
241
|
+
Y cuando la sesión queda `idle`, el hook de auto-continuación inyecta esa foto en el prompt del agente que arranca. Sin esto, el peer mesh es un bus al que nadie mira.
|
|
242
|
+
|
|
243
|
+
### El ciclo de un agente
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
DEPLOYED → WORKING → (terminó) → IDLE_AVAILABLE → (ayuda) → WORKING
|
|
247
|
+
↓ ↑
|
|
248
|
+
BLOCKED ──→ IDLE_AVAILABLE ──────────────────┘
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Cuando un agente termina, **ofrece ayuda** para lo que sigue. No queda esperando.
|
|
252
|
+
|
|
253
|
+
### Hablar por el bus
|
|
254
|
+
|
|
255
|
+
El que recibe un pedido de ayuda **hace su propio triage** — no acepta por cortesía:
|
|
256
|
+
|
|
257
|
+
1. ¿Entiendo la tarea? → si no, `HELP_DECLINE` + explicar
|
|
258
|
+
2. ¿Está en mi scope? → si no, `HELP_DECLINE` + sugerir a quién
|
|
259
|
+
3. ¿Puedo hacerlo ya sin romper mi card? → si no, `HELP_DECLINE` + "cuando termine"
|
|
260
|
+
4. ¿Tengo el contexto? → si no, `HELP_QUESTION` por el bus
|
|
261
|
+
5. ¿Vale la pena? → si no, `HELP_DECLINE` + alternativa
|
|
262
|
+
6. ¿Tengo presupuesto? → si no, `HELP_DECLINE` + "sin presupuesto"
|
|
263
|
+
|
|
264
|
+
**Anti-deadlock**: máximo 2 saltos en una cadena de ayuda; después escala al Integrador.
|
|
265
|
+
**Anti-monotonía**: si el par A↔B ya se ayudaron 2 veces, el tercer pedido va a un tercero.
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## Capa 1 — El briefing: qué sabe cada agente antes de arrancar
|
|
270
|
+
|
|
271
|
+
Un subagente arranca con el contexto vacío. Si el Commander le pasa un prompt suelta (*"implementá el login"*), el agente tiene que adivinar: qué sabe del proyecto, qué espera de él, qué puede tocar, a quién preguntarle si se traba, y qué tiene que entregar. Adivinar es la fuente de todo el trabajo que se repite y de los agentes que se salen del scope.
|
|
272
|
+
|
|
273
|
+
El briefing es el contrato de información. Tiene **orden fijo** y **mínimo verificable**.
|
|
274
|
+
|
|
275
|
+
### El orden
|
|
276
|
+
|
|
277
|
+
La información llega de lo más estable a lo más cambiante. Un agente lee de arriba hacia abajo y siempre tiene lo que necesita para el paso siguiente.
|
|
278
|
+
|
|
279
|
+
| # | Sección | Qué le dice |
|
|
280
|
+
|---|---|---|
|
|
281
|
+
| 1 | **Quién sos** | Rol, especialidad de origen, de dónde viene tu briefing, tu vibe |
|
|
282
|
+
| 2 | **Tu misión** | La card exacta, los criterios de aceptación numerados, el control de calidad |
|
|
283
|
+
| 3 | **Lo que ya sabés** | Decisiones tomadas, restricciones descubiertas, piezas construidas, trampas |
|
|
284
|
+
| 4 | **Lo que NO sabés** | Los huecos explícitos y **a quién preguntarle** por cada uno |
|
|
285
|
+
| 5 | **Tu territorio** | Qué podés tocar, qué no, y qué prohibiciones trae tu ficha |
|
|
286
|
+
| 6 | **Cómo trabajás** | El ciclo, cuándo pedir ayuda, cuándo delegar, cuándo escalar |
|
|
287
|
+
| 7 | **Tu canal** | Dónde dejás y encontrás mensajes, formato, TTLs, cómo acusar recibo |
|
|
288
|
+
| 8 | **Qué se espera de vos** | Los entregables exactos: `handoff.md` y `evidence.md` |
|
|
289
|
+
| 9 | **Lo que no podés hacer** | Las prohibiciones duras. Están en código, no en tu criterio |
|
|
290
|
+
| 10 | **Cómo se mide que terminaste** | Las tres condiciones de terminado |
|
|
291
|
+
|
|
292
|
+
### El mínimo
|
|
293
|
+
|
|
294
|
+
Las diez secciones son **requeridas**. Si alguna queda vacía, el briefing se marca `INCOMPLETO` y **el agente no arranca**. Un briefing a medio llenar produce un agente a medio informado, que es peor que no tener agente.
|
|
295
|
+
|
|
296
|
+
### Los cuatro artefactos
|
|
297
|
+
|
|
298
|
+
El briefing se materializa en cuatro archivos en disco, porque un agente no "recuerda" un prompt largo: lo lee.
|
|
299
|
+
|
|
300
|
+
```
|
|
301
|
+
.oficina/fichas/<agentId>/
|
|
302
|
+
├── ficha.md ← Quién sos
|
|
303
|
+
├── misiones.md ← Tu misión · lo que ya sabés · lo que NO sabés
|
|
304
|
+
├── protocolo.md ← Cómo trabajás · tu territorio · qué se espera · límites · éxito
|
|
305
|
+
├── bus.md ← Tu canal
|
|
306
|
+
└── briefing.md ← el documento completo, para lectura humana
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
**El agente no arranca sin `misiones.md`. Nunca.**
|
|
310
|
+
|
|
311
|
+
### El ledger: lo que ya sabés
|
|
312
|
+
|
|
313
|
+
La sección 3 se alimenta del **contexto acumulado** de la corrida (`.oficina/context.md`), que registra decisiones, restricciones, piezas construidas y trampas. Es lo que evita que un agente repita lo que ya se decidió — y lo que convierte al bus en algo más que un tablón de avisos.
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
## Capa 2 — El bus
|
|
318
|
+
|
|
319
|
+
Los subagentes de OpenCode son **procesos aislados sin memoria compartida**. No pueden hablarse de facto. La solución coherente es un bus append-only sobre disco.
|
|
320
|
+
|
|
321
|
+
```json
|
|
322
|
+
{
|
|
323
|
+
"id": "m-0042",
|
|
324
|
+
"seq": 42,
|
|
325
|
+
"from": "e-frontend",
|
|
326
|
+
"to": ["e-architect"],
|
|
327
|
+
"kind": "HELP_REQUEST",
|
|
328
|
+
"subject": "T-03",
|
|
329
|
+
"body": {
|
|
330
|
+
"need": "convertir este componente a streaming SSR",
|
|
331
|
+
"why_me": "tengo el contexto del componente pero no el del server"
|
|
332
|
+
},
|
|
333
|
+
"reply_to": null,
|
|
334
|
+
"in_reply_to": null,
|
|
335
|
+
"ttl_sec": 900,
|
|
336
|
+
"created_at": "2026-10-21T15:04:00Z",
|
|
337
|
+
"priority": "normal",
|
|
338
|
+
"idempotency_key": "help:T-03:e-frontend:a1b2c3",
|
|
339
|
+
"hops": 0
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
| Garantía | Implementación | Por qué importa |
|
|
344
|
+
|---|---|---|
|
|
345
|
+
| **Append-only** | Los mensajes nunca se borran; `seq` es monótono por canal | El log es la auditoría |
|
|
346
|
+
| **Escritura atómica** | `.tmp` + `rename` | Nunca hay un JSON partido en el read |
|
|
347
|
+
| **Ack** | `inbox/<agentId>/<msgid>.ack` | El emisor sabe quién leyó |
|
|
348
|
+
| **TTL** | Vencido = invisible para lectores nuevos, sigue en el log | Un pedido viejo no bloquea nada |
|
|
349
|
+
| **Idempotency key** | Mismo key = mismo mensaje | Evita el storm de «¿me ayudás?» |
|
|
350
|
+
| **Broadcast** | `to: ["*"]` al pool | El primero que acepta toma el lease |
|
|
351
|
+
| **Anti-deadlock** | `hops` máximo 2 | Una cadena de ayuda no se enquista |
|
|
352
|
+
|
|
353
|
+
### El que recibe hace su propio triage
|
|
354
|
+
|
|
355
|
+
No acepta por cortesía. Seis preguntas, en orden:
|
|
356
|
+
|
|
357
|
+
1. ¿Entiendo la tarea? → si no, `HELP_DECLINE` + explicar
|
|
358
|
+
2. ¿Está en mi scope? → si no, `HELP_DECLINE` + sugerir a quién
|
|
359
|
+
3. ¿Puedo hacerlo **ya** sin romper mi card? → si no, `HELP_DECLINE` + «cuando termine»
|
|
360
|
+
4. ¿Tengo el contexto? → si no, `HELP_QUESTION` por el bus
|
|
361
|
+
5. ¿Vale la pena? → si no, `HELP_DECLINE` + alternativa
|
|
362
|
+
6. ¿Tengo presupuesto? → si no, `HELP_DECLINE` + «sin presupuesto»
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## Los 4 hooks
|
|
367
|
+
|
|
368
|
+
Acá es donde el protocolo deja de ser una intención.
|
|
369
|
+
|
|
370
|
+
| Hook | Qué hace | Por qué importa |
|
|
371
|
+
|---|---|---|
|
|
372
|
+
| `event` → `session.idle` | Si queda trabajo, manda un prompt sintético y la corrida sigue | La oficina trabaja sola. El modelo no declara el fin: el estado. |
|
|
373
|
+
| `tool.execute.before` | Bloquea escrituras fuera del scope de la card | «No toques eso» pasa de instrucción a restricción |
|
|
374
|
+
| `experimental.chat.system.transform` | Inyecta el estado de la oficina en cada llamada | El agente no repregunta lo que ya sabe ni repite trabajo |
|
|
375
|
+
| `experimental.session.compacting` | Reinyecta el estado **leído del disco** al compactar | Sin esto, la oficina se olvida a mitad de camino |
|
|
376
|
+
|
|
377
|
+
El hook de auto-continuación tiene guardas duras: solo reanuda si queda trabajo real, tope de 25 auto-reanudaciones por sesión, cooldown de 1,5 s entre reanudaciones, y nunca reanuda una corrida `paused`, `blocked` o `delivered`.
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## Los 6 gates
|
|
382
|
+
|
|
383
|
+
De la skill `verification-loop`. Se ejecutan de verdad; el motor interpreta los exit codes.
|
|
384
|
+
|
|
385
|
+
| # | Fase | Node | Python | Go | Rust |
|
|
386
|
+
|---|---|---|---|---|---|
|
|
387
|
+
| 1 | Build | `npm run build` | `python -m build` | `go build ./...` | `cargo build` |
|
|
388
|
+
| 2 | Type check | `npm run typecheck` | `mypy` | `go vet ./...` | `cargo check` |
|
|
389
|
+
| 3 | Lint | `npm run lint` | `ruff` | `golangci-lint` | `cargo clippy` |
|
|
390
|
+
| 4 | Tests | `npm run test` | `pytest` | `go test` | `cargo test` |
|
|
391
|
+
| 5 | Secretos | escaneo en código | ← | ← | ← |
|
|
392
|
+
| 6 | Diff review | `git diff --stat` | ← | ← | ← |
|
|
393
|
+
|
|
394
|
+
**Reglas de honestidad** — no negociables:
|
|
395
|
+
|
|
396
|
+
- Nunca `PASS` en una fase que no se corrió → `NOT RUN` y eso hace fallar el gate
|
|
397
|
+
- Nunca output inventado
|
|
398
|
+
- `N/A` siempre con motivo explícito
|
|
399
|
+
- Una fase aplicable en `FAIL` = gate `FAIL`. No se promedia
|
|
400
|
+
- El escaneo de secretos corre **en TypeScript**, no shelling out a `grep` (que no existe en Windows), y **redacta** el valor: un log de gate que imprime la credencial completa la acaba de filtrar
|
|
401
|
+
|
|
402
|
+
---
|
|
403
|
+
|
|
404
|
+
## Invariantes duros
|
|
405
|
+
|
|
406
|
+
Los implementa el código. Un prompt puede pedirle al modelo que sea cuidadoso; el motor no puede romperlos.
|
|
407
|
+
|
|
408
|
+
| Invariante | Valor |
|
|
409
|
+
|---|---|
|
|
410
|
+
| `gate_required_before_done` | `true` — sin gates en `PASS` no hay `DONE` |
|
|
411
|
+
| `reviewer_neq_owner` | `true` — no hay auto-aprobación |
|
|
412
|
+
| `max_concurrent_agents` | 10 base · 14 hard cap |
|
|
413
|
+
| `max_spawn_depth` | 2 niveles bajo el coordinador |
|
|
414
|
+
| `max_spawns_total` | 20 por corrida |
|
|
415
|
+
| `max_help_chain_hops` | 2 — anti-deadlock |
|
|
416
|
+
| `max_messages_per_agent` | 150 — anti-spam |
|
|
417
|
+
| `msg_ttl_sec` | 900 |
|
|
418
|
+
| `lease_ttl_sec` | 1800 — con heartbeat; si el agente muere, expira |
|
|
419
|
+
| `heartbeat_sec` | 60 |
|
|
420
|
+
| `max_review_rejections` | 3 → escalación |
|
|
421
|
+
| `max_cards` · `max_agent_calls` | 20 · 40 por corrida |
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## Los 3 coordinadores
|
|
426
|
+
|
|
427
|
+
Un solo coordinador es cuello de botella y punto único de falla.
|
|
428
|
+
|
|
429
|
+
| | COMANDANTE | ARQUITECTO | INTEGRADOR |
|
|
430
|
+
|---|---|---|---|
|
|
431
|
+
| **Dominio** | Scope, objetivo, prioridades | Decisiones técnicas cross-cutting | DAG, leases, gates, calidad |
|
|
432
|
+
| **Responde** | ¿esto está en el objetivo? | ¿qué patrón usamos? | ¿quién tiene T-07? ¿pasó el gate? |
|
|
433
|
+
| **Detecta** | Scope creep, requisitos no cubiertos | Drift arquitectónico, decisiones inconsistentes | Deadlock, starvation, gates rojos, doble-claim |
|
|
434
|
+
| **Nunca** | Diseñar ni revisar código | Escribir código | Escribir código |
|
|
435
|
+
|
|
436
|
+
**Arbitraje**: scope → Comandante. Patrón/contrato → Arquitecto. Flujo/lease/gate → Integrador. Si cruza dos dominios, el Arquitecto decide lo técnico con visto bueno del Comandante; si el Integrador detecta deadlock, reparte y avisa.
|
|
437
|
+
|
|
438
|
+
Las preguntas de un especialista al coordinador **van por el bus**, no interrumpen al humano. Al usuario solo llega lo que ningún coordinador puede resolver.
|
|
439
|
+
|
|
440
|
+
---
|
|
441
|
+
|
|
442
|
+
## Los 13 tools
|
|
443
|
+
|
|
444
|
+
Trece, no treinta. Cada tool que se suma es ruido en el contexto de todos los agentes.
|
|
445
|
+
|
|
446
|
+
| Tool | Qué hace |
|
|
447
|
+
|---|---|
|
|
448
|
+
| `oficina_plan` | Guarda el plan de cards. El modelo propone; el motor valida y rutea owner/reviewer |
|
|
449
|
+
| `oficina_dispatch` | **Arma el equipo**: resuelve los roles, materializa los agentes con su briefing y los arranca en paralelo |
|
|
450
|
+
| `oficina_mesa` | **La mesa**: quién trabaja en qué, quién está libre, qué ayuda espera respuesta, qué cards no tienen owner |
|
|
451
|
+
| `oficina_board` | Estado: cards, owners, agentes libres, agentes sin heartbeat, presupuesto |
|
|
452
|
+
| `oficina_assign` | Toma trabajo — la próxima card, o claim proactivo de una puntual |
|
|
453
|
+
| `oficina_submit` | Entrega handoff + evidencia real → pasa a `REVIEW` |
|
|
454
|
+
| `oficina_review` | Registra el veredicto. El motor **rechaza** que el owner se auto-revise |
|
|
455
|
+
| `oficina_gate` | Corre los 6 gates. Exit codes reales, no opinión |
|
|
456
|
+
| `oficina_bus` | Bus: `send` · `receive` · `ack` · `help` |
|
|
457
|
+
| `oficina_spawn` | Pide desplegar un agente. El Integrador aprueba o rechaza |
|
|
458
|
+
| `oficina_escalate` | Escala al usuario con 2-3 opciones concretas y recomendación |
|
|
459
|
+
| `oficina_ficha` | Resuelve un rol a ficha y **despliega** el agente con su briefing |
|
|
460
|
+
| `oficina_brief` | Muestra el briefing y el **panel de roles** sin desplegar: decisión del Comandante |
|
|
461
|
+
|
|
462
|
+
---
|
|
463
|
+
|
|
464
|
+
## Los 7 agentes
|
|
465
|
+
|
|
466
|
+
| Agente | Rol | Aprueba? |
|
|
467
|
+
|---|---|---|
|
|
468
|
+
| `oficina-commander` | Scope, objetivo, prioridades | No revisa técnico |
|
|
469
|
+
| `oficina-architect` | Decisiones técnicas cross-cutting | No revisa técnico |
|
|
470
|
+
| `oficina-integrator` | DAG, leases, gates, presupuesto | No revisa técnico |
|
|
471
|
+
| `oficina-critico` | Revisión con confianza calibrada (umbral 80) | Sí |
|
|
472
|
+
| `oficina-guardian` | Secretos, inyecciones, deps, auth | Sí |
|
|
473
|
+
| `oficina-verificador` | Ejecuta build/typecheck/lint/test | Sí |
|
|
474
|
+
| `oficina-director` | Dirección de la oficina v1 (fallback) | No |
|
|
475
|
+
|
|
476
|
+
---
|
|
477
|
+
|
|
478
|
+
## Instalación
|
|
479
|
+
|
|
480
|
+
### Desde npm
|
|
481
|
+
|
|
482
|
+
```bash
|
|
483
|
+
npm install -g oficina-opencode
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
### Local (desarrollo)
|
|
487
|
+
|
|
488
|
+
```bash
|
|
489
|
+
git clone https://github.com/SOCRAMbt/AI-OFFICE.git
|
|
490
|
+
cd AI-OFFICE
|
|
491
|
+
npm install
|
|
492
|
+
npm run build
|
|
493
|
+
|
|
494
|
+
cd ~/.config/opencode
|
|
495
|
+
npm install file:/ruta/a/AI-OFFICE
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
Y el entry point en `~/.config/opencode/plugins/oficina.js`:
|
|
499
|
+
|
|
500
|
+
```js
|
|
501
|
+
export { OficinaPlugin, default } from "oficina-opencode"
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
### Configuración
|
|
505
|
+
|
|
506
|
+
`~/.config/opencode/opencode.json`:
|
|
507
|
+
|
|
508
|
+
```json
|
|
509
|
+
{
|
|
510
|
+
"experimental": {
|
|
511
|
+
"subagent_depth": 3
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
`subagent_depth: 3` es **obligatorio**: con el default (`1`) los subagentes no pueden delegar a otros subagentes, y la malla no arranca.
|
|
517
|
+
|
|
518
|
+
### Los assets del protocolo
|
|
519
|
+
|
|
520
|
+
```bash
|
|
521
|
+
oficina install --target ~/.config/opencode
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
Instala la skill, los 7 agentes, el comando y el entry point del plugin.
|
|
525
|
+
|
|
526
|
+
---
|
|
527
|
+
|
|
528
|
+
## Uso
|
|
529
|
+
|
|
530
|
+
Desde la raíz del proyecto:
|
|
531
|
+
|
|
532
|
+
```text
|
|
533
|
+
/oficina Agregar autenticación JWT con refresh rotation
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
El comando carga la skill `oficina`, el plugin registra las tools, y la corrida arranca sola.
|
|
537
|
+
|
|
538
|
+
El ciclo:
|
|
539
|
+
|
|
540
|
+
```
|
|
541
|
+
OBJETIVO → PLAN (Comandante)
|
|
542
|
+
↓
|
|
543
|
+
STANDUP — la card con deps satisfechas
|
|
544
|
+
↓
|
|
545
|
+
TRABAJO (owner) → handoff.md + evidence.md
|
|
546
|
+
↓
|
|
547
|
+
AUTO-REVIEW (oficina-critico, ≠ owner)
|
|
548
|
+
↓
|
|
549
|
+
GATES (oficina-verificador + oficina-guardian)
|
|
550
|
+
↓
|
|
551
|
+
┌────────┴────────┐
|
|
552
|
+
DONE REWORK → REPLAN → ESCALADA
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
---
|
|
556
|
+
|
|
557
|
+
## CLI — operación
|
|
558
|
+
|
|
559
|
+
```bash
|
|
560
|
+
oficina init [objetivo] # Crea .oficina/ en el proyecto
|
|
561
|
+
oficina status # Cards, agentes, presupuesto, escalaciones
|
|
562
|
+
oficina board # Tablero Kanban
|
|
563
|
+
oficina bus # Estadísticas del bus
|
|
564
|
+
oficina audit T-03 # Artefactos, gates y review de una card
|
|
565
|
+
oficina verify # Corre los gates de las cards en review
|
|
566
|
+
oficina install --target <dir> # Instala skill, agentes y comando
|
|
567
|
+
oficina catalog --agency-root <path> # Lista el catálogo de roles disponible
|
|
568
|
+
oficina reset --force # Abandona la corrida
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
---
|
|
572
|
+
|
|
573
|
+
## Estructura de la corrida
|
|
574
|
+
|
|
575
|
+
```
|
|
576
|
+
<proyecto>/.oficina/
|
|
577
|
+
├── board.md # Kanban legible por humanos
|
|
578
|
+
├── state.json # estado legible por máquinas — retoma tras reinicio
|
|
579
|
+
├── context.md # ledger de conocimiento acumulado
|
|
580
|
+
├── roster.json # quién existe, qué puede, qué está haciendo
|
|
581
|
+
├── config.json
|
|
582
|
+
├── bus/
|
|
583
|
+
│ ├── coord-command/0001-m-xxxxxxxx.json
|
|
584
|
+
│ ├── coord-arch/
|
|
585
|
+
│ ├── coord-flow/
|
|
586
|
+
│ └── peer/
|
|
587
|
+
├── inbox/<agentId>/ # acks
|
|
588
|
+
├── leases/<taskId>.json
|
|
589
|
+
├── fichas/<agentId>/ # los 4 artefactos del briefing + briefing.md
|
|
590
|
+
└── artifacts/<cardId>/
|
|
591
|
+
├── handoff.md # qué hizo el owner
|
|
592
|
+
├── evidence.md # salida real de los comandos
|
|
593
|
+
├── review.md # veredicto del crítico
|
|
594
|
+
├── gates.md # PASS/FAIL técnico y seguridad
|
|
595
|
+
└── decision.md # decisión del director
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
El formato de artefactos es compatible con OFICINA v1 (la skill). El plugin **migra el estado v1 automáticamente**: objetivo, cards y resultados se conservan.
|
|
599
|
+
|
|
600
|
+
---
|
|
601
|
+
|
|
602
|
+
## Código
|
|
603
|
+
|
|
604
|
+
24 archivos TypeScript · 14 módulos en `core/` · 1 toolset · 4 hooks.
|
|
605
|
+
|
|
606
|
+
```
|
|
607
|
+
src/
|
|
608
|
+
├── index.ts # entry del plugin: tools + hooks
|
|
609
|
+
├── cli.ts # CLI de operación
|
|
610
|
+
├── core/
|
|
611
|
+
│ ├── types.ts # el schema único del sistema
|
|
612
|
+
│ ├── state.ts # máquina de estados + invariantes + migración v1
|
|
613
|
+
│ ├── bus.ts # bus append-only con TTL/ack/idempotencia
|
|
614
|
+
│ ├── roster.ts # quién existe + pool idle
|
|
615
|
+
│ ├── leases.ts # leases con TTL y heartbeat
|
|
616
|
+
│ ├── catalog.ts # escaneo de agency-agents + matching + panel
|
|
617
|
+
│ ├── factory.ts # generación de roles con briefing de 10 secciones
|
|
618
|
+
│ ├── briefing.ts # el contrato de información del agente
|
|
619
|
+
│ ├── router.ts # enrutado + rotación anti-monotonía
|
|
620
|
+
│ ├── gates.ts # runner de los 6 gates + escaneo de secretos
|
|
621
|
+
│ ├── retry.ts # política de 3 golpes
|
|
622
|
+
│ ├── office.ts # la fachada que amarra todo
|
|
623
|
+
│ ├── paths.ts # resolución de project root
|
|
624
|
+
│ ├── fsx.ts # IO atómico
|
|
625
|
+
│ └── log.ts # logging estructurado
|
|
626
|
+
├── tools/index.ts # las 11 tools
|
|
627
|
+
└── hooks/
|
|
628
|
+
├── sessionIdle.ts # auto-continuación
|
|
629
|
+
├── scopeGuard.ts # enforcement de scope
|
|
630
|
+
├── systemTransform.ts
|
|
631
|
+
└── compacting.ts
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
### Dependencias
|
|
635
|
+
|
|
636
|
+
Una sola, y es peer del host: `@opencode-ai/plugin`. Zod entra por `tool.schema` — la instancia del propio plugin — para que los tipos nunca divergan de la versión que el host está corriendo.
|
|
637
|
+
|
|
638
|
+
---
|
|
639
|
+
|
|
640
|
+
## Testing
|
|
641
|
+
|
|
642
|
+
```bash
|
|
643
|
+
npm test # 213 tests, 14 archivos
|
|
644
|
+
npm run coverage # 90.2% stmts · 82.8% branches
|
|
645
|
+
npm run typecheck # tsc --noEmit, strict
|
|
646
|
+
npm run build # tsc → dist/
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
| Suite | Cubre |
|
|
650
|
+
|---|---|
|
|
651
|
+
| `bus.test.ts` | Append-only, ack, TTL, idempotencia, broadcast, prioridad, atomicidad |
|
|
652
|
+
| `state.test.ts` | Transiciones, invariantes duros, selección de card, ciclo de corrida |
|
|
653
|
+
| `gates.test.ts` | Detección de tooling, las 6 fases, N/A con motivo, consolidación, redacción |
|
|
654
|
+
| `router.test.ts` | Enrutado, rotación, reviewer ≠ owner, política de 3 golpes, presupuesto |
|
|
655
|
+
| `leases.test.ts` | Adquisición, heartbeat, expiración, double-claim, reasignación |
|
|
656
|
+
| `catalog.test.ts` | Frontmatter, secciones, extracción, matching ponderado, panel, factory |
|
|
657
|
+
| `catalogReal.test.ts` | El catálogo real de 306 agentes: estructuras, matching, división |
|
|
658
|
+
| `briefing.test.ts` | Orden, mínimo, las 10 secciones, los 4 artefactos, INCOMPLETO |
|
|
659
|
+
| `office.test.ts` | Ciclo completo end-to-end, invariantes, artefactos en disco |
|
|
660
|
+
| `scopeGuard.test.ts` | Globs, extracción de path, enforcement de scope |
|
|
661
|
+
| `plugin.test.ts` | El plugin contra un host mockeado: tools, hooks, auto-resume |
|
|
662
|
+
| `migration.test.ts` | Migración v1 → v2 sin pérdida de datos |
|
|
663
|
+
| `cli.test.ts` | init, status, board, install, audit, reset, idempotencia |
|
|
664
|
+
|
|
665
|
+
---
|
|
666
|
+
|
|
667
|
+
## Licencia
|
|
668
|
+
|
|
669
|
+
MIT — ver [LICENSE](https://github.com/SOCRAMbt/AI-OFFICE/tree/main/LICENSE).
|
|
670
|
+
|
|
671
|
+
El diseño se apoya en los patrones de `durable-execution` y `workflow-engine-design`: estado durable antes de los efectos, leases con TTL, transiciones nombradas explícitamente y at-least-once con reconciliación por idempotency key.
|