oxe-cc 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/commands/oxe-ask.md +4 -2
- package/.cursor/commands/oxe-capabilities.md +4 -2
- package/.cursor/commands/oxe-checkpoint.md +4 -2
- package/.cursor/commands/oxe-compact.md +4 -2
- package/.cursor/commands/oxe-dashboard.md +4 -2
- package/.cursor/commands/oxe-debug.md +4 -2
- package/.cursor/commands/oxe-discuss.md +4 -2
- package/.cursor/commands/oxe-execute.md +5 -3
- package/.cursor/commands/oxe-forensics.md +4 -2
- package/.cursor/commands/oxe-help.md +4 -2
- package/.cursor/commands/oxe-loop.md +4 -2
- package/.cursor/commands/oxe-milestone.md +4 -2
- package/.cursor/commands/oxe-next.md +4 -2
- package/.cursor/commands/oxe-obs.md +4 -2
- package/.cursor/commands/oxe-plan-agent.md +4 -2
- package/.cursor/commands/oxe-plan.md +4 -2
- package/.cursor/commands/oxe-project.md +4 -2
- package/.cursor/commands/oxe-quick.md +4 -2
- package/.cursor/commands/oxe-research.md +4 -2
- package/.cursor/commands/oxe-retro.md +4 -2
- package/.cursor/commands/oxe-review-pr.md +4 -2
- package/.cursor/commands/oxe-route.md +4 -2
- package/.cursor/commands/oxe-scan.md +4 -2
- package/.cursor/commands/oxe-security.md +4 -2
- package/.cursor/commands/oxe-session.md +5 -3
- package/.cursor/commands/oxe-ship.md +4 -2
- package/.cursor/commands/oxe-skill.md +4 -2
- package/.cursor/commands/oxe-spec.md +4 -2
- package/.cursor/commands/oxe-ui-review.md +4 -2
- package/.cursor/commands/oxe-ui-spec.md +4 -2
- package/.cursor/commands/oxe-update.md +4 -2
- package/.cursor/commands/oxe-validate-gaps.md +4 -2
- package/.cursor/commands/oxe-verify-audit.md +46 -0
- package/.cursor/commands/oxe-verify.md +4 -2
- package/.cursor/commands/oxe-workflow-authoring.md +47 -0
- package/.cursor/commands/oxe-workstream.md +4 -2
- package/.cursor/commands/oxe.md +6 -2
- package/.github/prompts/oxe-ask.prompt.md +4 -2
- package/.github/prompts/oxe-capabilities.prompt.md +4 -2
- package/.github/prompts/oxe-checkpoint.prompt.md +4 -2
- package/.github/prompts/oxe-compact.prompt.md +5 -3
- package/.github/prompts/oxe-dashboard.prompt.md +4 -2
- package/.github/prompts/oxe-debug.prompt.md +4 -2
- package/.github/prompts/oxe-discuss.prompt.md +6 -2
- package/.github/prompts/oxe-execute.prompt.md +5 -3
- package/.github/prompts/oxe-forensics.prompt.md +4 -2
- package/.github/prompts/oxe-help.prompt.md +6 -2
- package/.github/prompts/oxe-loop.prompt.md +4 -2
- package/.github/prompts/oxe-milestone.prompt.md +4 -2
- package/.github/prompts/oxe-next.prompt.md +6 -2
- package/.github/prompts/oxe-obs.prompt.md +4 -2
- package/.github/prompts/oxe-plan-agent.prompt.md +5 -2
- package/.github/prompts/oxe-plan.prompt.md +4 -2
- package/.github/prompts/oxe-project.prompt.md +4 -2
- package/.github/prompts/oxe-quick.prompt.md +4 -2
- package/.github/prompts/oxe-research.prompt.md +4 -2
- package/.github/prompts/oxe-retro.prompt.md +4 -2
- package/.github/prompts/oxe-review-pr.prompt.md +4 -2
- package/.github/prompts/oxe-route.prompt.md +4 -2
- package/.github/prompts/oxe-scan.prompt.md +4 -2
- package/.github/prompts/oxe-security.prompt.md +4 -2
- package/.github/prompts/oxe-session.prompt.md +5 -3
- package/.github/prompts/oxe-ship.prompt.md +4 -2
- package/.github/prompts/oxe-skill.prompt.md +4 -2
- package/.github/prompts/oxe-spec.prompt.md +4 -2
- package/.github/prompts/oxe-ui-review.prompt.md +4 -2
- package/.github/prompts/oxe-ui-spec.prompt.md +4 -2
- package/.github/prompts/oxe-update.prompt.md +4 -2
- package/.github/prompts/oxe-validate-gaps.prompt.md +4 -2
- package/.github/prompts/oxe-verify-audit.prompt.md +46 -0
- package/.github/prompts/oxe-verify.prompt.md +4 -2
- package/.github/prompts/oxe-workflow-authoring.prompt.md +47 -0
- package/.github/prompts/oxe-workstream.prompt.md +4 -2
- package/.github/prompts/oxe.prompt.md +6 -2
- package/.github/workflows/ci.yml +57 -20
- package/.github/workflows/release.yml +94 -0
- package/AGENTS.md +3 -1
- package/CHANGELOG.md +383 -342
- package/QUICKSTART.md +99 -0
- package/README.md +89 -65
- package/bin/lib/oxe-agent-install.cjs +127 -107
- package/bin/lib/oxe-install-resolve.cjs +10 -0
- package/bin/lib/oxe-operational.cjs +34 -28
- package/bin/lib/oxe-project-health.cjs +38 -6
- package/bin/lib/oxe-release.cjs +423 -0
- package/bin/lib/oxe-runtime-semantics.cjs +68 -24
- package/bin/oxe-cc.js +388 -55
- package/commands/oxe/ask.md +7 -3
- package/commands/oxe/capabilities.md +6 -2
- package/commands/oxe/checkpoint.md +5 -1
- package/commands/oxe/compact.md +6 -2
- package/commands/oxe/dashboard.md +6 -2
- package/commands/oxe/debug.md +6 -2
- package/commands/oxe/discuss.md +6 -2
- package/commands/oxe/execute.md +6 -2
- package/commands/oxe/forensics.md +6 -2
- package/commands/oxe/help.md +6 -2
- package/commands/oxe/loop.md +6 -2
- package/commands/oxe/milestone.md +6 -2
- package/commands/oxe/next.md +6 -2
- package/commands/oxe/obs.md +6 -2
- package/commands/oxe/oxe.md +6 -2
- package/commands/oxe/plan-agent.md +6 -2
- package/commands/oxe/plan.md +6 -2
- package/commands/oxe/project.md +6 -2
- package/commands/oxe/quick.md +6 -2
- package/commands/oxe/research.md +6 -2
- package/commands/oxe/retro.md +6 -2
- package/commands/oxe/review-pr.md +6 -2
- package/commands/oxe/route.md +6 -2
- package/commands/oxe/scan.md +6 -2
- package/commands/oxe/security.md +6 -2
- package/commands/oxe/session.md +6 -2
- package/commands/oxe/ship.md +6 -2
- package/commands/oxe/skill.md +6 -2
- package/commands/oxe/spec.md +6 -2
- package/commands/oxe/ui-review.md +6 -2
- package/commands/oxe/ui-spec.md +6 -2
- package/commands/oxe/update.md +6 -2
- package/commands/oxe/validate-gaps.md +6 -2
- package/commands/oxe/verify-audit.md +50 -0
- package/commands/oxe/verify.md +6 -2
- package/commands/oxe/workflow-authoring.md +50 -0
- package/commands/oxe/workstream.md +6 -2
- package/docs/INCIDENT-PLAYBOOK.md +181 -0
- package/docs/RELEASE-READINESS.md +46 -0
- package/docs/ROLES.md +129 -0
- package/docs/RUNTIME-SMOKE-MATRIX.md +128 -0
- package/docs/TEAM-ADOPTION.md +153 -0
- package/docs/WALKTHROUGH.md +241 -0
- package/lib/runtime/scheduler/multi-agent-coordinator.d.ts +28 -0
- package/lib/runtime/scheduler/multi-agent-coordinator.js +152 -26
- package/lib/sdk/README.md +2 -0
- package/lib/sdk/index.cjs +22 -8
- package/lib/sdk/index.d.ts +60 -16
- package/oxe/templates/config.template.json +1 -0
- package/package.json +30 -22
- package/packages/runtime/package.json +1 -1
- package/packages/runtime/src/scheduler/multi-agent-coordinator.ts +357 -193
- package/vscode-extension/oxe-agents-1.4.0.vsix +0 -0
- package/vscode-extension/oxe-agents-1.5.0.vsix +0 -0
- package/vscode-extension/package.json +1 -1
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# OXE — Walkthrough Reproduzível
|
|
2
|
+
|
|
3
|
+
> Feature: "Adicionar endpoint REST `GET /api/items` de listagem paginada"
|
|
4
|
+
>
|
|
5
|
+
> Tempo estimado: 20 minutos. Todos os comandos são reproduzíveis — copie e execute.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Pré-requisitos
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx oxe-cc doctor
|
|
13
|
+
# ✓ Node 18+
|
|
14
|
+
# ✓ Workflows presentes
|
|
15
|
+
# ✓ Estrutura base .oxe/
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Se algum item falhar, siga o conselho exibido antes de continuar.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Passo 1 — Descobrir onde você está
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
/oxe
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Output esperado (projeto novo):**
|
|
29
|
+
```
|
|
30
|
+
Estado atual: init
|
|
31
|
+
Próximo passo recomendado: /oxe-scan
|
|
32
|
+
Motivo: nenhum mapeamento de codebase encontrado em .oxe/codebase/
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Artefato gerado:** nenhum (apenas diagnóstico)
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Passo 2 — Mapear o codebase
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
/oxe-scan
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Output esperado:**
|
|
46
|
+
```
|
|
47
|
+
Scan completo.
|
|
48
|
+
Arquivos mapeados: 47
|
|
49
|
+
Módulos detectados: routes/, controllers/, services/, tests/
|
|
50
|
+
Artefato: .oxe/codebase/map.json
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Artefato gerado:** `.oxe/codebase/map.json`
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Passo 3 — Gerar critérios de aceite
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
/oxe-spec
|
|
61
|
+
|
|
62
|
+
> Adicionar endpoint GET /api/items com paginação (page, limit). Retornar lista de items e metadados de paginação.
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Output esperado:**
|
|
66
|
+
```
|
|
67
|
+
SPEC.md gerado.
|
|
68
|
+
Critérios A* (bloqueantes):
|
|
69
|
+
A1. GET /api/items retorna 200 com lista de items
|
|
70
|
+
A2. Parâmetros page e limit são aceitos e validados
|
|
71
|
+
A3. Resposta inclui { items: [...], total, page, limit }
|
|
72
|
+
A4. Testes de integração cobrem os 3 critérios acima
|
|
73
|
+
|
|
74
|
+
Critérios B (desejáveis):
|
|
75
|
+
B1. Cursor inválido retorna 400 com mensagem clara
|
|
76
|
+
B2. Documentação OpenAPI atualizada
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Artefato gerado:** `.oxe/SPEC.md`
|
|
80
|
+
|
|
81
|
+
Revise os critérios. Se estiverem corretos, avance.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Passo 4 — Planejar a implementação
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
/oxe-plan
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Output esperado:**
|
|
92
|
+
```
|
|
93
|
+
PLAN.md gerado.
|
|
94
|
+
Onda 1 (obrigatória):
|
|
95
|
+
T1. Criar route GET /api/items em routes/items.js
|
|
96
|
+
T2. Implementar ItemsController.list() com paginação
|
|
97
|
+
T3. Implementar ItemsService.findPaginated(page, limit)
|
|
98
|
+
T4. Escrever testes de integração para os 3 critérios A*
|
|
99
|
+
|
|
100
|
+
Hipótese crítica: ItemsService tem acesso ao repositório de dados correto
|
|
101
|
+
Arquivos-alvo: routes/items.js, controllers/items.js, services/items.js, tests/items.test.js
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Artefato gerado:** `.oxe/PLAN.md`
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Passo 5 — Implementar
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
/oxe-execute
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
O executor vai implementar T1→T4 em sequência, marcando cada tarefa concluída.
|
|
115
|
+
|
|
116
|
+
**Output esperado ao final:**
|
|
117
|
+
```
|
|
118
|
+
Onda 1 completa.
|
|
119
|
+
T1 ✓ routes/items.js — route criada
|
|
120
|
+
T2 ✓ controllers/items.js — ItemsController.list() implementado
|
|
121
|
+
T3 ✓ services/items.js — ItemsService.findPaginated() implementado
|
|
122
|
+
T4 ✓ tests/items.test.js — 4 testes de integração passando
|
|
123
|
+
|
|
124
|
+
Estado: execute_complete
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Para executar uma tarefa específica:**
|
|
128
|
+
```
|
|
129
|
+
/oxe-execute --task T3
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Passo 6 — Verificar
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
/oxe-verify
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Output esperado:**
|
|
141
|
+
```
|
|
142
|
+
Verificação completa.
|
|
143
|
+
|
|
144
|
+
A1 ✓ GET /api/items retorna 200 — evidência: tests/items.test.js:12
|
|
145
|
+
A2 ✓ Parâmetros page e limit validados — evidência: controllers/items.js:34
|
|
146
|
+
A3 ✓ Resposta inclui {items, total, page, limit} — evidência: controllers/items.js:41
|
|
147
|
+
A4 ✓ Testes cobrem A1–A3 — evidência: tests/items.test.js (4 testes)
|
|
148
|
+
|
|
149
|
+
Riscos residuais: nenhum
|
|
150
|
+
Estado: verify_complete
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**Artefato gerado:** `.oxe/VERIFY.md`
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Passo 7 — Gate de aprovação (opcional, runtime-first)
|
|
158
|
+
|
|
159
|
+
Se o projeto usa runtime gates:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
npx oxe-cc runtime gates list --dir .
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Output esperado:**
|
|
166
|
+
```
|
|
167
|
+
Gates pendentes: 1
|
|
168
|
+
⏳ gate-001 tipo: verify_complete estado: pending
|
|
169
|
+
ação sugerida: approve
|
|
170
|
+
impacto: bloqueia promoção para pr_draft
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Para aprovar:
|
|
174
|
+
```bash
|
|
175
|
+
npx oxe-cc runtime gates resolve --dir . --gate gate-001 --decision approve --actor "seu-nome"
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Output esperado:**
|
|
179
|
+
```
|
|
180
|
+
✓ Gate gate-001 aprovado.
|
|
181
|
+
Run pode avançar para promoção — nenhum gate restante.
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Passo 8 — Promover para PR draft
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
npx oxe-cc runtime promote --dir . --target pr_draft
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**Output esperado:**
|
|
193
|
+
```
|
|
194
|
+
Promoção para pr_draft iniciada.
|
|
195
|
+
Branch: feature/items-endpoint
|
|
196
|
+
VERIFY.md incluído como evidência
|
|
197
|
+
PR draft criado: https://github.com/seu-org/repo/pull/42
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Estado final dos artefatos
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
.oxe/
|
|
206
|
+
├── STATE.md # fase: verify_complete (ou pr_draft após promote)
|
|
207
|
+
├── SPEC.md # critérios A1–A4 + B1–B2
|
|
208
|
+
├── PLAN.md # ondas e tarefas T1–T4
|
|
209
|
+
├── VERIFY.md # evidências para cada critério A*
|
|
210
|
+
└── codebase/
|
|
211
|
+
└── map.json # mapa do projeto
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Validação rápida
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
npx oxe-cc status
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**Output esperado:**
|
|
223
|
+
```
|
|
224
|
+
Estado: verify_complete
|
|
225
|
+
Próximo passo: runtime promote --target pr_draft (ou abrir PR manualmente)
|
|
226
|
+
SPEC: 4 critérios A* — todos verificados
|
|
227
|
+
VERIFY.md: presente
|
|
228
|
+
Gates pendentes: 0
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Se algo der errado
|
|
234
|
+
|
|
235
|
+
| Sintoma | Comando |
|
|
236
|
+
|---------|---------|
|
|
237
|
+
| Travado em uma tarefa | `/oxe-debug` |
|
|
238
|
+
| Critério A* não verificado | `/oxe-verify` + revisar implementação |
|
|
239
|
+
| Gate stale (>24h) | Ver [`docs/INCIDENT-PLAYBOOK.md`](INCIDENT-PLAYBOOK.md) |
|
|
240
|
+
| Plano desatualizado | `/oxe-plan` novamente (sobrescreve PLAN.md) |
|
|
241
|
+
| Estado incoerente | `npx oxe-cc status --full` + `/oxe` para rediagnóstico |
|
|
@@ -17,6 +17,7 @@ export interface CoordinationOptions {
|
|
|
17
17
|
sessionId: string | null;
|
|
18
18
|
runId: string;
|
|
19
19
|
onEvent?: SchedulerContext['onEvent'];
|
|
20
|
+
heartbeatTimeoutMs?: number;
|
|
20
21
|
}
|
|
21
22
|
export interface ArbitrationRecord {
|
|
22
23
|
work_item_id: string;
|
|
@@ -48,12 +49,36 @@ export interface MultiAgentStatusSnapshot {
|
|
|
48
49
|
assigned_task_ids: string[];
|
|
49
50
|
completed: string[];
|
|
50
51
|
failed: string[];
|
|
52
|
+
timed_out: boolean;
|
|
53
|
+
reassigned_task_ids: string[];
|
|
51
54
|
}>;
|
|
52
55
|
orphan_reassignments: Array<{
|
|
53
56
|
from_agent_id: string;
|
|
54
57
|
to_agent_id: string;
|
|
55
58
|
work_item_ids: string[];
|
|
56
59
|
}>;
|
|
60
|
+
timed_out_agents: Array<{
|
|
61
|
+
agent_id: string;
|
|
62
|
+
work_item_ids: string[];
|
|
63
|
+
detected_at: string;
|
|
64
|
+
}>;
|
|
65
|
+
updated_at: string;
|
|
66
|
+
}
|
|
67
|
+
export interface MultiAgentOperationalSummary {
|
|
68
|
+
run_id: string;
|
|
69
|
+
mode: CoordinationMode;
|
|
70
|
+
workspace_isolation_enforced: boolean;
|
|
71
|
+
agent_count: number;
|
|
72
|
+
completed_count: number;
|
|
73
|
+
failed_count: number;
|
|
74
|
+
blocked_count: number;
|
|
75
|
+
ownership_count: number;
|
|
76
|
+
handoff_count: number;
|
|
77
|
+
arbitration_count: number;
|
|
78
|
+
orphan_reassignment_count: number;
|
|
79
|
+
timeout_count: number;
|
|
80
|
+
participating_agents: string[];
|
|
81
|
+
health: 'healthy' | 'degraded';
|
|
57
82
|
updated_at: string;
|
|
58
83
|
}
|
|
59
84
|
export interface CoordinationResult {
|
|
@@ -70,9 +95,12 @@ export interface CoordinationResult {
|
|
|
70
95
|
handoffs?: CooperativeHandoff[];
|
|
71
96
|
arbitration_results?: ArbitrationRecord[];
|
|
72
97
|
state?: MultiAgentStatusSnapshot;
|
|
98
|
+
summary?: MultiAgentOperationalSummary;
|
|
73
99
|
}
|
|
74
100
|
export declare class MultiAgentCoordinator {
|
|
75
101
|
run(graph: ExecutionGraph, opts: CoordinationOptions): Promise<CoordinationResult>;
|
|
76
102
|
}
|
|
77
103
|
export declare function multiAgentStatePath(projectRoot: string, runId: string): string;
|
|
104
|
+
export declare function multiAgentSummaryPath(projectRoot: string, runId: string): string;
|
|
78
105
|
export declare function loadMultiAgentState(projectRoot: string, runId: string): MultiAgentStatusSnapshot | null;
|
|
106
|
+
export declare function loadMultiAgentSummary(projectRoot: string, runId: string): MultiAgentOperationalSummary | null;
|
|
@@ -5,12 +5,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.MultiAgentCoordinator = void 0;
|
|
7
7
|
exports.multiAgentStatePath = multiAgentStatePath;
|
|
8
|
+
exports.multiAgentSummaryPath = multiAgentSummaryPath;
|
|
8
9
|
exports.loadMultiAgentState = loadMultiAgentState;
|
|
10
|
+
exports.loadMultiAgentSummary = loadMultiAgentSummary;
|
|
9
11
|
const fs_1 = __importDefault(require("fs"));
|
|
10
12
|
const path_1 = __importDefault(require("path"));
|
|
11
13
|
const bus_1 = require("../events/bus");
|
|
12
14
|
const scheduler_1 = require("./scheduler");
|
|
13
15
|
const agent_roles_1 = require("./agent-roles");
|
|
16
|
+
const agent_registry_1 = require("./agent-registry");
|
|
14
17
|
function ensureRunDir(projectRoot, runId) {
|
|
15
18
|
const dir = path_1.default.join(projectRoot, '.oxe', 'runs', runId);
|
|
16
19
|
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
@@ -21,6 +24,24 @@ function persistMultiAgentArtifacts(projectRoot, runId, state, handoffs = [], ar
|
|
|
21
24
|
fs_1.default.writeFileSync(path_1.default.join(runDir, 'multi-agent-state.json'), JSON.stringify(state, null, 2), 'utf8');
|
|
22
25
|
fs_1.default.writeFileSync(path_1.default.join(runDir, 'handoffs.json'), JSON.stringify(handoffs, null, 2), 'utf8');
|
|
23
26
|
fs_1.default.writeFileSync(path_1.default.join(runDir, 'arbitration-results.json'), JSON.stringify(arbitrationResults, null, 2), 'utf8');
|
|
27
|
+
const summary = {
|
|
28
|
+
run_id: state.run_id,
|
|
29
|
+
mode: state.mode,
|
|
30
|
+
workspace_isolation_enforced: state.workspace_isolation_enforced,
|
|
31
|
+
agent_count: state.agent_count,
|
|
32
|
+
completed_count: state.completed.length,
|
|
33
|
+
failed_count: state.failed.length,
|
|
34
|
+
blocked_count: state.blocked.length,
|
|
35
|
+
ownership_count: state.ownership.length,
|
|
36
|
+
handoff_count: handoffs.length,
|
|
37
|
+
arbitration_count: arbitrationResults.length,
|
|
38
|
+
orphan_reassignment_count: state.orphan_reassignments.length,
|
|
39
|
+
timeout_count: state.timed_out_agents.length,
|
|
40
|
+
participating_agents: state.agent_results.map((entry) => entry.agent_id),
|
|
41
|
+
health: state.timed_out_agents.length > 0 || state.failed.length > 0 ? 'degraded' : 'healthy',
|
|
42
|
+
updated_at: state.updated_at,
|
|
43
|
+
};
|
|
44
|
+
fs_1.default.writeFileSync(path_1.default.join(runDir, 'multi-agent-summary.json'), JSON.stringify(summary, null, 2), 'utf8');
|
|
24
45
|
}
|
|
25
46
|
function ensureIsolatedAgents(agents) {
|
|
26
47
|
const shared = agents.filter((agent) => agent.workspaceManager.isolation_level !== 'isolated');
|
|
@@ -41,7 +62,7 @@ function buildOwnership(agents, partitions) {
|
|
|
41
62
|
}
|
|
42
63
|
return ownership;
|
|
43
64
|
}
|
|
44
|
-
function makeState(mode, runId, agents, partitions, agentResults, completed, failed, blocked, orphanReassignments) {
|
|
65
|
+
function makeState(mode, runId, agents, partitions, agentResults, completed, failed, blocked, orphanReassignments, timedOutAgents) {
|
|
45
66
|
return {
|
|
46
67
|
run_id: runId,
|
|
47
68
|
mode,
|
|
@@ -60,16 +81,82 @@ function makeState(mode, runId, agents, partitions, agentResults, completed, fai
|
|
|
60
81
|
assigned_task_ids: partitions[idx] ?? agent.assignedTaskIds ?? [],
|
|
61
82
|
completed: result?.completed ?? [],
|
|
62
83
|
failed: result?.failed ?? [],
|
|
84
|
+
timed_out: Boolean(result?.timed_out),
|
|
85
|
+
reassigned_task_ids: result?.reassigned_task_ids ?? [],
|
|
63
86
|
};
|
|
64
87
|
}),
|
|
65
88
|
orphan_reassignments: orphanReassignments,
|
|
89
|
+
timed_out_agents: timedOutAgents,
|
|
66
90
|
updated_at: new Date().toISOString(),
|
|
67
91
|
};
|
|
68
92
|
}
|
|
93
|
+
async function runGraphForAgent(graph, nodeIds, agent, idx, opts, heartbeatTimeoutMs) {
|
|
94
|
+
const subGraph = subGraphFor(graph, nodeIds);
|
|
95
|
+
if (subGraph.nodes.size === 0) {
|
|
96
|
+
return {
|
|
97
|
+
agent_id: agent.id,
|
|
98
|
+
completed: [],
|
|
99
|
+
failed: [],
|
|
100
|
+
timed_out: false,
|
|
101
|
+
assigned_task_ids: nodeIds,
|
|
102
|
+
reassigned_task_ids: [],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const ctx = {
|
|
106
|
+
projectRoot: opts.projectRoot,
|
|
107
|
+
sessionId: opts.sessionId,
|
|
108
|
+
runId: `${opts.runId}-agent${idx}`,
|
|
109
|
+
executor: agent.executor,
|
|
110
|
+
workspaceManager: agent.workspaceManager,
|
|
111
|
+
onEvent: opts.onEvent,
|
|
112
|
+
};
|
|
113
|
+
const scheduler = new scheduler_1.Scheduler();
|
|
114
|
+
const work = scheduler.run(subGraph, ctx);
|
|
115
|
+
if (!heartbeatTimeoutMs || heartbeatTimeoutMs <= 0) {
|
|
116
|
+
const result = await work;
|
|
117
|
+
return {
|
|
118
|
+
agent_id: agent.id,
|
|
119
|
+
completed: result.completed,
|
|
120
|
+
failed: result.failed,
|
|
121
|
+
timed_out: false,
|
|
122
|
+
assigned_task_ids: nodeIds,
|
|
123
|
+
reassigned_task_ids: [],
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
let timer = null;
|
|
127
|
+
const raced = await Promise.race([
|
|
128
|
+
work.then((result) => ({ type: 'result', result })),
|
|
129
|
+
new Promise((resolve) => {
|
|
130
|
+
timer = setTimeout(() => resolve({ type: 'timeout' }), heartbeatTimeoutMs);
|
|
131
|
+
}),
|
|
132
|
+
]);
|
|
133
|
+
if (timer)
|
|
134
|
+
clearTimeout(timer);
|
|
135
|
+
if (raced && raced.type === 'timeout') {
|
|
136
|
+
return {
|
|
137
|
+
agent_id: agent.id,
|
|
138
|
+
completed: [],
|
|
139
|
+
failed: [],
|
|
140
|
+
timed_out: true,
|
|
141
|
+
assigned_task_ids: nodeIds,
|
|
142
|
+
reassigned_task_ids: [],
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const result = raced.result;
|
|
146
|
+
return {
|
|
147
|
+
agent_id: agent.id,
|
|
148
|
+
completed: result.completed,
|
|
149
|
+
failed: result.failed,
|
|
150
|
+
timed_out: false,
|
|
151
|
+
assigned_task_ids: nodeIds,
|
|
152
|
+
reassigned_task_ids: [],
|
|
153
|
+
};
|
|
154
|
+
}
|
|
69
155
|
// ─── Parallel mode ───────────────────────────────────────────────────────────
|
|
70
156
|
async function runParallel(graph, opts) {
|
|
71
157
|
const { agents, projectRoot, sessionId, runId } = opts;
|
|
72
158
|
ensureIsolatedAgents(agents);
|
|
159
|
+
const heartbeatTimeoutMs = opts.heartbeatTimeoutMs ?? null;
|
|
73
160
|
const partitions = agents.map((agent) => [...(agent.assignedTaskIds ?? [])]);
|
|
74
161
|
if (partitions.every((partition) => partition.length === 0)) {
|
|
75
162
|
const allIds = [...graph.nodes.keys()];
|
|
@@ -77,33 +164,55 @@ async function runParallel(graph, opts) {
|
|
|
77
164
|
partitions[index % agents.length].push(id);
|
|
78
165
|
});
|
|
79
166
|
}
|
|
167
|
+
const registry = new agent_registry_1.AgentRegistry(heartbeatTimeoutMs == null ? 30000 : heartbeatTimeoutMs);
|
|
168
|
+
agents.forEach((agent, idx) => {
|
|
169
|
+
registry.register(agent.id, agent.executor, agent.workspaceManager, partitions[idx] ?? []);
|
|
170
|
+
});
|
|
80
171
|
(0, bus_1.appendEvent)(projectRoot, sessionId, {
|
|
81
172
|
type: 'RunStarted',
|
|
82
173
|
run_id: runId,
|
|
83
174
|
payload: { mode: 'parallel', agent_count: agents.length, isolation_level: 'isolated' },
|
|
84
175
|
});
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const ctx = {
|
|
91
|
-
projectRoot,
|
|
92
|
-
sessionId,
|
|
93
|
-
runId: `${runId}-agent${idx}`,
|
|
94
|
-
executor: agent.executor,
|
|
95
|
-
workspaceManager: agent.workspaceManager,
|
|
96
|
-
onEvent: opts.onEvent,
|
|
97
|
-
};
|
|
98
|
-
const scheduler = new scheduler_1.Scheduler();
|
|
99
|
-
const result = await scheduler.run(subGraph, ctx);
|
|
100
|
-
return { agent_id: agent.id, completed: result.completed, failed: result.failed };
|
|
176
|
+
const initialResults = await Promise.all(agents.map(async (agent, idx) => {
|
|
177
|
+
registry.beat(agent.id, partitions[idx][0] || null);
|
|
178
|
+
const result = await runGraphForAgent(graph, partitions[idx], agent, idx, opts, heartbeatTimeoutMs);
|
|
179
|
+
registry.setStatus(agent.id, result.timed_out ? 'timeout' : 'idle');
|
|
180
|
+
return result;
|
|
101
181
|
}));
|
|
102
|
-
const
|
|
103
|
-
const failed = agentResults.flatMap((result) => result.failed);
|
|
182
|
+
const timedOutAgents = [];
|
|
104
183
|
const blocked = [];
|
|
105
184
|
const orphanReassignments = [];
|
|
106
|
-
const
|
|
185
|
+
const agentResults = initialResults.map((entry) => ({
|
|
186
|
+
...entry,
|
|
187
|
+
reassigned_task_ids: entry.reassigned_task_ids || [],
|
|
188
|
+
}));
|
|
189
|
+
const liveAgents = agentResults.filter((entry) => !entry.timed_out);
|
|
190
|
+
for (const timedOut of agentResults.filter((entry) => entry.timed_out)) {
|
|
191
|
+
timedOutAgents.push({
|
|
192
|
+
agent_id: timedOut.agent_id,
|
|
193
|
+
work_item_ids: timedOut.assigned_task_ids,
|
|
194
|
+
detected_at: new Date().toISOString(),
|
|
195
|
+
});
|
|
196
|
+
const fallback = liveAgents.find((entry) => entry.agent_id !== timedOut.agent_id);
|
|
197
|
+
if (!fallback || timedOut.assigned_task_ids.length === 0)
|
|
198
|
+
continue;
|
|
199
|
+
const fallbackIdx = agents.findIndex((agent) => agent.id === fallback.agent_id);
|
|
200
|
+
const timeoutIdx = agents.findIndex((agent) => agent.id === timedOut.agent_id);
|
|
201
|
+
const rerun = await runGraphForAgent(graph, timedOut.assigned_task_ids, agents[fallbackIdx], fallbackIdx, opts, null);
|
|
202
|
+
fallback.completed.push(...rerun.completed);
|
|
203
|
+
fallback.failed.push(...rerun.failed);
|
|
204
|
+
fallback.reassigned_task_ids.push(...timedOut.assigned_task_ids);
|
|
205
|
+
partitions[fallbackIdx] = [...partitions[fallbackIdx], ...timedOut.assigned_task_ids];
|
|
206
|
+
partitions[timeoutIdx] = [];
|
|
207
|
+
orphanReassignments.push({
|
|
208
|
+
from_agent_id: timedOut.agent_id,
|
|
209
|
+
to_agent_id: fallback.agent_id,
|
|
210
|
+
work_item_ids: timedOut.assigned_task_ids,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
const completed = Array.from(new Set(agentResults.flatMap((result) => result.completed)));
|
|
214
|
+
const failed = Array.from(new Set(agentResults.flatMap((result) => result.failed)));
|
|
215
|
+
const state = makeState('parallel', runId, agents, partitions, agentResults, completed, failed, blocked, orphanReassignments, timedOutAgents);
|
|
107
216
|
persistMultiAgentArtifacts(projectRoot, runId, state);
|
|
108
217
|
(0, bus_1.appendEvent)(projectRoot, sessionId, {
|
|
109
218
|
type: 'RunCompleted',
|
|
@@ -119,6 +228,7 @@ async function runParallel(graph, opts) {
|
|
|
119
228
|
agent_results: agentResults,
|
|
120
229
|
arbitration_results: [],
|
|
121
230
|
state,
|
|
231
|
+
summary: loadMultiAgentSummary(projectRoot, runId) || undefined,
|
|
122
232
|
};
|
|
123
233
|
}
|
|
124
234
|
// ─── Competitive mode ────────────────────────────────────────────────────────
|
|
@@ -154,9 +264,9 @@ async function runCompetitive(graph, opts) {
|
|
|
154
264
|
}
|
|
155
265
|
const partitions = [Array.from(graph.nodes.keys()), Array.from(graph.nodes.keys())];
|
|
156
266
|
const state = makeState('competitive', runId, opts.agents, partitions, [
|
|
157
|
-
{ agent_id: agentA.id, completed, failed },
|
|
158
|
-
{ agent_id: agentB.id, completed: [], failed: [] },
|
|
159
|
-
], completed, failed, blocked, []);
|
|
267
|
+
{ agent_id: agentA.id, completed, failed, timed_out: false, reassigned_task_ids: [] },
|
|
268
|
+
{ agent_id: agentB.id, completed: [], failed: [], timed_out: false, reassigned_task_ids: [] },
|
|
269
|
+
], completed, failed, blocked, [], []);
|
|
160
270
|
persistMultiAgentArtifacts(projectRoot, runId, state, [], arbitrationResults);
|
|
161
271
|
(0, bus_1.appendEvent)(projectRoot, sessionId, {
|
|
162
272
|
type: 'RunCompleted',
|
|
@@ -175,6 +285,7 @@ async function runCompetitive(graph, opts) {
|
|
|
175
285
|
],
|
|
176
286
|
arbitration_results: arbitrationResults,
|
|
177
287
|
state,
|
|
288
|
+
summary: loadMultiAgentSummary(projectRoot, runId) || undefined,
|
|
178
289
|
};
|
|
179
290
|
}
|
|
180
291
|
async function competeTwoAgents(nodeId, node, agentA, agentB, opts, arbitrationResults) {
|
|
@@ -294,9 +405,9 @@ async function runCooperative(graph, opts) {
|
|
|
294
405
|
}
|
|
295
406
|
const partitions = [Array.from(graph.nodes.keys()), Array.from(graph.nodes.keys())];
|
|
296
407
|
const state = makeState('cooperative', runId, opts.agents, partitions, [
|
|
297
|
-
{ agent_id: planner.id, completed: [], failed: [] },
|
|
298
|
-
{ agent_id: executor.id, completed, failed },
|
|
299
|
-
], completed, failed, blocked, []);
|
|
408
|
+
{ agent_id: planner.id, completed: [], failed: [], timed_out: false, reassigned_task_ids: [] },
|
|
409
|
+
{ agent_id: executor.id, completed, failed, timed_out: false, reassigned_task_ids: [] },
|
|
410
|
+
], completed, failed, blocked, [], []);
|
|
300
411
|
persistMultiAgentArtifacts(projectRoot, runId, state, handoffs, []);
|
|
301
412
|
(0, bus_1.appendEvent)(projectRoot, sessionId, {
|
|
302
413
|
type: 'RunCompleted',
|
|
@@ -316,6 +427,7 @@ async function runCooperative(graph, opts) {
|
|
|
316
427
|
handoffs,
|
|
317
428
|
arbitration_results: [],
|
|
318
429
|
state,
|
|
430
|
+
summary: loadMultiAgentSummary(projectRoot, runId) || undefined,
|
|
319
431
|
};
|
|
320
432
|
}
|
|
321
433
|
// ─── Public API ──────────────────────────────────────────────────────────────
|
|
@@ -334,6 +446,9 @@ exports.MultiAgentCoordinator = MultiAgentCoordinator;
|
|
|
334
446
|
function multiAgentStatePath(projectRoot, runId) {
|
|
335
447
|
return path_1.default.join(projectRoot, '.oxe', 'runs', runId, 'multi-agent-state.json');
|
|
336
448
|
}
|
|
449
|
+
function multiAgentSummaryPath(projectRoot, runId) {
|
|
450
|
+
return path_1.default.join(projectRoot, '.oxe', 'runs', runId, 'multi-agent-summary.json');
|
|
451
|
+
}
|
|
337
452
|
function loadMultiAgentState(projectRoot, runId) {
|
|
338
453
|
const statePath = multiAgentStatePath(projectRoot, runId);
|
|
339
454
|
if (!fs_1.default.existsSync(statePath))
|
|
@@ -345,6 +460,17 @@ function loadMultiAgentState(projectRoot, runId) {
|
|
|
345
460
|
return null;
|
|
346
461
|
}
|
|
347
462
|
}
|
|
463
|
+
function loadMultiAgentSummary(projectRoot, runId) {
|
|
464
|
+
const summaryPath = multiAgentSummaryPath(projectRoot, runId);
|
|
465
|
+
if (!fs_1.default.existsSync(summaryPath))
|
|
466
|
+
return null;
|
|
467
|
+
try {
|
|
468
|
+
return JSON.parse(fs_1.default.readFileSync(summaryPath, 'utf8'));
|
|
469
|
+
}
|
|
470
|
+
catch {
|
|
471
|
+
return null;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
348
474
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
349
475
|
function subGraphFor(graph, nodeIds) {
|
|
350
476
|
const ids = new Set(nodeIds);
|
package/lib/sdk/README.md
CHANGED
|
@@ -76,5 +76,7 @@ O SDK já expõe os helpers usados pela operação enterprise do OXE:
|
|
|
76
76
|
- `operational.readRuntimeGates(...)` / `operational.resolveRuntimeGate(...)`
|
|
77
77
|
- `operational.recoverRuntimeState(...)` / `operational.replayRuntimeState(...)`
|
|
78
78
|
- `operational.readRuntimeMultiAgentStatus(...)` / `operational.multiAgentStatus(...)`
|
|
79
|
+
- `release.checkReleaseConsistency(...)` / `release.buildReleaseManifest(...)`
|
|
80
|
+
- `release.loadRuntimeSmokeReport(...)` / `release.loadRecoveryFixtureReport(...)` / `release.loadMultiAgentSoakReport(...)`
|
|
79
81
|
|
|
80
82
|
Use esses helpers quando quiser integrar gates, verify, recovery e multi-agent a scripts de CI, automações internas ou observabilidade.
|
package/lib/sdk/index.cjs
CHANGED
|
@@ -17,8 +17,9 @@ const plugins = require('../../bin/lib/oxe-plugins.cjs');
|
|
|
17
17
|
const dashboard = require('../../bin/lib/oxe-dashboard.cjs');
|
|
18
18
|
const operational = require('../../bin/lib/oxe-operational.cjs');
|
|
19
19
|
const azure = require('../../bin/lib/oxe-azure.cjs');
|
|
20
|
-
const context = require('../../bin/lib/oxe-context-engine.cjs');
|
|
21
|
-
const runtimeSemantics = require('../../bin/lib/oxe-runtime-semantics.cjs');
|
|
20
|
+
const context = require('../../bin/lib/oxe-context-engine.cjs');
|
|
21
|
+
const runtimeSemantics = require('../../bin/lib/oxe-runtime-semantics.cjs');
|
|
22
|
+
const release = require('../../bin/lib/oxe-release.cjs');
|
|
22
23
|
|
|
23
24
|
const PACKAGE_ROOT = path.join(__dirname, '..', '..');
|
|
24
25
|
|
|
@@ -636,12 +637,25 @@ module.exports = {
|
|
|
636
637
|
},
|
|
637
638
|
|
|
638
639
|
/** Dashboard local: contexto consolidado e persistência de revisão do plano. */
|
|
639
|
-
dashboard: {
|
|
640
|
-
loadDashboardContext: dashboard.loadDashboardContext,
|
|
641
|
-
savePlanReviewStatus: dashboard.savePlanReviewStatus,
|
|
642
|
-
addPlanReviewComment: dashboard.addPlanReviewComment,
|
|
643
|
-
updatePlanReviewCommentStatus: dashboard.updatePlanReviewCommentStatus,
|
|
644
|
-
},
|
|
640
|
+
dashboard: {
|
|
641
|
+
loadDashboardContext: dashboard.loadDashboardContext,
|
|
642
|
+
savePlanReviewStatus: dashboard.savePlanReviewStatus,
|
|
643
|
+
addPlanReviewComment: dashboard.addPlanReviewComment,
|
|
644
|
+
updatePlanReviewCommentStatus: dashboard.updatePlanReviewCommentStatus,
|
|
645
|
+
},
|
|
646
|
+
|
|
647
|
+
/** Release readiness: manifest, smoke matrix e checks de consistência antes de publicar. */
|
|
648
|
+
release: {
|
|
649
|
+
REQUIRED_RUNTIMES: release.REQUIRED_RUNTIMES,
|
|
650
|
+
WRAPPER_TARGETS: release.WRAPPER_TARGETS,
|
|
651
|
+
releasePaths: release.releasePaths,
|
|
652
|
+
collectWrapperHashes: release.collectWrapperHashes,
|
|
653
|
+
loadRuntimeSmokeReport: release.loadRuntimeSmokeReport,
|
|
654
|
+
loadRecoveryFixtureReport: release.loadRecoveryFixtureReport,
|
|
655
|
+
loadMultiAgentSoakReport: release.loadMultiAgentSoakReport,
|
|
656
|
+
buildReleaseManifest: release.buildReleaseManifest,
|
|
657
|
+
checkReleaseConsistency: release.checkReleaseConsistency,
|
|
658
|
+
},
|
|
645
659
|
|
|
646
660
|
/** Runtime operacional: tracing, active run, catálogo de capabilities e memória em camadas. */
|
|
647
661
|
operational: {
|