oxe-cc 1.2.1 → 1.4.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 +2 -2
- package/.cursor/commands/oxe-capabilities.md +2 -2
- package/.cursor/commands/oxe-checkpoint.md +2 -2
- package/.cursor/commands/oxe-compact.md +2 -2
- package/.cursor/commands/oxe-dashboard.md +2 -2
- package/.cursor/commands/oxe-debug.md +2 -2
- package/.cursor/commands/oxe-discuss.md +2 -2
- package/.cursor/commands/oxe-execute.md +5 -2
- package/.cursor/commands/oxe-forensics.md +2 -2
- package/.cursor/commands/oxe-help.md +2 -2
- package/.cursor/commands/oxe-loop.md +2 -2
- package/.cursor/commands/oxe-milestone.md +2 -2
- package/.cursor/commands/oxe-next.md +2 -2
- package/.cursor/commands/oxe-obs.md +2 -2
- package/.cursor/commands/oxe-plan-agent.md +2 -2
- package/.cursor/commands/oxe-plan.md +2 -2
- package/.cursor/commands/oxe-project.md +2 -2
- package/.cursor/commands/oxe-quick.md +2 -2
- package/.cursor/commands/oxe-research.md +2 -2
- package/.cursor/commands/oxe-retro.md +2 -2
- package/.cursor/commands/oxe-review-pr.md +2 -2
- package/.cursor/commands/oxe-route.md +2 -2
- package/.cursor/commands/oxe-scan.md +2 -2
- package/.cursor/commands/oxe-security.md +2 -2
- package/.cursor/commands/oxe-session.md +2 -2
- package/.cursor/commands/oxe-ship.md +2 -2
- package/.cursor/commands/oxe-skill.md +2 -2
- package/.cursor/commands/oxe-spec.md +2 -2
- package/.cursor/commands/oxe-ui-review.md +2 -2
- package/.cursor/commands/oxe-ui-spec.md +2 -2
- package/.cursor/commands/oxe-update.md +2 -2
- package/.cursor/commands/oxe-validate-gaps.md +2 -2
- package/.cursor/commands/oxe-verify.md +5 -2
- package/.cursor/commands/oxe-workstream.md +2 -2
- package/.cursor/commands/oxe.md +2 -2
- package/.github/copilot-instructions.md +13 -13
- package/.github/prompts/oxe-ask.prompt.md +2 -2
- package/.github/prompts/oxe-capabilities.prompt.md +2 -2
- package/.github/prompts/oxe-checkpoint.prompt.md +2 -2
- package/.github/prompts/oxe-compact.prompt.md +2 -2
- package/.github/prompts/oxe-dashboard.prompt.md +2 -2
- package/.github/prompts/oxe-debug.prompt.md +2 -2
- package/.github/prompts/oxe-discuss.prompt.md +2 -2
- package/.github/prompts/oxe-execute.prompt.md +5 -2
- package/.github/prompts/oxe-forensics.prompt.md +2 -2
- package/.github/prompts/oxe-help.prompt.md +2 -2
- package/.github/prompts/oxe-loop.prompt.md +2 -2
- package/.github/prompts/oxe-milestone.prompt.md +2 -2
- package/.github/prompts/oxe-next.prompt.md +2 -2
- package/.github/prompts/oxe-obs.prompt.md +2 -2
- package/.github/prompts/oxe-plan-agent.prompt.md +2 -2
- package/.github/prompts/oxe-plan.prompt.md +2 -2
- package/.github/prompts/oxe-project.prompt.md +2 -2
- package/.github/prompts/oxe-quick.prompt.md +2 -2
- package/.github/prompts/oxe-research.prompt.md +2 -2
- package/.github/prompts/oxe-retro.prompt.md +2 -2
- package/.github/prompts/oxe-review-pr.prompt.md +2 -2
- package/.github/prompts/oxe-route.prompt.md +2 -2
- package/.github/prompts/oxe-scan.prompt.md +2 -2
- package/.github/prompts/oxe-security.prompt.md +2 -2
- package/.github/prompts/oxe-session.prompt.md +2 -2
- package/.github/prompts/oxe-ship.prompt.md +2 -2
- package/.github/prompts/oxe-skill.prompt.md +2 -2
- package/.github/prompts/oxe-spec.prompt.md +2 -2
- package/.github/prompts/oxe-ui-review.prompt.md +2 -2
- package/.github/prompts/oxe-ui-spec.prompt.md +2 -2
- package/.github/prompts/oxe-update.prompt.md +2 -2
- package/.github/prompts/oxe-validate-gaps.prompt.md +2 -2
- package/.github/prompts/oxe-verify.prompt.md +5 -2
- package/.github/prompts/oxe-workstream.prompt.md +2 -2
- package/.github/prompts/oxe.prompt.md +2 -2
- package/AGENTS.md +5 -3
- package/CHANGELOG.md +72 -10
- package/LICENSE +21 -674
- package/README.md +631 -535
- package/bin/banner.txt +6 -6
- package/bin/lib/oxe-agent-install.cjs +69 -69
- package/bin/lib/oxe-azure.cjs +1445 -1445
- package/bin/lib/oxe-context-engine.cjs +867 -867
- package/bin/lib/oxe-dashboard.cjs +76 -28
- package/bin/lib/oxe-operational.cjs +2144 -1340
- package/bin/lib/oxe-project-health.cjs +483 -1
- package/bin/lib/oxe-runtime-semantics.cjs +12 -0
- package/bin/oxe-cc.js +554 -152
- package/commands/oxe/ask.md +2 -2
- package/commands/oxe/capabilities.md +2 -2
- package/commands/oxe/checkpoint.md +2 -2
- package/commands/oxe/compact.md +2 -2
- package/commands/oxe/dashboard.md +2 -2
- package/commands/oxe/debug.md +2 -2
- package/commands/oxe/discuss.md +2 -2
- package/commands/oxe/execute.md +5 -2
- package/commands/oxe/forensics.md +2 -2
- package/commands/oxe/help.md +2 -2
- package/commands/oxe/loop.md +2 -2
- package/commands/oxe/milestone.md +2 -2
- package/commands/oxe/next.md +2 -2
- package/commands/oxe/obs.md +2 -2
- package/commands/oxe/oxe.md +2 -2
- package/commands/oxe/plan-agent.md +2 -2
- package/commands/oxe/plan.md +2 -2
- package/commands/oxe/project.md +2 -2
- package/commands/oxe/quick.md +2 -2
- package/commands/oxe/research.md +2 -2
- package/commands/oxe/retro.md +2 -2
- package/commands/oxe/review-pr.md +2 -2
- package/commands/oxe/route.md +2 -2
- package/commands/oxe/scan.md +2 -2
- package/commands/oxe/security.md +2 -2
- package/commands/oxe/session.md +2 -2
- package/commands/oxe/ship.md +2 -2
- package/commands/oxe/skill.md +2 -2
- package/commands/oxe/spec.md +2 -2
- package/commands/oxe/ui-review.md +2 -2
- package/commands/oxe/ui-spec.md +2 -2
- package/commands/oxe/update.md +2 -2
- package/commands/oxe/validate-gaps.md +2 -2
- package/commands/oxe/verify.md +5 -2
- package/commands/oxe/workstream.md +2 -2
- package/lib/runtime/delivery/branch-manager.d.ts +1 -0
- package/lib/runtime/delivery/branch-manager.js +7 -0
- package/lib/runtime/delivery/ci-checks.js +34 -1
- package/lib/runtime/delivery/delivery-records.d.ts +34 -0
- package/lib/runtime/delivery/delivery-records.js +48 -0
- package/lib/runtime/delivery/index.d.ts +1 -0
- package/lib/runtime/delivery/index.js +1 -0
- package/lib/runtime/delivery/promotion-pipeline.d.ts +26 -2
- package/lib/runtime/delivery/promotion-pipeline.js +111 -14
- package/lib/runtime/gate/gate-manager.d.ts +41 -0
- package/lib/runtime/gate/gate-manager.js +108 -1
- package/lib/runtime/index.d.ts +2 -2
- package/lib/runtime/index.js +3 -1
- package/lib/runtime/models/gate-decision.d.ts +4 -1
- package/lib/runtime/models/workspace.d.ts +3 -0
- package/lib/runtime/plugins/capability-adapter.d.ts +12 -0
- package/lib/runtime/plugins/capability-adapter.js +204 -0
- package/lib/runtime/plugins/capability-matrix.d.ts +5 -0
- package/lib/runtime/plugins/capability-matrix.js +48 -17
- package/lib/runtime/plugins/index.d.ts +1 -0
- package/lib/runtime/plugins/index.js +1 -0
- package/lib/runtime/plugins/plugin-abi.d.ts +2 -0
- package/lib/runtime/plugins/plugin-manifest.d.ts +1 -1
- package/lib/runtime/plugins/plugin-manifest.js +6 -2
- package/lib/runtime/plugins/plugin-registry.d.ts +46 -0
- package/lib/runtime/plugins/plugin-registry.js +79 -2
- package/lib/runtime/policy/policy-engine.d.ts +19 -0
- package/lib/runtime/policy/policy-engine.js +76 -4
- package/lib/runtime/projection/projection-engine.d.ts +9 -1
- package/lib/runtime/projection/projection-engine.js +73 -3
- package/lib/runtime/scheduler/multi-agent-coordinator.d.ts +43 -1
- package/lib/runtime/scheduler/multi-agent-coordinator.js +151 -39
- package/lib/runtime/scheduler/run-journal.d.ts +1 -1
- package/lib/runtime/scheduler/scheduler.d.ts +19 -1
- package/lib/runtime/scheduler/scheduler.js +258 -13
- package/lib/runtime/verification/verification-compiler.d.ts +43 -0
- package/lib/runtime/verification/verification-compiler.js +137 -0
- package/lib/runtime/verification/verification-manifest.d.ts +9 -0
- package/lib/runtime/verification/verification-manifest.js +56 -6
- package/lib/runtime/workspace/strategies/ephemeral-container.d.ts +1 -0
- package/lib/runtime/workspace/strategies/ephemeral-container.js +4 -0
- package/lib/runtime/workspace/strategies/git-worktree.d.ts +1 -0
- package/lib/runtime/workspace/strategies/git-worktree.js +2 -0
- package/lib/runtime/workspace/strategies/inplace.d.ts +1 -0
- package/lib/runtime/workspace/strategies/inplace.js +2 -0
- package/lib/runtime/workspace/workspace-manager.d.ts +2 -1
- package/lib/sdk/README.md +20 -8
- package/lib/sdk/index.cjs +33 -24
- package/lib/sdk/index.d.ts +149 -14
- package/oxe/templates/ACTIVE-RUN.template.json +32 -32
- package/oxe/templates/CAPABILITIES.template.md +7 -7
- package/oxe/templates/CAPABILITY.template.md +45 -45
- package/oxe/templates/CHECKPOINTS.template.md +7 -7
- package/oxe/templates/EXECUTION-RUNTIME.template.md +68 -68
- package/oxe/templates/HYPOTHESES.template.md +33 -33
- package/oxe/templates/LESSONS-METRICS.template.json +13 -13
- package/oxe/templates/NOTES.template.md +16 -16
- package/oxe/templates/PLAN-REVIEW.template.md +31 -31
- package/oxe/templates/SESSION.template.md +34 -34
- package/oxe/templates/SKILL.template.md +26 -26
- package/oxe/templates/STATE.md +55 -55
- package/oxe/templates/WORKFLOW_AUTHORING.md +18 -18
- package/oxe/workflows/ask.md +96 -96
- package/oxe/workflows/capabilities.md +25 -25
- package/oxe/workflows/dashboard.md +33 -33
- package/oxe/workflows/discuss.md +12 -12
- package/oxe/workflows/execute.md +14 -0
- package/oxe/workflows/help.md +352 -352
- package/oxe/workflows/next.md +22 -22
- package/oxe/workflows/oxe.md +6 -6
- package/oxe/workflows/plan-agent.md +9 -9
- package/oxe/workflows/plan.md +51 -20
- package/oxe/workflows/quick.md +10 -10
- package/oxe/workflows/references/reasoning-discovery.md +28 -28
- package/oxe/workflows/references/reasoning-execution.md +29 -29
- package/oxe/workflows/references/reasoning-planning.md +32 -32
- package/oxe/workflows/references/reasoning-review.md +29 -29
- package/oxe/workflows/references/reasoning-status.md +24 -24
- package/oxe/workflows/references/robustness-elevation.md +295 -295
- package/oxe/workflows/references/workflow-runtime-contracts.json +952 -930
- package/oxe/workflows/route.md +16 -16
- package/oxe/workflows/session.md +213 -213
- package/oxe/workflows/ship.md +142 -142
- package/oxe/workflows/skill.md +44 -44
- package/oxe/workflows/ui-review.md +36 -36
- package/oxe/workflows/verify-audit.md +73 -73
- package/oxe/workflows/verify.md +10 -0
- package/package.json +92 -92
- package/packages/runtime/package.json +16 -15
- package/packages/runtime/src/audit/audit-trail.ts +243 -243
- package/packages/runtime/src/audit/index.ts +2 -2
- package/packages/runtime/src/audit/policy-pack.ts +62 -62
- package/packages/runtime/src/compiler/graph-compiler.ts +245 -245
- package/packages/runtime/src/compiler/index.ts +1 -1
- package/packages/runtime/src/context/context-pack-builder.ts +259 -259
- package/packages/runtime/src/context/context-pack-store.ts +197 -197
- package/packages/runtime/src/context/context-profiles.ts +60 -60
- package/packages/runtime/src/context/index.ts +3 -3
- package/packages/runtime/src/decision/decision-engine.ts +174 -174
- package/packages/runtime/src/decision/decision-memo.ts +211 -211
- package/packages/runtime/src/decision/index.ts +2 -2
- package/packages/runtime/src/delivery/branch-manager.ts +91 -84
- package/packages/runtime/src/delivery/ci-checks.ts +285 -252
- package/packages/runtime/src/delivery/delivery-records.ts +75 -0
- package/packages/runtime/src/delivery/index.ts +5 -4
- package/packages/runtime/src/delivery/pr-manager.ts +112 -112
- package/packages/runtime/src/delivery/promotion-pipeline.ts +334 -180
- package/packages/runtime/src/events/bus.ts +92 -92
- package/packages/runtime/src/events/catalog.ts +29 -29
- package/packages/runtime/src/events/envelope.ts +14 -14
- package/packages/runtime/src/events/index.ts +3 -3
- package/packages/runtime/src/evidence/evidence-store.ts +130 -130
- package/packages/runtime/src/evidence/index.ts +1 -1
- package/packages/runtime/src/gate/gate-manager.ts +289 -137
- package/packages/runtime/src/gate/index.ts +1 -1
- package/packages/runtime/src/index.ts +41 -37
- package/packages/runtime/src/models/attempt.ts +19 -19
- package/packages/runtime/src/models/evidence.ts +21 -21
- package/packages/runtime/src/models/gate-decision.ts +25 -21
- package/packages/runtime/src/models/index.ts +8 -8
- package/packages/runtime/src/models/run.ts +24 -24
- package/packages/runtime/src/models/session.ts +11 -11
- package/packages/runtime/src/models/verification-result.ts +10 -10
- package/packages/runtime/src/models/work-item.ts +25 -25
- package/packages/runtime/src/models/workspace.ts +31 -28
- package/packages/runtime/src/plugins/capability-adapter.ts +206 -0
- package/packages/runtime/src/plugins/capability-matrix.ts +126 -83
- package/packages/runtime/src/plugins/index.ts +5 -4
- package/packages/runtime/src/plugins/plugin-abi.ts +97 -95
- package/packages/runtime/src/plugins/plugin-manifest.ts +118 -113
- package/packages/runtime/src/plugins/plugin-registry.ts +232 -124
- package/packages/runtime/src/policy/index.ts +1 -1
- package/packages/runtime/src/policy/policy-engine.ts +330 -244
- package/packages/runtime/src/projection/index.ts +1 -1
- package/packages/runtime/src/projection/projection-engine.ts +328 -249
- package/packages/runtime/src/reducers/debug-reducer.ts +36 -36
- package/packages/runtime/src/reducers/index.ts +2 -2
- package/packages/runtime/src/reducers/run-state-reducer.ts +269 -269
- package/packages/runtime/src/scheduler/agent-registry.ts +132 -132
- package/packages/runtime/src/scheduler/agent-roles.ts +109 -109
- package/packages/runtime/src/scheduler/index.ts +4 -4
- package/packages/runtime/src/scheduler/multi-agent-coordinator.ts +521 -333
- package/packages/runtime/src/scheduler/run-journal.ts +62 -62
- package/packages/runtime/src/scheduler/scheduler.ts +722 -441
- package/packages/runtime/src/verification/index.ts +2 -2
- package/packages/runtime/src/verification/verification-compiler.ts +436 -225
- package/packages/runtime/src/verification/verification-manifest.ts +252 -192
- package/packages/runtime/src/workspace/index.ts +5 -5
- package/packages/runtime/src/workspace/strategies/ephemeral-container.ts +126 -121
- package/packages/runtime/src/workspace/strategies/git-worktree.ts +79 -77
- package/packages/runtime/src/workspace/strategies/inplace.ts +38 -35
- package/packages/runtime/src/workspace/workspace-manager.ts +16 -15
- package/packages/runtime/tsconfig.json +17 -17
- package/vscode-extension/.vscodeignore +7 -7
- package/vscode-extension/LICENSE +21 -0
- package/vscode-extension/oxe-agents-1.0.0.vsix +0 -0
- package/vscode-extension/oxe-agents-1.4.0.vsix +0 -0
- package/vscode-extension/package.json +184 -184
- package/vscode-extension/src/extension.js +310 -310
- package/vscode-extension/src/shared/contextLoader.js +137 -137
- package/vscode-extension/src/shared/contractBuilder.js +159 -159
- package/vscode-extension/src/shared/stateReader.js +101 -101
|
@@ -1,1340 +1,2144 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const { spawnSync } = require('child_process');
|
|
6
|
-
|
|
7
|
-
const VALID_RUN_STATUSES = new Set([
|
|
8
|
-
'planned',
|
|
9
|
-
'running',
|
|
10
|
-
'paused',
|
|
11
|
-
'waiting_approval',
|
|
12
|
-
'
|
|
13
|
-
'
|
|
14
|
-
'
|
|
15
|
-
'
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
'
|
|
21
|
-
'
|
|
22
|
-
'
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (raw
|
|
63
|
-
if (
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
.
|
|
67
|
-
.
|
|
68
|
-
.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function
|
|
117
|
-
const
|
|
118
|
-
if (!
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
payload.
|
|
228
|
-
payload.
|
|
229
|
-
payload.
|
|
230
|
-
payload.
|
|
231
|
-
payload.
|
|
232
|
-
payload.
|
|
233
|
-
payload.
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
payload.
|
|
238
|
-
|
|
239
|
-
payload.
|
|
240
|
-
payload.
|
|
241
|
-
payload.
|
|
242
|
-
payload.
|
|
243
|
-
payload.
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
payload
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
const
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
const
|
|
436
|
-
const
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
const
|
|
493
|
-
const
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
const
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
const
|
|
613
|
-
const
|
|
614
|
-
|
|
615
|
-
const
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
const
|
|
659
|
-
const
|
|
660
|
-
const
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
const
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
:
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
};
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
const
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
.
|
|
844
|
-
|
|
845
|
-
.
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
? {
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
)
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
const
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
.
|
|
978
|
-
.
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
if (!
|
|
1008
|
-
|
|
1009
|
-
}
|
|
1010
|
-
const
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
const
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
const
|
|
1036
|
-
const
|
|
1037
|
-
const
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
.
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
const
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
if (
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
const
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
const
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
}
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { spawnSync } = require('child_process');
|
|
6
|
+
|
|
7
|
+
const VALID_RUN_STATUSES = new Set([
|
|
8
|
+
'planned',
|
|
9
|
+
'running',
|
|
10
|
+
'paused',
|
|
11
|
+
'waiting_approval',
|
|
12
|
+
'blocked',
|
|
13
|
+
'failed',
|
|
14
|
+
'completed',
|
|
15
|
+
'replaying',
|
|
16
|
+
'aborted',
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
const VALID_APPROVAL_POLICIES = new Set([
|
|
20
|
+
'always_allow',
|
|
21
|
+
'require_approval',
|
|
22
|
+
'require_approval_if_external_side_effect',
|
|
23
|
+
'deny_unless_overridden',
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
const VALID_CAPABILITY_TYPES = new Set(['script', 'mcp', 'automation', 'local']);
|
|
27
|
+
|
|
28
|
+
function readTextIfExists(filePath) {
|
|
29
|
+
try {
|
|
30
|
+
return fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : null;
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function ensureDir(dirPath) {
|
|
37
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function ensureDirForFile(filePath) {
|
|
41
|
+
ensureDir(path.dirname(filePath));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function parseFrontmatter(text) {
|
|
45
|
+
const match = String(text || '').match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
46
|
+
if (!match) return {};
|
|
47
|
+
const out = {};
|
|
48
|
+
for (const line of match[1].split(/\r?\n/)) {
|
|
49
|
+
const trimmed = line.trim();
|
|
50
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
51
|
+
const idx = trimmed.indexOf(':');
|
|
52
|
+
if (idx === -1) continue;
|
|
53
|
+
const key = trimmed.slice(0, idx).trim();
|
|
54
|
+
const value = trimmed.slice(idx + 1).trim();
|
|
55
|
+
out[key] = value;
|
|
56
|
+
}
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function parseArrayField(value) {
|
|
61
|
+
const raw = String(value || '').trim();
|
|
62
|
+
if (!raw) return [];
|
|
63
|
+
if (raw === '[]') return [];
|
|
64
|
+
if (/^\[.*\]$/.test(raw)) {
|
|
65
|
+
return raw
|
|
66
|
+
.slice(1, -1)
|
|
67
|
+
.split(',')
|
|
68
|
+
.map((item) => item.trim().replace(/^['"`]|['"`]$/g, ''))
|
|
69
|
+
.filter(Boolean);
|
|
70
|
+
}
|
|
71
|
+
return raw.split(',').map((item) => item.trim()).filter(Boolean);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function loadRuntimeModule() {
|
|
75
|
+
try {
|
|
76
|
+
return require('../../lib/runtime/index.js');
|
|
77
|
+
} catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function buildRuntimePluginRegistry(projectRoot) {
|
|
83
|
+
const runtime = loadRuntimeModule();
|
|
84
|
+
if (!runtime || typeof runtime.PluginRegistry !== 'function') return null;
|
|
85
|
+
const registry = new runtime.PluginRegistry();
|
|
86
|
+
const pluginDir = path.join(projectRoot, '.oxe', 'plugins');
|
|
87
|
+
if (fs.existsSync(pluginDir) && typeof registry.loadFromDirectory === 'function') {
|
|
88
|
+
registry.loadFromDirectory(pluginDir);
|
|
89
|
+
}
|
|
90
|
+
if (typeof registry.registerProjectCapabilities === 'function') {
|
|
91
|
+
registry.registerProjectCapabilities(projectRoot);
|
|
92
|
+
}
|
|
93
|
+
return registry;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function loadSdkParsers() {
|
|
97
|
+
try {
|
|
98
|
+
const sdk = require('../../lib/sdk/index.cjs');
|
|
99
|
+
if (sdk && typeof sdk.parsePlan === 'function' && typeof sdk.parseSpec === 'function') {
|
|
100
|
+
return { parsePlan: sdk.parsePlan, parseSpec: sdk.parseSpec };
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
} catch {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function loadProjectHealth() {
|
|
109
|
+
try {
|
|
110
|
+
return require('./oxe-project-health.cjs');
|
|
111
|
+
} catch {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function readJsonIfExists(filePath) {
|
|
117
|
+
const raw = readTextIfExists(filePath);
|
|
118
|
+
if (!raw) return null;
|
|
119
|
+
try {
|
|
120
|
+
return JSON.parse(raw);
|
|
121
|
+
} catch {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function preferExistingPath(primaryPath, fallbackPath) {
|
|
127
|
+
if (primaryPath && fs.existsSync(primaryPath)) return primaryPath;
|
|
128
|
+
return fallbackPath || primaryPath || null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function resolveRuntimeArtifactPaths(projectRoot, activeSession) {
|
|
132
|
+
const health = loadProjectHealth();
|
|
133
|
+
if (!health || typeof health.oxePaths !== 'function' || typeof health.scopedOxePaths !== 'function') {
|
|
134
|
+
const oxeDir = path.join(projectRoot, '.oxe');
|
|
135
|
+
if (!activeSession) {
|
|
136
|
+
return {
|
|
137
|
+
state: path.join(oxeDir, 'STATE.md'),
|
|
138
|
+
spec: path.join(oxeDir, 'SPEC.md'),
|
|
139
|
+
plan: path.join(oxeDir, 'PLAN.md'),
|
|
140
|
+
verify: path.join(oxeDir, 'VERIFY.md'),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
const sessionRoot = path.join(oxeDir, ...String(activeSession).split('/'));
|
|
144
|
+
return {
|
|
145
|
+
state: path.join(oxeDir, 'STATE.md'),
|
|
146
|
+
spec: preferExistingPath(path.join(sessionRoot, 'spec', 'SPEC.md'), path.join(oxeDir, 'SPEC.md')),
|
|
147
|
+
plan: preferExistingPath(path.join(sessionRoot, 'plan', 'PLAN.md'), path.join(oxeDir, 'PLAN.md')),
|
|
148
|
+
verify: preferExistingPath(path.join(sessionRoot, 'verification', 'VERIFY.md'), path.join(oxeDir, 'VERIFY.md')),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const base = health.oxePaths(projectRoot);
|
|
152
|
+
const scoped = health.scopedOxePaths(projectRoot, activeSession || null);
|
|
153
|
+
return {
|
|
154
|
+
state: base.state,
|
|
155
|
+
spec: preferExistingPath(scoped.spec, base.spec),
|
|
156
|
+
plan: preferExistingPath(scoped.plan, base.plan),
|
|
157
|
+
verify: preferExistingPath(scoped.verify, base.verify),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function normalizeRuntimeEventType(type) {
|
|
162
|
+
const raw = String(type || '').trim();
|
|
163
|
+
if (!raw) return 'RunStarted';
|
|
164
|
+
const directMap = {
|
|
165
|
+
SessionCreated: 'SessionCreated',
|
|
166
|
+
RunStarted: 'RunStarted',
|
|
167
|
+
GraphCompiled: 'GraphCompiled',
|
|
168
|
+
WorkItemReady: 'WorkItemReady',
|
|
169
|
+
WorkspaceAllocated: 'WorkspaceAllocated',
|
|
170
|
+
AttemptStarted: 'AttemptStarted',
|
|
171
|
+
ToolInvoked: 'ToolInvoked',
|
|
172
|
+
ToolCompleted: 'ToolCompleted',
|
|
173
|
+
ToolFailed: 'ToolFailed',
|
|
174
|
+
EvidenceCollected: 'EvidenceCollected',
|
|
175
|
+
PolicyEvaluated: 'PolicyEvaluated',
|
|
176
|
+
GateRequested: 'GateRequested',
|
|
177
|
+
GateResolved: 'GateResolved',
|
|
178
|
+
VerificationStarted: 'VerificationStarted',
|
|
179
|
+
VerificationCompleted: 'VerificationCompleted',
|
|
180
|
+
RetryScheduled: 'RetryScheduled',
|
|
181
|
+
WorkItemCompleted: 'WorkItemCompleted',
|
|
182
|
+
WorkItemBlocked: 'WorkItemBlocked',
|
|
183
|
+
RunCompleted: 'RunCompleted',
|
|
184
|
+
RetroPublished: 'RetroPublished',
|
|
185
|
+
LessonPromoted: 'LessonPromoted',
|
|
186
|
+
};
|
|
187
|
+
if (directMap[raw]) return directMap[raw];
|
|
188
|
+
const lower = raw.toLowerCase();
|
|
189
|
+
const legacyMap = {
|
|
190
|
+
session_created: 'SessionCreated',
|
|
191
|
+
run_started: 'RunStarted',
|
|
192
|
+
graph_compiled: 'GraphCompiled',
|
|
193
|
+
work_item_ready: 'WorkItemReady',
|
|
194
|
+
task_ready: 'WorkItemReady',
|
|
195
|
+
workspace_allocated: 'WorkspaceAllocated',
|
|
196
|
+
attempt_started: 'AttemptStarted',
|
|
197
|
+
tool_invoked: 'ToolInvoked',
|
|
198
|
+
tool_completed: 'ToolCompleted',
|
|
199
|
+
tool_failed: 'ToolFailed',
|
|
200
|
+
evidence_collected: 'EvidenceCollected',
|
|
201
|
+
policy_evaluated: 'PolicyEvaluated',
|
|
202
|
+
gate_requested: 'GateRequested',
|
|
203
|
+
gate_resolved: 'GateResolved',
|
|
204
|
+
checkpoint_opened: 'GateRequested',
|
|
205
|
+
checkpoint_resolved: 'GateResolved',
|
|
206
|
+
verification_started: 'VerificationStarted',
|
|
207
|
+
verification_completed: 'VerificationCompleted',
|
|
208
|
+
verify_complete: 'VerificationCompleted',
|
|
209
|
+
retry_scheduled: 'RetryScheduled',
|
|
210
|
+
work_item_completed: 'WorkItemCompleted',
|
|
211
|
+
task_completed: 'WorkItemCompleted',
|
|
212
|
+
work_item_blocked: 'WorkItemBlocked',
|
|
213
|
+
task_blocked: 'WorkItemBlocked',
|
|
214
|
+
run_completed: 'RunCompleted',
|
|
215
|
+
retro_published: 'RetroPublished',
|
|
216
|
+
lesson_promoted: 'LessonPromoted',
|
|
217
|
+
};
|
|
218
|
+
return legacyMap[lower] || raw;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function toRuntimeEventEnvelope(event = {}, activeSession) {
|
|
222
|
+
const type = normalizeRuntimeEventType(event.type);
|
|
223
|
+
const payload = event.payload && typeof event.payload === 'object' ? { ...event.payload } : {};
|
|
224
|
+
const workItemId = event.work_item_id || event.task_id || payload.work_item_id || payload.task_id || null;
|
|
225
|
+
const attemptId = event.attempt_id || payload.attempt_id || null;
|
|
226
|
+
if (type === 'RunStarted') {
|
|
227
|
+
payload.run_id = payload.run_id || event.run_id || null;
|
|
228
|
+
payload.session_id = payload.session_id || activeSession || event.session_id || null;
|
|
229
|
+
payload.graph_version = payload.graph_version || 'legacy';
|
|
230
|
+
payload.started_at = payload.started_at || event.timestamp || new Date().toISOString();
|
|
231
|
+
payload.ended_at = payload.ended_at == null ? null : payload.ended_at;
|
|
232
|
+
payload.status = payload.status || 'running';
|
|
233
|
+
payload.initiator = payload.initiator || 'scheduler';
|
|
234
|
+
payload.mode = payload.mode || 'por_onda';
|
|
235
|
+
} else if (type === 'RunCompleted') {
|
|
236
|
+
payload.run_id = payload.run_id || event.run_id || null;
|
|
237
|
+
payload.status = payload.status || 'completed';
|
|
238
|
+
} else if (type === 'WorkItemReady') {
|
|
239
|
+
payload.work_item_id = payload.work_item_id || workItemId;
|
|
240
|
+
payload.title = payload.title || workItemId || 'work-item';
|
|
241
|
+
payload.type = payload.type || 'task';
|
|
242
|
+
payload.depends_on = Array.isArray(payload.depends_on) ? payload.depends_on : [];
|
|
243
|
+
payload.mutation_scope = Array.isArray(payload.mutation_scope) ? payload.mutation_scope : [];
|
|
244
|
+
payload.policy_ref = payload.policy_ref || null;
|
|
245
|
+
payload.verify_ref = Array.isArray(payload.verify_ref) ? payload.verify_ref : [];
|
|
246
|
+
payload.status = payload.status || 'pending';
|
|
247
|
+
payload.workspace_strategy = payload.workspace_strategy || 'inplace';
|
|
248
|
+
payload.run_id = payload.run_id || event.run_id || null;
|
|
249
|
+
} else if (type === 'AttemptStarted') {
|
|
250
|
+
payload.attempt_number = payload.attempt_number || 1;
|
|
251
|
+
} else if (type === 'WorkspaceAllocated') {
|
|
252
|
+
payload.workspace_id = payload.workspace_id || `ws-${workItemId || 'runtime'}`;
|
|
253
|
+
payload.strategy = payload.strategy || payload.workspace_strategy || 'inplace';
|
|
254
|
+
payload.root_path = payload.root_path || null;
|
|
255
|
+
payload.base_commit = payload.base_commit || null;
|
|
256
|
+
payload.branch = payload.branch || null;
|
|
257
|
+
payload.container_ref = payload.container_ref || null;
|
|
258
|
+
payload.status = payload.status || 'ready';
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
id: String(event.event_id || ''),
|
|
262
|
+
type,
|
|
263
|
+
timestamp: event.timestamp || new Date().toISOString(),
|
|
264
|
+
session_id: activeSession || event.session_id || null,
|
|
265
|
+
run_id: event.run_id || payload.run_id || null,
|
|
266
|
+
work_item_id: workItemId,
|
|
267
|
+
attempt_id: attemptId,
|
|
268
|
+
causation_id: event.causation_id || payload.causation_id || null,
|
|
269
|
+
correlation_id: event.correlation_id || payload.correlation_id || null,
|
|
270
|
+
payload,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function serializeCanonicalState(state) {
|
|
275
|
+
if (!state || typeof state !== 'object') return null;
|
|
276
|
+
return {
|
|
277
|
+
run: state.run || null,
|
|
278
|
+
workItems: Array.from((state.workItems && state.workItems.values()) || []),
|
|
279
|
+
attempts: Object.fromEntries(Array.from((state.attempts && state.attempts.entries()) || [])),
|
|
280
|
+
workspaces: Array.from((state.workspaces && state.workspaces.values()) || []),
|
|
281
|
+
completedWorkItems: Array.from(state.completedWorkItems || []),
|
|
282
|
+
failedWorkItems: Array.from(state.failedWorkItems || []),
|
|
283
|
+
blockedWorkItems: Array.from(state.blockedWorkItems || []),
|
|
284
|
+
summary: {
|
|
285
|
+
work_item_count: state.workItems instanceof Map ? state.workItems.size : 0,
|
|
286
|
+
attempt_count: state.attempts instanceof Map
|
|
287
|
+
? Array.from(state.attempts.values()).reduce((acc, list) => acc + (Array.isArray(list) ? list.length : 0), 0)
|
|
288
|
+
: 0,
|
|
289
|
+
workspace_count: state.workspaces instanceof Map ? state.workspaces.size : 0,
|
|
290
|
+
completed: state.completedWorkItems instanceof Set ? state.completedWorkItems.size : 0,
|
|
291
|
+
failed: state.failedWorkItems instanceof Set ? state.failedWorkItems.size : 0,
|
|
292
|
+
blocked: state.blockedWorkItems instanceof Set ? state.blockedWorkItems.size : 0,
|
|
293
|
+
},
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function hydrateCanonicalState(serialized) {
|
|
298
|
+
const safe = serialized && typeof serialized === 'object' ? serialized : {};
|
|
299
|
+
return {
|
|
300
|
+
run: safe.run || null,
|
|
301
|
+
workItems: new Map(
|
|
302
|
+
Array.isArray(safe.workItems)
|
|
303
|
+
? safe.workItems
|
|
304
|
+
.filter((item) => item && item.work_item_id)
|
|
305
|
+
.map((item) => [item.work_item_id, item])
|
|
306
|
+
: []
|
|
307
|
+
),
|
|
308
|
+
attempts: new Map(Object.entries(safe.attempts && typeof safe.attempts === 'object' ? safe.attempts : {})),
|
|
309
|
+
workspaces: new Map(
|
|
310
|
+
Array.isArray(safe.workspaces)
|
|
311
|
+
? safe.workspaces
|
|
312
|
+
.filter((item) => item && item.workspace_id)
|
|
313
|
+
.map((item) => [item.workspace_id, item])
|
|
314
|
+
: []
|
|
315
|
+
),
|
|
316
|
+
completedWorkItems: new Set(Array.isArray(safe.completedWorkItems) ? safe.completedWorkItems : []),
|
|
317
|
+
failedWorkItems: new Set(Array.isArray(safe.failedWorkItems) ? safe.failedWorkItems : []),
|
|
318
|
+
blockedWorkItems: new Set(Array.isArray(safe.blockedWorkItems) ? safe.blockedWorkItems : []),
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function mergeCanonicalStateWithRunState(serializedState, runState = {}, activeSession, compiledGraph) {
|
|
323
|
+
const live = hydrateCanonicalState(serializedState);
|
|
324
|
+
const runId = runState.run_id || (live.run && live.run.run_id) || null;
|
|
325
|
+
if (!live.run && runId) {
|
|
326
|
+
live.run = {
|
|
327
|
+
run_id: runId,
|
|
328
|
+
session_id: activeSession || null,
|
|
329
|
+
graph_version: (compiledGraph && compiledGraph.metadata && compiledGraph.metadata.plan_hash) || 'legacy',
|
|
330
|
+
started_at: runState.created_at || new Date().toISOString(),
|
|
331
|
+
ended_at: /completed|failed|blocked|aborted|cancelled/.test(String(runState.status || '')) ? (runState.updated_at || null) : null,
|
|
332
|
+
status: runState.status || 'planned',
|
|
333
|
+
initiator: 'scheduler',
|
|
334
|
+
mode: runState.cursor && runState.cursor.mode === 'task'
|
|
335
|
+
? 'por_tarefa'
|
|
336
|
+
: runState.cursor && runState.cursor.mode === 'wave'
|
|
337
|
+
? 'por_onda'
|
|
338
|
+
: 'completo',
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const compiledNodes = compiledGraph && compiledGraph.nodes && typeof compiledGraph.nodes === 'object'
|
|
343
|
+
? Object.values(compiledGraph.nodes)
|
|
344
|
+
: [];
|
|
345
|
+
for (const node of compiledNodes) {
|
|
346
|
+
if (!node || !node.id || live.workItems.has(node.id)) continue;
|
|
347
|
+
live.workItems.set(node.id, {
|
|
348
|
+
work_item_id: node.id,
|
|
349
|
+
run_id: runId,
|
|
350
|
+
title: node.title || node.id,
|
|
351
|
+
type: 'task',
|
|
352
|
+
depends_on: Array.isArray(node.depends_on) ? node.depends_on : [],
|
|
353
|
+
mutation_scope: Array.isArray(node.mutation_scope) ? node.mutation_scope : [],
|
|
354
|
+
policy_ref: node.policy && node.policy.requires_human_approval ? 'human_approval' : null,
|
|
355
|
+
verify_ref: node.verify && Array.isArray(node.verify.acceptance_refs) ? node.verify.acceptance_refs : [],
|
|
356
|
+
status: 'pending',
|
|
357
|
+
workspace_strategy: node.workspace_strategy || 'inplace',
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const activeTasks = Array.isArray(runState.active_tasks) ? runState.active_tasks.map(String) : [];
|
|
362
|
+
for (const taskId of activeTasks) {
|
|
363
|
+
const currentItem = live.workItems.get(taskId) || {
|
|
364
|
+
work_item_id: taskId,
|
|
365
|
+
run_id: runId,
|
|
366
|
+
title: taskId,
|
|
367
|
+
type: 'task',
|
|
368
|
+
depends_on: [],
|
|
369
|
+
mutation_scope: [],
|
|
370
|
+
policy_ref: null,
|
|
371
|
+
verify_ref: [],
|
|
372
|
+
status: 'pending',
|
|
373
|
+
workspace_strategy: 'inplace',
|
|
374
|
+
};
|
|
375
|
+
live.workItems.set(taskId, { ...currentItem, status: 'running' });
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
for (const blockedId of Array.isArray(runState.pending_checkpoints) ? runState.pending_checkpoints.map(String) : []) {
|
|
379
|
+
if (!live.blockedWorkItems.has(blockedId)) live.blockedWorkItems.add(blockedId);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (Array.isArray(runState.failures)) {
|
|
383
|
+
for (const failure of runState.failures) {
|
|
384
|
+
const ref = failure && typeof failure === 'object'
|
|
385
|
+
? String(failure.work_item_id || failure.task_id || failure.id || '')
|
|
386
|
+
: '';
|
|
387
|
+
if (ref) {
|
|
388
|
+
live.failedWorkItems.add(ref);
|
|
389
|
+
const item = live.workItems.get(ref);
|
|
390
|
+
if (item) live.workItems.set(ref, { ...item, status: 'failed' });
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (Array.isArray(runState.evidence) && runState.evidence.length && live.run) {
|
|
396
|
+
live.run = { ...live.run, evidence_count: runState.evidence.length };
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
for (const completedId of live.completedWorkItems) {
|
|
400
|
+
const item = live.workItems.get(completedId);
|
|
401
|
+
if (item) live.workItems.set(completedId, { ...item, status: 'completed' });
|
|
402
|
+
}
|
|
403
|
+
for (const failedId of live.failedWorkItems) {
|
|
404
|
+
const item = live.workItems.get(failedId);
|
|
405
|
+
if (item) live.workItems.set(failedId, { ...item, status: 'failed' });
|
|
406
|
+
}
|
|
407
|
+
for (const blockedId of live.blockedWorkItems) {
|
|
408
|
+
const item = live.workItems.get(blockedId);
|
|
409
|
+
if (item) live.workItems.set(blockedId, { ...item, status: 'blocked' });
|
|
410
|
+
}
|
|
411
|
+
return live;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function reduceCanonicalRunStateLive(projectRoot, activeSession, options = {}) {
|
|
415
|
+
const runtime = loadRuntimeModule();
|
|
416
|
+
if (!runtime || typeof runtime.reduce !== 'function') return null;
|
|
417
|
+
const currentRun = options.runState || readRunState(projectRoot, activeSession) || null;
|
|
418
|
+
const runId = options.runId || (currentRun && currentRun.run_id) || null;
|
|
419
|
+
let events = readEvents(projectRoot, activeSession);
|
|
420
|
+
if (runId) events = events.filter((event) => !event.run_id || event.run_id === runId);
|
|
421
|
+
const reduced = runtime.reduce(events.map((event) => toRuntimeEventEnvelope(event, activeSession)));
|
|
422
|
+
return mergeCanonicalStateWithRunState(
|
|
423
|
+
serializeCanonicalState(reduced),
|
|
424
|
+
currentRun || {},
|
|
425
|
+
activeSession,
|
|
426
|
+
currentRun && currentRun.compiled_graph ? currentRun.compiled_graph : null
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function reduceCanonicalRunState(projectRoot, activeSession, options = {}) {
|
|
431
|
+
return serializeCanonicalState(reduceCanonicalRunStateLive(projectRoot, activeSession, options));
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function compileExecutionGraphFromArtifacts(projectRoot, activeSession, options = {}) {
|
|
435
|
+
const runtime = loadRuntimeModule();
|
|
436
|
+
const parsers = loadSdkParsers();
|
|
437
|
+
if (!runtime || typeof runtime.compile !== 'function' || typeof runtime.toSerializable !== 'function') {
|
|
438
|
+
throw new Error('Runtime package não está disponível. Rode npm run build:runtime.');
|
|
439
|
+
}
|
|
440
|
+
if (!parsers) {
|
|
441
|
+
throw new Error('Parsers do SDK indisponíveis para compilar o grafo.');
|
|
442
|
+
}
|
|
443
|
+
const artifactPaths = resolveRuntimeArtifactPaths(projectRoot, activeSession);
|
|
444
|
+
const specText = readTextIfExists(artifactPaths.spec);
|
|
445
|
+
const planText = readTextIfExists(artifactPaths.plan);
|
|
446
|
+
if (!specText) throw new Error(`SPEC.md ausente em ${artifactPaths.spec}`);
|
|
447
|
+
if (!planText) throw new Error(`PLAN.md ausente em ${artifactPaths.plan}`);
|
|
448
|
+
const parsedSpec = parsers.parseSpec(specText);
|
|
449
|
+
const parsedPlan = parsers.parsePlan(planText);
|
|
450
|
+
const graph = runtime.compile(parsedPlan, parsedSpec, options.compilerOptions || {});
|
|
451
|
+
const validationErrors = typeof runtime.validateGraph === 'function' ? runtime.validateGraph(graph) : [];
|
|
452
|
+
const compiledGraph = runtime.toSerializable(graph);
|
|
453
|
+
const current = options.runState || readRunState(projectRoot, activeSession) || {};
|
|
454
|
+
const runId = current.run_id || makeRunId();
|
|
455
|
+
const next = writeRunState(projectRoot, activeSession, {
|
|
456
|
+
...current,
|
|
457
|
+
run_id: runId,
|
|
458
|
+
status: current.status || 'planned',
|
|
459
|
+
compiled_graph: compiledGraph,
|
|
460
|
+
graph_version: compiledGraph.metadata && compiledGraph.metadata.plan_hash ? compiledGraph.metadata.plan_hash : 'compiled',
|
|
461
|
+
canonical_state: serializeCanonicalState(
|
|
462
|
+
mergeCanonicalStateWithRunState(
|
|
463
|
+
reduceCanonicalRunState(projectRoot, activeSession, { runState: { ...current, run_id: runId } }),
|
|
464
|
+
{ ...current, run_id: runId },
|
|
465
|
+
activeSession,
|
|
466
|
+
compiledGraph
|
|
467
|
+
)
|
|
468
|
+
),
|
|
469
|
+
projections: current.projections || {},
|
|
470
|
+
});
|
|
471
|
+
appendEvent(projectRoot, activeSession, {
|
|
472
|
+
type: 'GraphCompiled',
|
|
473
|
+
run_id: next.run_id,
|
|
474
|
+
payload: {
|
|
475
|
+
node_count: compiledGraph.metadata && compiledGraph.metadata.node_count || 0,
|
|
476
|
+
wave_count: compiledGraph.metadata && compiledGraph.metadata.wave_count || 0,
|
|
477
|
+
plan_hash: compiledGraph.metadata && compiledGraph.metadata.plan_hash || null,
|
|
478
|
+
spec_hash: compiledGraph.metadata && compiledGraph.metadata.spec_hash || null,
|
|
479
|
+
},
|
|
480
|
+
});
|
|
481
|
+
return {
|
|
482
|
+
run: next,
|
|
483
|
+
graph: compiledGraph,
|
|
484
|
+
validationErrors,
|
|
485
|
+
parsedPlan,
|
|
486
|
+
parsedSpec,
|
|
487
|
+
paths: artifactPaths,
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function compileVerificationSuiteFromArtifacts(projectRoot, activeSession, options = {}) {
|
|
492
|
+
const runtime = loadRuntimeModule();
|
|
493
|
+
const parsers = loadSdkParsers();
|
|
494
|
+
if (!runtime || typeof runtime.compileVerification !== 'function') {
|
|
495
|
+
throw new Error('Runtime package não está disponível. Rode npm run build:runtime.');
|
|
496
|
+
}
|
|
497
|
+
if (!parsers) {
|
|
498
|
+
throw new Error('Parsers do SDK indisponíveis para compilar verification suite.');
|
|
499
|
+
}
|
|
500
|
+
const artifactPaths = resolveRuntimeArtifactPaths(projectRoot, activeSession);
|
|
501
|
+
const specText = readTextIfExists(artifactPaths.spec);
|
|
502
|
+
const planText = readTextIfExists(artifactPaths.plan);
|
|
503
|
+
if (!specText || !planText) {
|
|
504
|
+
throw new Error('SPEC.md e PLAN.md são obrigatórios para compilar a suite de verificação.');
|
|
505
|
+
}
|
|
506
|
+
const suite = runtime.compileVerification(parsers.parseSpec(specText), parsers.parsePlan(planText), options.compilerOptions || {});
|
|
507
|
+
const current = options.runState || readRunState(projectRoot, activeSession) || {};
|
|
508
|
+
const runId = current.run_id || makeRunId();
|
|
509
|
+
const next = writeRunState(projectRoot, activeSession, {
|
|
510
|
+
...current,
|
|
511
|
+
run_id: runId,
|
|
512
|
+
status: current.status || 'planned',
|
|
513
|
+
verification_suite: suite,
|
|
514
|
+
});
|
|
515
|
+
appendEvent(projectRoot, activeSession, {
|
|
516
|
+
type: 'verification_suite_compiled',
|
|
517
|
+
run_id: next.run_id,
|
|
518
|
+
payload: {
|
|
519
|
+
total_checks: Array.isArray(suite.checks) ? suite.checks.length : 0,
|
|
520
|
+
spec_hash: suite.spec_hash || null,
|
|
521
|
+
plan_hash: suite.plan_hash || null,
|
|
522
|
+
},
|
|
523
|
+
});
|
|
524
|
+
return { run: next, suite, paths: artifactPaths };
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
function runResultFromRunState(runState) {
|
|
528
|
+
const canonical = runState && runState.canonical_state && typeof runState.canonical_state === 'object'
|
|
529
|
+
? hydrateCanonicalState(runState.canonical_state)
|
|
530
|
+
: null;
|
|
531
|
+
return {
|
|
532
|
+
run_id: runState && runState.run_id ? runState.run_id : makeRunId(),
|
|
533
|
+
status: String(runState && runState.status || 'planned'),
|
|
534
|
+
completed: canonical ? Array.from(canonical.completedWorkItems || []) : [],
|
|
535
|
+
failed: canonical ? Array.from(canonical.failedWorkItems || []) : [],
|
|
536
|
+
blocked: canonical ? Array.from(canonical.blockedWorkItems || []) : [],
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function gateStoragePath(projectRoot, activeSession) {
|
|
541
|
+
return activeSession
|
|
542
|
+
? path.join(projectRoot, '.oxe', ...String(activeSession).split('/'), 'execution', 'GATES.json')
|
|
543
|
+
: path.join(projectRoot, '.oxe', 'execution', 'GATES.json');
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function readRuntimeGates(projectRoot, activeSession, options = {}) {
|
|
547
|
+
const gatesPath = gateStoragePath(projectRoot, activeSession);
|
|
548
|
+
const raw = readJsonIfExists(gatesPath);
|
|
549
|
+
const gates = Array.isArray(raw) ? raw : [];
|
|
550
|
+
const runId = options.runId || null;
|
|
551
|
+
const gateSlaHours = Number(options.gateSlaHours || options.gate_sla_hours || 24);
|
|
552
|
+
const status = String(options.status || 'all');
|
|
553
|
+
const scope = options.scope ? String(options.scope) : null;
|
|
554
|
+
const workItemId = options.task || options.workItemId || null;
|
|
555
|
+
const action = options.action || null;
|
|
556
|
+
const filtered = gates.filter((gate) => {
|
|
557
|
+
if (runId && gate.run_id && gate.run_id !== runId) return false;
|
|
558
|
+
if (scope && gate.scope !== scope) return false;
|
|
559
|
+
if (workItemId && gate.work_item_id !== workItemId) return false;
|
|
560
|
+
if (action && gate.action !== action) return false;
|
|
561
|
+
if (status !== 'all') {
|
|
562
|
+
const requestedAt = Date.parse(String(gate.requested_at || ''));
|
|
563
|
+
const isStale = gate.status === 'pending'
|
|
564
|
+
&& Number.isFinite(requestedAt)
|
|
565
|
+
&& Date.now() - requestedAt > gateSlaHours * 60 * 60 * 1000;
|
|
566
|
+
if (status === 'stale') return isStale;
|
|
567
|
+
return gate.status === status;
|
|
568
|
+
}
|
|
569
|
+
return true;
|
|
570
|
+
});
|
|
571
|
+
const pending = filtered.filter((gate) => gate && gate.status === 'pending');
|
|
572
|
+
const stalePending = pending.filter((gate) => {
|
|
573
|
+
const requestedAt = Date.parse(String(gate.requested_at || ''));
|
|
574
|
+
return Number.isFinite(requestedAt) && Date.now() - requestedAt > gateSlaHours * 60 * 60 * 1000;
|
|
575
|
+
});
|
|
576
|
+
const resolvedRecent = filtered.filter((gate) => {
|
|
577
|
+
if (gate.status !== 'resolved') return false;
|
|
578
|
+
const resolvedAt = Date.parse(String(gate.resolved_at || ''));
|
|
579
|
+
return Number.isFinite(resolvedAt) && Date.now() - resolvedAt <= gateSlaHours * 60 * 60 * 1000;
|
|
580
|
+
});
|
|
581
|
+
const byRun = {};
|
|
582
|
+
const byScope = {};
|
|
583
|
+
for (const gate of filtered) {
|
|
584
|
+
const runKey = gate.run_id || 'unscoped';
|
|
585
|
+
const scopeKey = gate.scope || 'unknown';
|
|
586
|
+
byRun[runKey] = (byRun[runKey] || 0) + 1;
|
|
587
|
+
byScope[scopeKey] = (byScope[scopeKey] || 0) + 1;
|
|
588
|
+
}
|
|
589
|
+
return {
|
|
590
|
+
path: gatesPath,
|
|
591
|
+
total: filtered.length,
|
|
592
|
+
gateSlaHours,
|
|
593
|
+
pending,
|
|
594
|
+
stalePending,
|
|
595
|
+
staleCount: stalePending.length,
|
|
596
|
+
resolvedRecent,
|
|
597
|
+
byRun,
|
|
598
|
+
byScope,
|
|
599
|
+
all: filtered,
|
|
600
|
+
filters: { runId, status, scope, workItemId, action },
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
async function resolveRuntimeGate(projectRoot, activeSession, options = {}) {
|
|
605
|
+
const runtime = loadRuntimeModule();
|
|
606
|
+
if (!runtime || typeof runtime.GateManager !== 'function') {
|
|
607
|
+
throw new Error('Runtime package não está disponível. Rode npm run build:runtime.');
|
|
608
|
+
}
|
|
609
|
+
const current = readRunState(projectRoot, activeSession);
|
|
610
|
+
const runId = options.runId || (current && current.run_id);
|
|
611
|
+
if (!runId) throw new Error('Nenhum run ativo para resolver gates.');
|
|
612
|
+
const manager = new runtime.GateManager(projectRoot, activeSession || null, runId);
|
|
613
|
+
const gate = manager.get(String(options.gateId || ''));
|
|
614
|
+
if (!gate) throw new Error(`Gate ${options.gateId} não encontrado.`);
|
|
615
|
+
const mappedDecision = options.decision === 'approve'
|
|
616
|
+
? 'approved'
|
|
617
|
+
: options.decision === 'reject'
|
|
618
|
+
? 'rejected'
|
|
619
|
+
: 'approved_with_caveats';
|
|
620
|
+
const resolved = await runtime.resolveGate(manager, gate.gate_id, {
|
|
621
|
+
decision: mappedDecision,
|
|
622
|
+
actor: String(options.actor || ''),
|
|
623
|
+
reason: options.reason ? String(options.reason) : undefined,
|
|
624
|
+
});
|
|
625
|
+
const queue = readRuntimeGates(projectRoot, activeSession, {
|
|
626
|
+
runId,
|
|
627
|
+
gateSlaHours: options.gateSlaHours || options.gate_sla_hours || 24,
|
|
628
|
+
});
|
|
629
|
+
return {
|
|
630
|
+
gate: resolved,
|
|
631
|
+
queue,
|
|
632
|
+
impact: {
|
|
633
|
+
pendingRemaining: queue.pending.length,
|
|
634
|
+
staleRemaining: queue.staleCount || 0,
|
|
635
|
+
runId,
|
|
636
|
+
},
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
function readRuntimeMultiAgentStatus(projectRoot, activeSession, options = {}) {
|
|
641
|
+
const runtime = loadRuntimeModule();
|
|
642
|
+
const current = readRunState(projectRoot, activeSession);
|
|
643
|
+
const runId = options.runId || (current && current.run_id) || null;
|
|
644
|
+
if (!runId) {
|
|
645
|
+
return {
|
|
646
|
+
path: null,
|
|
647
|
+
enabled: false,
|
|
648
|
+
runId: null,
|
|
649
|
+
mode: null,
|
|
650
|
+
workspaceIsolationEnforced: false,
|
|
651
|
+
agents: [],
|
|
652
|
+
ownership: [],
|
|
653
|
+
orphanReassignments: [],
|
|
654
|
+
handoffs: [],
|
|
655
|
+
arbitrationResults: [],
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
const runDir = path.join(projectRoot, '.oxe', 'runs', runId);
|
|
659
|
+
const statePath = path.join(runDir, 'multi-agent-state.json');
|
|
660
|
+
const handoffsPath = path.join(runDir, 'handoffs.json');
|
|
661
|
+
const arbitrationPath = path.join(runDir, 'arbitration-results.json');
|
|
662
|
+
const state = runtime && typeof runtime.loadMultiAgentState === 'function'
|
|
663
|
+
? runtime.loadMultiAgentState(projectRoot, runId)
|
|
664
|
+
: readJsonIfExists(statePath);
|
|
665
|
+
const handoffs = readJsonIfExists(handoffsPath);
|
|
666
|
+
const arbitrationResults = readJsonIfExists(arbitrationPath);
|
|
667
|
+
return {
|
|
668
|
+
path: statePath,
|
|
669
|
+
enabled: Boolean(state),
|
|
670
|
+
runId,
|
|
671
|
+
mode: state && state.mode ? state.mode : null,
|
|
672
|
+
workspaceIsolationEnforced: Boolean(state && state.workspace_isolation_enforced),
|
|
673
|
+
agents: state && Array.isArray(state.agent_results) ? state.agent_results : [],
|
|
674
|
+
ownership: state && Array.isArray(state.ownership) ? state.ownership : [],
|
|
675
|
+
orphanReassignments: state && Array.isArray(state.orphan_reassignments) ? state.orphan_reassignments : [],
|
|
676
|
+
handoffs: Array.isArray(handoffs) ? handoffs : [],
|
|
677
|
+
arbitrationResults: Array.isArray(arbitrationResults) ? arbitrationResults : [],
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
function loadRuntimeVerificationArtifacts(projectRoot, runState) {
|
|
682
|
+
const runtime = loadRuntimeModule();
|
|
683
|
+
if (!runtime || !runState || !runState.run_id) {
|
|
684
|
+
return {
|
|
685
|
+
manifest: null,
|
|
686
|
+
residualRisks: null,
|
|
687
|
+
evidenceCoverage: null,
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
const manifest = runState.verification_manifest
|
|
691
|
+
|| (typeof runtime.loadManifest === 'function' ? runtime.loadManifest(projectRoot, runState.run_id) : null)
|
|
692
|
+
|| null;
|
|
693
|
+
const residualRisks = runState.residual_risks
|
|
694
|
+
|| (typeof runtime.loadRiskLedger === 'function' ? runtime.loadRiskLedger(projectRoot, runState.run_id) : null)
|
|
695
|
+
|| null;
|
|
696
|
+
const evidenceCoverage = runState.verification_evidence_coverage
|
|
697
|
+
|| (typeof runtime.loadEvidenceCoverage === 'function' ? runtime.loadEvidenceCoverage(projectRoot, runState.run_id) : null)
|
|
698
|
+
|| (manifest && typeof runtime.summarizeEvidenceCoverage === 'function'
|
|
699
|
+
? runtime.summarizeEvidenceCoverage(manifest)
|
|
700
|
+
: null)
|
|
701
|
+
|| null;
|
|
702
|
+
return { manifest, residualRisks, evidenceCoverage };
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
function countVerificationEvidenceRefs(runState, verificationArtifacts) {
|
|
706
|
+
if (verificationArtifacts && verificationArtifacts.manifest && Array.isArray(verificationArtifacts.manifest.checks)) {
|
|
707
|
+
return verificationArtifacts.manifest.checks.reduce((sum, check) => {
|
|
708
|
+
return sum + (Array.isArray(check.evidence_refs) ? check.evidence_refs.length : 0);
|
|
709
|
+
}, 0);
|
|
710
|
+
}
|
|
711
|
+
if (Array.isArray(runState && runState.verification_results)) {
|
|
712
|
+
return runState.verification_results.reduce((sum, result) => {
|
|
713
|
+
return sum + (Array.isArray(result.evidence_refs) ? result.evidence_refs.length : 0);
|
|
714
|
+
}, 0);
|
|
715
|
+
}
|
|
716
|
+
return 0;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
function buildRuntimeModeStatus(runState) {
|
|
720
|
+
if (!runState) {
|
|
721
|
+
return {
|
|
722
|
+
runtime_mode: 'legacy',
|
|
723
|
+
fallback_mode: 'legacy',
|
|
724
|
+
source: 'absent',
|
|
725
|
+
reason: 'Nenhum ACTIVE-RUN encontrado para o escopo atual.',
|
|
726
|
+
enterprise_available: false,
|
|
727
|
+
fallback_recorded: true,
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
const hasEnterpriseArtifacts = Boolean(
|
|
731
|
+
runState.compiled_graph
|
|
732
|
+
|| runState.canonical_state
|
|
733
|
+
|| runState.verification_manifest
|
|
734
|
+
|| runState.verification_evidence_coverage
|
|
735
|
+
|| runState.delivery
|
|
736
|
+
|| runState.recovery_summary
|
|
737
|
+
);
|
|
738
|
+
return {
|
|
739
|
+
runtime_mode: hasEnterpriseArtifacts ? 'enterprise' : 'legacy',
|
|
740
|
+
fallback_mode: hasEnterpriseArtifacts ? 'none' : 'legacy',
|
|
741
|
+
source: hasEnterpriseArtifacts ? 'canonical_state' : 'legacy_state',
|
|
742
|
+
reason: hasEnterpriseArtifacts
|
|
743
|
+
? 'Run com artefatos canónicos do runtime enterprise persistidos.'
|
|
744
|
+
: 'Run sem artefatos canónicos do runtime; fluxo degradado para legado.',
|
|
745
|
+
enterprise_available: hasEnterpriseArtifacts,
|
|
746
|
+
fallback_recorded: !hasEnterpriseArtifacts,
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
function buildRuntimeProviderCatalog(projectRoot) {
|
|
751
|
+
const runtime = loadRuntimeModule();
|
|
752
|
+
if (!runtime || typeof runtime.PluginRegistry !== 'function') {
|
|
753
|
+
return {
|
|
754
|
+
available: false,
|
|
755
|
+
plugin_dir: path.join(projectRoot, '.oxe', 'plugins'),
|
|
756
|
+
loaded_capabilities: [],
|
|
757
|
+
loaded_plugins: [],
|
|
758
|
+
load_errors: ['Runtime package não está disponível. Rode npm run build:runtime.'],
|
|
759
|
+
summary: null,
|
|
760
|
+
matrix: null,
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
const registry = new runtime.PluginRegistry();
|
|
764
|
+
const pluginDir = path.join(projectRoot, '.oxe', 'plugins');
|
|
765
|
+
const loadedCapabilities = typeof registry.registerProjectCapabilities === 'function'
|
|
766
|
+
? registry.registerProjectCapabilities(projectRoot)
|
|
767
|
+
: [];
|
|
768
|
+
const loadedPlugins = typeof registry.loadFromDirectory === 'function'
|
|
769
|
+
? registry.loadFromDirectory(pluginDir)
|
|
770
|
+
: [];
|
|
771
|
+
const loadErrors = typeof registry.loadErrorsSnapshot === 'function'
|
|
772
|
+
? registry.loadErrorsSnapshot()
|
|
773
|
+
: [];
|
|
774
|
+
const summary = typeof runtime.registrySummary === 'function'
|
|
775
|
+
? runtime.registrySummary(registry)
|
|
776
|
+
: (typeof registry.summary === 'function' ? registry.summary() : null);
|
|
777
|
+
const matrix = typeof runtime.resolveCapabilityMatrix === 'function'
|
|
778
|
+
? runtime.resolveCapabilityMatrix(registry)
|
|
779
|
+
: (typeof registry.capabilityMatrix === 'function' ? registry.capabilityMatrix() : null);
|
|
780
|
+
return {
|
|
781
|
+
available: true,
|
|
782
|
+
plugin_dir: pluginDir,
|
|
783
|
+
loaded_capabilities: loadedCapabilities,
|
|
784
|
+
loaded_plugins: loadedPlugins,
|
|
785
|
+
load_errors: loadErrors,
|
|
786
|
+
summary,
|
|
787
|
+
matrix,
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
function buildRecoveryConsistency(projectRoot, activeSession, runState, journal, verificationArtifacts) {
|
|
792
|
+
const op = operationalPaths(projectRoot, activeSession);
|
|
793
|
+
const activeRunRef = readJsonIfExists(op.activeRun);
|
|
794
|
+
const runFile = runState && runState.run_id ? path.join(op.runsDir, `${runState.run_id}.json`) : null;
|
|
795
|
+
const runDir = runState && runState.run_id ? path.join(projectRoot, '.oxe', 'runs', runState.run_id) : null;
|
|
796
|
+
const allEvents = readEvents(projectRoot, activeSession);
|
|
797
|
+
const runEvents = runState && runState.run_id ? allEvents.filter((event) => event.run_id === runState.run_id) : [];
|
|
798
|
+
const issues = [];
|
|
799
|
+
if (!activeRunRef || activeRunRef.run_id !== (runState && runState.run_id)) {
|
|
800
|
+
issues.push('ACTIVE-RUN.json não referencia o mesmo run persistido em .oxe/runs/.');
|
|
801
|
+
}
|
|
802
|
+
if (!runFile || !fs.existsSync(runFile)) {
|
|
803
|
+
issues.push('Arquivo canónico da run ausente em .oxe/runs/<run>.json.');
|
|
804
|
+
}
|
|
805
|
+
if (!journal) {
|
|
806
|
+
issues.push('Journal ausente para recover/replay.');
|
|
807
|
+
}
|
|
808
|
+
if (runEvents.length === 0) {
|
|
809
|
+
issues.push('Nenhum evento NDJSON encontrado para a run ativa.');
|
|
810
|
+
}
|
|
811
|
+
if (!runState || !runState.canonical_state) {
|
|
812
|
+
issues.push('canonical_state ausente no ACTIVE-RUN.');
|
|
813
|
+
}
|
|
814
|
+
const pendingGates = readRuntimeGates(projectRoot, activeSession, { runId: runState && runState.run_id ? runState.run_id : null });
|
|
815
|
+
const policyDecisionPath = runState && runState.run_id ? path.join(projectRoot, '.oxe', 'runs', runState.run_id, 'policy-decisions.json') : null;
|
|
816
|
+
const policyDecisions = policyDecisionPath && fs.existsSync(policyDecisionPath)
|
|
817
|
+
? (Array.isArray(readJsonIfExists(policyDecisionPath)) ? readJsonIfExists(policyDecisionPath) : [])
|
|
818
|
+
: [];
|
|
819
|
+
const promotionRecordPath = runDir ? path.join(runDir, 'promotion-record.json') : null;
|
|
820
|
+
const promotionRecord = promotionRecordPath ? readJsonIfExists(promotionRecordPath) : null;
|
|
821
|
+
const attempts = runState && runState.canonical_state && runState.canonical_state.attempts && typeof runState.canonical_state.attempts === 'object'
|
|
822
|
+
? runState.canonical_state.attempts
|
|
823
|
+
: {};
|
|
824
|
+
const incompleteAttempts = Object.entries(attempts)
|
|
825
|
+
.flatMap(([workItemId, entries]) => Array.isArray(entries) ? entries.map((entry) => ({ workItemId, entry })) : [])
|
|
826
|
+
.filter(({ entry }) => {
|
|
827
|
+
const outcome = entry && typeof entry === 'object' ? String(entry.outcome || '') : '';
|
|
828
|
+
return !outcome || outcome === 'running' || outcome === 'pending';
|
|
829
|
+
});
|
|
830
|
+
if (incompleteAttempts.length > 0) {
|
|
831
|
+
issues.push(`${incompleteAttempts.length} tentativa(s) incompleta(s) ainda persistida(s) no estado canônico.`);
|
|
832
|
+
}
|
|
833
|
+
return {
|
|
834
|
+
active_run_path: op.activeRun,
|
|
835
|
+
run_file_path: runFile,
|
|
836
|
+
run_dir: runDir,
|
|
837
|
+
journal_path: runDir ? path.join(runDir, 'journal.json') : null,
|
|
838
|
+
events_path: op.events,
|
|
839
|
+
gates_path: op.gates,
|
|
840
|
+
policy_decisions_path: policyDecisionPath,
|
|
841
|
+
verification_manifest_path: runDir ? path.join(runDir, 'verification-manifest.json') : null,
|
|
842
|
+
residual_risk_path: runDir ? path.join(runDir, 'residual-risk-ledger.json') : null,
|
|
843
|
+
evidence_coverage_path: runDir ? path.join(runDir, 'evidence-coverage.json') : null,
|
|
844
|
+
promotion_record_path: promotionRecordPath,
|
|
845
|
+
run_id: runState && runState.run_id ? runState.run_id : null,
|
|
846
|
+
active_run_synced: Boolean(activeRunRef && runState && activeRunRef.run_id === runState.run_id),
|
|
847
|
+
run_file_exists: Boolean(runFile && fs.existsSync(runFile)),
|
|
848
|
+
journal_exists: Boolean(journal),
|
|
849
|
+
event_count: runEvents.length,
|
|
850
|
+
issues,
|
|
851
|
+
pending_gates_rehydrated: pendingGates.pending.length,
|
|
852
|
+
policy_decisions_rehydrated: Array.isArray(policyDecisions) ? policyDecisions.length : 0,
|
|
853
|
+
evidence_refs_tracked: countVerificationEvidenceRefs(runState, verificationArtifacts),
|
|
854
|
+
verification_artifacts_present: Boolean(verificationArtifacts && verificationArtifacts.manifest),
|
|
855
|
+
promotion_attempt_present: Boolean(promotionRecord),
|
|
856
|
+
promotion_status: promotionRecord && promotionRecord.status ? promotionRecord.status : null,
|
|
857
|
+
incomplete_attempts: incompleteAttempts.map(({ workItemId, entry }) => ({
|
|
858
|
+
work_item_id: workItemId,
|
|
859
|
+
attempt_id: entry && typeof entry === 'object' ? entry.attempt_id || null : null,
|
|
860
|
+
outcome: entry && typeof entry === 'object' ? entry.outcome || null : null,
|
|
861
|
+
})),
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
function writeRecoverySummaryMarkdown(projectRoot, activeSession, runState, recoverySummary) {
|
|
866
|
+
const op = operationalPaths(projectRoot, activeSession);
|
|
867
|
+
const summaryPath = path.join(op.executionRoot, 'RECOVERY-SUMMARY.md');
|
|
868
|
+
const lines = [
|
|
869
|
+
'# OXE — Recovery Summary',
|
|
870
|
+
'',
|
|
871
|
+
`- **Data:** ${new Date().toISOString()}`,
|
|
872
|
+
`- **Run:** ${runState && runState.run_id ? runState.run_id : '—'}`,
|
|
873
|
+
`- **Estado pós-recover:** ${runState && runState.status ? runState.status : '—'}`,
|
|
874
|
+
`- **Journal:** ${recoverySummary.journal_state || '—'}`,
|
|
875
|
+
`- **Pending gates reidratados:** ${recoverySummary.consistency && recoverySummary.consistency.pending_gates_rehydrated != null ? recoverySummary.consistency.pending_gates_rehydrated : 0}`,
|
|
876
|
+
`- **Policy decisions reidratadas:** ${recoverySummary.consistency && recoverySummary.consistency.policy_decisions_rehydrated != null ? recoverySummary.consistency.policy_decisions_rehydrated : 0}`,
|
|
877
|
+
`- **Evidence refs rastreados:** ${recoverySummary.consistency && recoverySummary.consistency.evidence_refs_tracked != null ? recoverySummary.consistency.evidence_refs_tracked : 0}`,
|
|
878
|
+
`- **Promotion attempt:** ${recoverySummary.consistency && recoverySummary.consistency.promotion_status ? recoverySummary.consistency.promotion_status : '—'}`,
|
|
879
|
+
'',
|
|
880
|
+
'## Work items órfãos',
|
|
881
|
+
'',
|
|
882
|
+
...(Array.isArray(recoverySummary.orphan_work_items) && recoverySummary.orphan_work_items.length
|
|
883
|
+
? recoverySummary.orphan_work_items.map((item) => `- ${item}`)
|
|
884
|
+
: ['- Nenhum']),
|
|
885
|
+
'',
|
|
886
|
+
'## Tentativas incompletas',
|
|
887
|
+
'',
|
|
888
|
+
...(recoverySummary.consistency && Array.isArray(recoverySummary.consistency.incomplete_attempts) && recoverySummary.consistency.incomplete_attempts.length
|
|
889
|
+
? recoverySummary.consistency.incomplete_attempts.map((item) => `- ${item.work_item_id} · ${item.attempt_id || 'attempt'} · ${item.outcome || 'unknown'}`)
|
|
890
|
+
: ['- Nenhuma']),
|
|
891
|
+
'',
|
|
892
|
+
'## Consistência',
|
|
893
|
+
'',
|
|
894
|
+
...(recoverySummary.consistency && Array.isArray(recoverySummary.consistency.issues) && recoverySummary.consistency.issues.length
|
|
895
|
+
? recoverySummary.consistency.issues.map((issue) => `- ${issue}`)
|
|
896
|
+
: ['- Sem inconsistências críticas detectadas.']),
|
|
897
|
+
];
|
|
898
|
+
ensureDirForFile(summaryPath);
|
|
899
|
+
fs.writeFileSync(summaryPath, lines.join('\n') + '\n', 'utf8');
|
|
900
|
+
return summaryPath;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
async function runRuntimeVerify(projectRoot, activeSession, options = {}) {
|
|
904
|
+
const runtime = loadRuntimeModule();
|
|
905
|
+
const parsers = loadSdkParsers();
|
|
906
|
+
if (!runtime || typeof runtime.verifyRun !== 'function') {
|
|
907
|
+
throw new Error('Runtime package não está disponível. Rode npm run build:runtime.');
|
|
908
|
+
}
|
|
909
|
+
if (!parsers) {
|
|
910
|
+
throw new Error('Parsers do SDK indisponíveis para executar verification suite.');
|
|
911
|
+
}
|
|
912
|
+
let current = options.runState || readRunState(projectRoot, activeSession);
|
|
913
|
+
if (!current) {
|
|
914
|
+
current = compileExecutionGraphFromArtifacts(projectRoot, activeSession).run;
|
|
915
|
+
}
|
|
916
|
+
if (!current.verification_suite) {
|
|
917
|
+
current = compileVerificationSuiteFromArtifacts(projectRoot, activeSession, { runState: current }).run;
|
|
918
|
+
}
|
|
919
|
+
const artifactPaths = resolveRuntimeArtifactPaths(projectRoot, activeSession);
|
|
920
|
+
const specText = readTextIfExists(artifactPaths.spec);
|
|
921
|
+
const planText = readTextIfExists(artifactPaths.plan);
|
|
922
|
+
if (!specText || !planText) {
|
|
923
|
+
throw new Error('SPEC.md e PLAN.md são obrigatórios para runtime verify.');
|
|
924
|
+
}
|
|
925
|
+
const parsedSpec = parsers.parseSpec(specText);
|
|
926
|
+
const parsedPlan = parsers.parsePlan(planText);
|
|
927
|
+
const suite = current.verification_suite
|
|
928
|
+
|| runtime.compileVerification(parsedSpec, parsedPlan, options.compilerOptions || {});
|
|
929
|
+
const registry = buildRuntimePluginRegistry(projectRoot);
|
|
930
|
+
const evidenceStore = new runtime.EvidenceStore(projectRoot);
|
|
931
|
+
const targetWorkItem = options.workItemId
|
|
932
|
+
|| options.task
|
|
933
|
+
|| (current.cursor && current.cursor.task)
|
|
934
|
+
|| (Array.isArray(current.active_tasks) && current.active_tasks[0])
|
|
935
|
+
|| 'run';
|
|
936
|
+
appendEvent(projectRoot, activeSession, {
|
|
937
|
+
type: 'VerificationStarted',
|
|
938
|
+
run_id: current.run_id,
|
|
939
|
+
work_item_id: targetWorkItem,
|
|
940
|
+
payload: {
|
|
941
|
+
total_checks: Array.isArray(suite.checks) ? suite.checks.length : 0,
|
|
942
|
+
},
|
|
943
|
+
});
|
|
944
|
+
const report = await runtime.verifyRun({
|
|
945
|
+
projectRoot,
|
|
946
|
+
runId: current.run_id,
|
|
947
|
+
workItemId: String(targetWorkItem),
|
|
948
|
+
cwd: options.cwd || projectRoot,
|
|
949
|
+
suite,
|
|
950
|
+
pluginRegistry: registry || undefined,
|
|
951
|
+
evidenceStore,
|
|
952
|
+
attemptNumber: options.attemptNumber || 1,
|
|
953
|
+
timeoutMs: options.timeoutMs,
|
|
954
|
+
});
|
|
955
|
+
const nextStatus = report.status === 'passed'
|
|
956
|
+
? current.status === 'completed' ? 'completed' : 'running'
|
|
957
|
+
: report.status === 'failed'
|
|
958
|
+
? 'failed'
|
|
959
|
+
: 'blocked';
|
|
960
|
+
const next = writeRunState(projectRoot, activeSession, {
|
|
961
|
+
...current,
|
|
962
|
+
status: nextStatus,
|
|
963
|
+
verification_suite: suite,
|
|
964
|
+
verification_results: report.verification_results,
|
|
965
|
+
verification_check_results: report.check_results,
|
|
966
|
+
verification_manifest: report.manifest,
|
|
967
|
+
residual_risks: report.risk_ledger,
|
|
968
|
+
verification_evidence_coverage: report.evidence_coverage,
|
|
969
|
+
verification_gaps: report.gaps,
|
|
970
|
+
});
|
|
971
|
+
appendEvent(projectRoot, activeSession, {
|
|
972
|
+
type: 'VerificationCompleted',
|
|
973
|
+
run_id: next.run_id,
|
|
974
|
+
work_item_id: targetWorkItem,
|
|
975
|
+
payload: {
|
|
976
|
+
status: report.status,
|
|
977
|
+
total_checks: report.manifest && report.manifest.summary ? report.manifest.summary.total : 0,
|
|
978
|
+
fail: report.manifest && report.manifest.summary ? report.manifest.summary.fail : 0,
|
|
979
|
+
error: report.manifest && report.manifest.summary ? report.manifest.summary.error : 0,
|
|
980
|
+
gaps: report.gaps,
|
|
981
|
+
},
|
|
982
|
+
});
|
|
983
|
+
const projected = projectRuntimeArtifacts(projectRoot, activeSession, { runState: next, write: true });
|
|
984
|
+
return {
|
|
985
|
+
run: projected.run,
|
|
986
|
+
report,
|
|
987
|
+
projected,
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
function projectRuntimeArtifacts(projectRoot, activeSession, options = {}) {
|
|
992
|
+
const runtime = loadRuntimeModule();
|
|
993
|
+
if (!runtime || typeof runtime.ProjectionEngine !== 'function' || typeof runtime.fromSerializable !== 'function') {
|
|
994
|
+
throw new Error('Runtime package não está disponível. Rode npm run build:runtime.');
|
|
995
|
+
}
|
|
996
|
+
let current = options.runState || readRunState(projectRoot, activeSession);
|
|
997
|
+
if (!current) {
|
|
998
|
+
current = writeRunState(projectRoot, activeSession, {});
|
|
999
|
+
}
|
|
1000
|
+
if (!current.compiled_graph) {
|
|
1001
|
+
current = compileExecutionGraphFromArtifacts(projectRoot, activeSession, { runState: current }).run;
|
|
1002
|
+
}
|
|
1003
|
+
const graph = runtime.fromSerializable(current.compiled_graph);
|
|
1004
|
+
const canonicalLive = current.canonical_state
|
|
1005
|
+
? mergeCanonicalStateWithRunState(current.canonical_state, current, activeSession, current.compiled_graph)
|
|
1006
|
+
: reduceCanonicalRunStateLive(projectRoot, activeSession, { runState: current });
|
|
1007
|
+
if (!canonicalLive) {
|
|
1008
|
+
throw new Error('Não foi possível reconstruir o estado canônico da run.');
|
|
1009
|
+
}
|
|
1010
|
+
const canonicalState = serializeCanonicalState(canonicalLive);
|
|
1011
|
+
const projector = new runtime.ProjectionEngine();
|
|
1012
|
+
const verificationResults = Array.isArray(current.verification_results) ? current.verification_results : [];
|
|
1013
|
+
const verificationCheckResults = Array.isArray(current.verification_check_results) ? current.verification_check_results : [];
|
|
1014
|
+
const verificationArtifacts = loadRuntimeVerificationArtifacts(projectRoot, current);
|
|
1015
|
+
const projections = {
|
|
1016
|
+
plan: projector.projectPlan(canonicalLive, graph),
|
|
1017
|
+
verify: projector.projectVerify(
|
|
1018
|
+
canonicalLive,
|
|
1019
|
+
verificationResults,
|
|
1020
|
+
verificationCheckResults,
|
|
1021
|
+
verificationArtifacts.manifest,
|
|
1022
|
+
verificationArtifacts.residualRisks,
|
|
1023
|
+
verificationArtifacts.evidenceCoverage
|
|
1024
|
+
),
|
|
1025
|
+
state: projector.projectState(canonicalLive),
|
|
1026
|
+
runSummary: projector.projectRunSummary(canonicalLive),
|
|
1027
|
+
commitSummary: typeof projector.projectCommitSummary === 'function'
|
|
1028
|
+
? projector.projectCommitSummary(canonicalLive, graph)
|
|
1029
|
+
: projector.projectRunSummary(canonicalLive),
|
|
1030
|
+
promotionSummary: typeof projector.projectPromotionSummary === 'function'
|
|
1031
|
+
? projector.projectPromotionSummary(canonicalLive, graph)
|
|
1032
|
+
: projector.projectPRSummary(canonicalLive, graph),
|
|
1033
|
+
prSummary: projector.projectPRSummary(canonicalLive, graph),
|
|
1034
|
+
};
|
|
1035
|
+
const paths = resolveRuntimeArtifactPaths(projectRoot, activeSession);
|
|
1036
|
+
const op = operationalPaths(projectRoot, activeSession);
|
|
1037
|
+
const projectionRefs = {
|
|
1038
|
+
plan_ref: path.relative(projectRoot, paths.plan).replace(/\\/g, '/'),
|
|
1039
|
+
verify_ref: path.relative(projectRoot, paths.verify).replace(/\\/g, '/'),
|
|
1040
|
+
state_ref: path.relative(projectRoot, paths.state).replace(/\\/g, '/'),
|
|
1041
|
+
run_summary_ref: path.relative(projectRoot, path.join(op.executionRoot, 'RUN-SUMMARY.md')).replace(/\\/g, '/'),
|
|
1042
|
+
commit_summary_ref: path.relative(projectRoot, path.join(op.executionRoot, 'COMMIT-SUMMARY.md')).replace(/\\/g, '/'),
|
|
1043
|
+
promotion_summary_ref: path.relative(projectRoot, path.join(op.executionRoot, 'PROMOTION-SUMMARY.md')).replace(/\\/g, '/'),
|
|
1044
|
+
pr_summary_ref: path.relative(projectRoot, path.join(op.executionRoot, 'PR-SUMMARY.md')).replace(/\\/g, '/'),
|
|
1045
|
+
generated_at: new Date().toISOString(),
|
|
1046
|
+
};
|
|
1047
|
+
if (options.write !== false) {
|
|
1048
|
+
ensureDirForFile(paths.plan);
|
|
1049
|
+
ensureDirForFile(paths.verify);
|
|
1050
|
+
ensureDirForFile(paths.state);
|
|
1051
|
+
fs.writeFileSync(paths.plan, projections.plan + '\n', 'utf8');
|
|
1052
|
+
fs.writeFileSync(paths.verify, projections.verify + '\n', 'utf8');
|
|
1053
|
+
fs.writeFileSync(paths.state, projections.state + '\n', 'utf8');
|
|
1054
|
+
fs.writeFileSync(path.join(op.executionRoot, 'RUN-SUMMARY.md'), projections.runSummary + '\n', 'utf8');
|
|
1055
|
+
fs.writeFileSync(path.join(op.executionRoot, 'COMMIT-SUMMARY.md'), projections.commitSummary + '\n', 'utf8');
|
|
1056
|
+
fs.writeFileSync(path.join(op.executionRoot, 'PROMOTION-SUMMARY.md'), projections.promotionSummary + '\n', 'utf8');
|
|
1057
|
+
fs.writeFileSync(path.join(op.executionRoot, 'PR-SUMMARY.md'), projections.prSummary + '\n', 'utf8');
|
|
1058
|
+
}
|
|
1059
|
+
const next = writeRunState(projectRoot, activeSession, {
|
|
1060
|
+
...current,
|
|
1061
|
+
canonical_state: canonicalState,
|
|
1062
|
+
verification_manifest: verificationArtifacts.manifest,
|
|
1063
|
+
residual_risks: verificationArtifacts.residualRisks,
|
|
1064
|
+
verification_evidence_coverage: verificationArtifacts.evidenceCoverage,
|
|
1065
|
+
projections: projectionRefs,
|
|
1066
|
+
});
|
|
1067
|
+
return {
|
|
1068
|
+
run: next,
|
|
1069
|
+
projections,
|
|
1070
|
+
paths: {
|
|
1071
|
+
...paths,
|
|
1072
|
+
runSummary: path.join(op.executionRoot, 'RUN-SUMMARY.md'),
|
|
1073
|
+
commitSummary: path.join(op.executionRoot, 'COMMIT-SUMMARY.md'),
|
|
1074
|
+
promotionSummary: path.join(op.executionRoot, 'PROMOTION-SUMMARY.md'),
|
|
1075
|
+
prSummary: path.join(op.executionRoot, 'PR-SUMMARY.md'),
|
|
1076
|
+
},
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
async function runRuntimeCiChecks(projectRoot, activeSession, options = {}) {
|
|
1081
|
+
const runtime = loadRuntimeModule();
|
|
1082
|
+
if (!runtime || typeof runtime.runCIChecks !== 'function' || typeof runtime.summarizeCIResults !== 'function') {
|
|
1083
|
+
throw new Error('Runtime package não está disponível. Rode npm run build:runtime.');
|
|
1084
|
+
}
|
|
1085
|
+
let current = options.runState || readRunState(projectRoot, activeSession);
|
|
1086
|
+
if (!current) {
|
|
1087
|
+
current = compileExecutionGraphFromArtifacts(projectRoot, activeSession).run;
|
|
1088
|
+
}
|
|
1089
|
+
const runId = options.runId || (current && current.run_id) || null;
|
|
1090
|
+
const results = await runtime.runCIChecks({
|
|
1091
|
+
projectRoot,
|
|
1092
|
+
sessionId: activeSession || null,
|
|
1093
|
+
runId,
|
|
1094
|
+
});
|
|
1095
|
+
const summary = runtime.summarizeCIResults(results);
|
|
1096
|
+
const ciResultsPath = path.join(projectRoot, '.oxe', 'runs', runId || makeRunId(), 'ci-results.json');
|
|
1097
|
+
ensureDirForFile(ciResultsPath);
|
|
1098
|
+
fs.writeFileSync(ciResultsPath, JSON.stringify({
|
|
1099
|
+
run_id: runId,
|
|
1100
|
+
generated_at: new Date().toISOString(),
|
|
1101
|
+
summary,
|
|
1102
|
+
results,
|
|
1103
|
+
}, null, 2), 'utf8');
|
|
1104
|
+
const next = writeRunState(projectRoot, activeSession, {
|
|
1105
|
+
...(current || {}),
|
|
1106
|
+
run_id: runId || makeRunId(),
|
|
1107
|
+
status: current && current.status ? current.status : 'planned',
|
|
1108
|
+
ci_checks: {
|
|
1109
|
+
generated_at: new Date().toISOString(),
|
|
1110
|
+
summary,
|
|
1111
|
+
results,
|
|
1112
|
+
path: path.relative(projectRoot, ciResultsPath).replace(/\\/g, '/'),
|
|
1113
|
+
},
|
|
1114
|
+
});
|
|
1115
|
+
appendEvent(projectRoot, activeSession, {
|
|
1116
|
+
type: 'runtime_ci_completed',
|
|
1117
|
+
run_id: next.run_id,
|
|
1118
|
+
payload: {
|
|
1119
|
+
total: summary.total,
|
|
1120
|
+
pass: summary.pass,
|
|
1121
|
+
fail: summary.fail,
|
|
1122
|
+
skip: summary.skip,
|
|
1123
|
+
error: summary.error,
|
|
1124
|
+
allPassed: summary.allPassed,
|
|
1125
|
+
},
|
|
1126
|
+
});
|
|
1127
|
+
return { run: next, runId: next.run_id, results, summary, path: ciResultsPath };
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
async function runRuntimePromotion(projectRoot, activeSession, options = {}) {
|
|
1131
|
+
const runtime = loadRuntimeModule();
|
|
1132
|
+
if (!runtime || typeof runtime.PromotionPipeline !== 'function' || typeof runtime.BranchManager !== 'function' || typeof runtime.PRManager !== 'function') {
|
|
1133
|
+
throw new Error('Runtime package não está disponível. Rode npm run build:runtime.');
|
|
1134
|
+
}
|
|
1135
|
+
let current = options.runState || readRunState(projectRoot, activeSession);
|
|
1136
|
+
if (!current) {
|
|
1137
|
+
throw new Error('Nenhum ACTIVE-RUN disponível para promover.');
|
|
1138
|
+
}
|
|
1139
|
+
const verificationArtifacts = loadRuntimeVerificationArtifacts(projectRoot, current);
|
|
1140
|
+
if (!verificationArtifacts.manifest) {
|
|
1141
|
+
throw new Error('Manifest de verify ausente — execute `oxe-cc runtime verify` antes de promover.');
|
|
1142
|
+
}
|
|
1143
|
+
const branchManager = new runtime.BranchManager(projectRoot);
|
|
1144
|
+
const prManager = new runtime.PRManager(projectRoot);
|
|
1145
|
+
const pipeline = new runtime.PromotionPipeline(projectRoot, branchManager, prManager);
|
|
1146
|
+
const runResult = runResultFromRunState(current);
|
|
1147
|
+
const gates = readRuntimeGates(projectRoot, activeSession, { runId: current.run_id }).all;
|
|
1148
|
+
const commitRecord = pipeline.loadCommitRecord(current.run_id)
|
|
1149
|
+
|| pipeline.recordLocalCommit(runResult, verificationArtifacts.manifest, verificationArtifacts.residualRisks, {
|
|
1150
|
+
commitSha: branchManager.currentCommit(),
|
|
1151
|
+
summaryPath: current.projections && current.projections.commit_summary_ref ? current.projections.commit_summary_ref : null,
|
|
1152
|
+
});
|
|
1153
|
+
const promotion = await pipeline.promote(
|
|
1154
|
+
runResult,
|
|
1155
|
+
verificationArtifacts.manifest,
|
|
1156
|
+
verificationArtifacts.residualRisks,
|
|
1157
|
+
{
|
|
1158
|
+
targetKind: options.targetKind || 'pr_draft',
|
|
1159
|
+
remote: options.remote || 'origin',
|
|
1160
|
+
baseBranch: options.baseBranch || 'main',
|
|
1161
|
+
targetRef: options.targetRef || options.baseBranch || 'main',
|
|
1162
|
+
minimumCoverage: options.minimumCoverage == null ? 100 : Number(options.minimumCoverage),
|
|
1163
|
+
draftPR: options.draftPR !== false,
|
|
1164
|
+
},
|
|
1165
|
+
gates,
|
|
1166
|
+
verificationArtifacts.evidenceCoverage
|
|
1167
|
+
);
|
|
1168
|
+
const health = loadProjectHealth();
|
|
1169
|
+
const healthReport = health && typeof health.buildHealthReport === 'function'
|
|
1170
|
+
? health.buildHealthReport(projectRoot)
|
|
1171
|
+
: null;
|
|
1172
|
+
const promotionReadiness = healthReport && healthReport.promotionReadiness ? healthReport.promotionReadiness : null;
|
|
1173
|
+
const readinessPath = path.join(projectRoot, '.oxe', 'runs', current.run_id, 'promotion-readiness.json');
|
|
1174
|
+
ensureDirForFile(readinessPath);
|
|
1175
|
+
fs.writeFileSync(readinessPath, JSON.stringify({
|
|
1176
|
+
run_id: current.run_id,
|
|
1177
|
+
generated_at: new Date().toISOString(),
|
|
1178
|
+
readiness: promotionReadiness,
|
|
1179
|
+
promotion,
|
|
1180
|
+
}, null, 2), 'utf8');
|
|
1181
|
+
const next = writeRunState(projectRoot, activeSession, {
|
|
1182
|
+
...current,
|
|
1183
|
+
delivery: {
|
|
1184
|
+
commit_record: commitRecord,
|
|
1185
|
+
promotion_record: promotion,
|
|
1186
|
+
promotion_readiness_ref: path.relative(projectRoot, readinessPath).replace(/\\/g, '/'),
|
|
1187
|
+
},
|
|
1188
|
+
});
|
|
1189
|
+
appendEvent(projectRoot, activeSession, {
|
|
1190
|
+
type: promotion.status === 'blocked' ? 'GateRequested' : 'ToolCompleted',
|
|
1191
|
+
run_id: next.run_id,
|
|
1192
|
+
payload: {
|
|
1193
|
+
promotion_target: promotion.target_kind,
|
|
1194
|
+
promotion_status: promotion.status,
|
|
1195
|
+
remote: promotion.remote,
|
|
1196
|
+
target_ref: promotion.target_ref,
|
|
1197
|
+
pr_url: promotion.pr_url,
|
|
1198
|
+
},
|
|
1199
|
+
});
|
|
1200
|
+
const projected = projectRuntimeArtifacts(projectRoot, activeSession, { runState: next, write: true });
|
|
1201
|
+
return {
|
|
1202
|
+
run: projected.run,
|
|
1203
|
+
commitRecord,
|
|
1204
|
+
promotion,
|
|
1205
|
+
promotionReadiness,
|
|
1206
|
+
projected,
|
|
1207
|
+
};
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
function recoverRuntimeState(projectRoot, activeSession, options = {}) {
|
|
1211
|
+
const runtime = loadRuntimeModule();
|
|
1212
|
+
if (!runtime || typeof runtime.loadJournal !== 'function') {
|
|
1213
|
+
throw new Error('Runtime package não está disponível. Rode npm run build:runtime.');
|
|
1214
|
+
}
|
|
1215
|
+
const current = options.runState || readRunState(projectRoot, activeSession);
|
|
1216
|
+
if (!current || !current.run_id) {
|
|
1217
|
+
throw new Error('Nenhum ACTIVE-RUN disponível para recover.');
|
|
1218
|
+
}
|
|
1219
|
+
const journal = runtime.loadJournal(projectRoot, current.run_id);
|
|
1220
|
+
if (!journal) {
|
|
1221
|
+
throw new Error(`Journal ausente para run ${current.run_id}.`);
|
|
1222
|
+
}
|
|
1223
|
+
const canonicalLive = reduceCanonicalRunStateLive(projectRoot, activeSession, {
|
|
1224
|
+
runId: current.run_id,
|
|
1225
|
+
runState: current,
|
|
1226
|
+
}) || mergeCanonicalStateWithRunState(current.canonical_state, current, activeSession, current.compiled_graph);
|
|
1227
|
+
const graphNodes = current.compiled_graph && current.compiled_graph.nodes && typeof current.compiled_graph.nodes === 'object'
|
|
1228
|
+
? new Set(Object.keys(current.compiled_graph.nodes))
|
|
1229
|
+
: new Set();
|
|
1230
|
+
const orphanWorkItems = Array.from(new Set([
|
|
1231
|
+
...((journal.pending_gates || []).filter((id) => id && !graphNodes.has(String(id)))),
|
|
1232
|
+
...((current.active_tasks || []).filter((id) => id && !graphNodes.has(String(id)))),
|
|
1233
|
+
]));
|
|
1234
|
+
const nextStatus = journal.scheduler_state === 'paused'
|
|
1235
|
+
? 'paused'
|
|
1236
|
+
: journal.scheduler_state === 'blocked'
|
|
1237
|
+
? 'blocked'
|
|
1238
|
+
: current.status || 'planned';
|
|
1239
|
+
const verificationArtifacts = loadRuntimeVerificationArtifacts(projectRoot, current);
|
|
1240
|
+
const consistency = buildRecoveryConsistency(
|
|
1241
|
+
projectRoot,
|
|
1242
|
+
activeSession,
|
|
1243
|
+
current,
|
|
1244
|
+
journal,
|
|
1245
|
+
verificationArtifacts
|
|
1246
|
+
);
|
|
1247
|
+
const recoverySummary = {
|
|
1248
|
+
recovered_at: new Date().toISOString(),
|
|
1249
|
+
journal_state: journal.scheduler_state,
|
|
1250
|
+
orphan_work_items: orphanWorkItems,
|
|
1251
|
+
pending_gates: readRuntimeGates(projectRoot, activeSession, { runId: current.run_id }).pending.map((gate) => gate.gate_id),
|
|
1252
|
+
consistency,
|
|
1253
|
+
};
|
|
1254
|
+
const runDir = path.join(projectRoot, '.oxe', 'runs', current.run_id);
|
|
1255
|
+
ensureDir(runDir);
|
|
1256
|
+
const recoverySummaryPath = path.join(runDir, 'recovery-summary.json');
|
|
1257
|
+
fs.writeFileSync(recoverySummaryPath, JSON.stringify(recoverySummary, null, 2), 'utf8');
|
|
1258
|
+
const summaryPath = writeRecoverySummaryMarkdown(projectRoot, activeSession, current, recoverySummary);
|
|
1259
|
+
recoverySummary.json_ref = path.relative(projectRoot, recoverySummaryPath).replace(/\\/g, '/');
|
|
1260
|
+
recoverySummary.markdown_ref = path.relative(projectRoot, summaryPath).replace(/\\/g, '/');
|
|
1261
|
+
const next = writeRunState(projectRoot, activeSession, {
|
|
1262
|
+
...current,
|
|
1263
|
+
status: nextStatus,
|
|
1264
|
+
canonical_state: serializeCanonicalState(canonicalLive),
|
|
1265
|
+
recovery_summary: recoverySummary,
|
|
1266
|
+
metrics: {
|
|
1267
|
+
...(current.metrics || {}),
|
|
1268
|
+
recover_count: Number((current.metrics || {}).recover_count || 0) + 1,
|
|
1269
|
+
last_action: 'recover',
|
|
1270
|
+
},
|
|
1271
|
+
});
|
|
1272
|
+
appendEvent(projectRoot, activeSession, {
|
|
1273
|
+
type: 'RunStarted',
|
|
1274
|
+
run_id: next.run_id,
|
|
1275
|
+
payload: {
|
|
1276
|
+
recovered: true,
|
|
1277
|
+
orphan_work_items: orphanWorkItems,
|
|
1278
|
+
journal_state: journal.scheduler_state,
|
|
1279
|
+
},
|
|
1280
|
+
});
|
|
1281
|
+
return {
|
|
1282
|
+
run: next,
|
|
1283
|
+
journal,
|
|
1284
|
+
recoverySummary,
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
function operationalPaths(projectRoot, activeSession) {
|
|
1289
|
+
const oxeDir = path.join(projectRoot, '.oxe');
|
|
1290
|
+
const scopeRoot = activeSession ? path.join(oxeDir, ...String(activeSession).split('/')) : oxeDir;
|
|
1291
|
+
const executionRoot = activeSession ? path.join(scopeRoot, 'execution') : oxeDir;
|
|
1292
|
+
const runsDir = path.join(executionRoot, 'runs');
|
|
1293
|
+
return {
|
|
1294
|
+
oxeDir,
|
|
1295
|
+
scopeRoot,
|
|
1296
|
+
executionRoot,
|
|
1297
|
+
runsDir,
|
|
1298
|
+
events: path.join(executionRoot, 'OXE-EVENTS.ndjson'),
|
|
1299
|
+
activeRun: path.join(executionRoot, 'ACTIVE-RUN.json'),
|
|
1300
|
+
projectLessons: path.join(oxeDir, 'global', 'LESSONS.md'),
|
|
1301
|
+
sessionManifest: activeSession ? path.join(scopeRoot, 'SESSION.md') : null,
|
|
1302
|
+
verify: activeSession ? path.join(scopeRoot, 'verification', 'VERIFY.md') : path.join(oxeDir, 'VERIFY.md'),
|
|
1303
|
+
investigations: activeSession ? path.join(scopeRoot, 'research', 'INVESTIGATIONS.md') : path.join(oxeDir, 'INVESTIGATIONS.md'),
|
|
1304
|
+
capabilitiesDir: path.join(oxeDir, 'capabilities'),
|
|
1305
|
+
capabilitiesIndex: path.join(oxeDir, 'CAPABILITIES.md'),
|
|
1306
|
+
checkpoints: activeSession ? path.join(scopeRoot, 'execution', 'CHECKPOINTS.md') : path.join(oxeDir, 'CHECKPOINTS.md'),
|
|
1307
|
+
gates: gateStoragePath(projectRoot, activeSession),
|
|
1308
|
+
auditTrail: path.join(oxeDir, 'AUDIT-TRAIL.ndjson'),
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
function makeRunId() {
|
|
1313
|
+
return `oxe-run-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
function dedupeNodes(nodes) {
|
|
1317
|
+
const byId = new Map();
|
|
1318
|
+
for (const node of nodes || []) {
|
|
1319
|
+
if (!node || !node.id) continue;
|
|
1320
|
+
byId.set(node.id, { ...byId.get(node.id), ...node });
|
|
1321
|
+
}
|
|
1322
|
+
return Array.from(byId.values());
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
function dedupeEdges(edges) {
|
|
1326
|
+
const byKey = new Map();
|
|
1327
|
+
for (const edge of edges || []) {
|
|
1328
|
+
if (!edge || !edge.from || !edge.to) continue;
|
|
1329
|
+
const key = [edge.from, edge.to, edge.type || 'link', edge.label || ''].join('::');
|
|
1330
|
+
byKey.set(key, { ...byKey.get(key), ...edge });
|
|
1331
|
+
}
|
|
1332
|
+
return Array.from(byKey.values());
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
function buildOperationalGraph(runState = {}) {
|
|
1336
|
+
const currentWave = runState.current_wave == null ? null : Number(runState.current_wave);
|
|
1337
|
+
const activeTasks = Array.isArray(runState.active_tasks) ? runState.active_tasks.map(String) : [];
|
|
1338
|
+
const pendingCheckpoints = Array.isArray(runState.pending_checkpoints) ? runState.pending_checkpoints.map(String) : [];
|
|
1339
|
+
const multiAgent = runState.multi_agent && typeof runState.multi_agent === 'object'
|
|
1340
|
+
? runState.multi_agent
|
|
1341
|
+
: null;
|
|
1342
|
+
const azureContext = runState.provider_context && runState.provider_context.azure && typeof runState.provider_context.azure === 'object'
|
|
1343
|
+
? runState.provider_context.azure
|
|
1344
|
+
: null;
|
|
1345
|
+
const baseNodes = (((runState.graph || {}).nodes) || []).map((node) => ({ ...node }));
|
|
1346
|
+
const baseEdges = (((runState.graph || {}).edges) || []).map((edge) => ({ ...edge }));
|
|
1347
|
+
const generatedNodes = [
|
|
1348
|
+
{
|
|
1349
|
+
id: `run:${runState.run_id || 'active'}`,
|
|
1350
|
+
label: runState.run_id || 'active-run',
|
|
1351
|
+
kind: 'run',
|
|
1352
|
+
status: runState.status || 'planned',
|
|
1353
|
+
detail: runState.cursor && runState.cursor.mode ? `mode:${runState.cursor.mode}` : 'run ativo',
|
|
1354
|
+
},
|
|
1355
|
+
{
|
|
1356
|
+
id: 'agent:main-executor',
|
|
1357
|
+
label: 'main-executor',
|
|
1358
|
+
kind: 'agent',
|
|
1359
|
+
status: /running|replaying/.test(String(runState.status || '')) ? 'active' : 'planned',
|
|
1360
|
+
detail: activeTasks.length ? activeTasks.join(', ') : 'sem tarefas ativas',
|
|
1361
|
+
},
|
|
1362
|
+
];
|
|
1363
|
+
const generatedEdges = [
|
|
1364
|
+
{
|
|
1365
|
+
from: `run:${runState.run_id || 'active'}`,
|
|
1366
|
+
to: 'agent:main-executor',
|
|
1367
|
+
type: 'handoff',
|
|
1368
|
+
status: /running|replaying/.test(String(runState.status || '')) ? 'active' : 'planned',
|
|
1369
|
+
reason: 'orquestração principal',
|
|
1370
|
+
},
|
|
1371
|
+
];
|
|
1372
|
+
if (currentWave != null) {
|
|
1373
|
+
generatedNodes.push({
|
|
1374
|
+
id: `wave:${currentWave}`,
|
|
1375
|
+
label: `wave-${currentWave}`,
|
|
1376
|
+
kind: 'wave',
|
|
1377
|
+
status: runState.status || 'planned',
|
|
1378
|
+
detail: activeTasks.length ? `${activeTasks.length} tarefa(s)` : 'sem tarefas ativas',
|
|
1379
|
+
});
|
|
1380
|
+
generatedEdges.push({
|
|
1381
|
+
from: `run:${runState.run_id || 'active'}`,
|
|
1382
|
+
to: `wave:${currentWave}`,
|
|
1383
|
+
type: 'contains',
|
|
1384
|
+
status: 'done',
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1387
|
+
for (const taskId of activeTasks) {
|
|
1388
|
+
generatedNodes.push({
|
|
1389
|
+
id: `task:${taskId}`,
|
|
1390
|
+
label: taskId,
|
|
1391
|
+
kind: 'task',
|
|
1392
|
+
status: /paused|waiting_approval/.test(String(runState.status || '')) ? 'blocked' : 'active',
|
|
1393
|
+
detail: currentWave != null ? `wave ${currentWave}` : 'task ativa',
|
|
1394
|
+
});
|
|
1395
|
+
generatedEdges.push({
|
|
1396
|
+
from: currentWave != null ? `wave:${currentWave}` : 'agent:main-executor',
|
|
1397
|
+
to: `task:${taskId}`,
|
|
1398
|
+
type: 'executes',
|
|
1399
|
+
status: /running|replaying/.test(String(runState.status || '')) ? 'active' : 'planned',
|
|
1400
|
+
});
|
|
1401
|
+
}
|
|
1402
|
+
for (const checkpointId of pendingCheckpoints) {
|
|
1403
|
+
generatedNodes.push({
|
|
1404
|
+
id: `checkpoint:${checkpointId}`,
|
|
1405
|
+
label: checkpointId,
|
|
1406
|
+
kind: 'checkpoint',
|
|
1407
|
+
status: 'pending_approval',
|
|
1408
|
+
detail: 'aprovação pendente',
|
|
1409
|
+
});
|
|
1410
|
+
generatedEdges.push({
|
|
1411
|
+
from: activeTasks.length ? `task:${activeTasks[activeTasks.length - 1]}` : (currentWave != null ? `wave:${currentWave}` : `run:${runState.run_id || 'active'}`),
|
|
1412
|
+
to: `checkpoint:${checkpointId}`,
|
|
1413
|
+
type: 'gate',
|
|
1414
|
+
status: 'blocked',
|
|
1415
|
+
reason: 'checkpoint pendente',
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
if (multiAgent && multiAgent.enabled) {
|
|
1419
|
+
const ownership = Array.isArray(multiAgent.ownership) ? multiAgent.ownership : [];
|
|
1420
|
+
const handoffs = Array.isArray(multiAgent.handoffs) ? multiAgent.handoffs : [];
|
|
1421
|
+
const agents = Array.isArray(multiAgent.agents) ? multiAgent.agents : [];
|
|
1422
|
+
for (const agent of agents) {
|
|
1423
|
+
const agentId = String(agent.agent_id || agent.id || 'agent');
|
|
1424
|
+
generatedNodes.push({
|
|
1425
|
+
id: `agent:${agentId}`,
|
|
1426
|
+
label: agentId,
|
|
1427
|
+
kind: 'agent',
|
|
1428
|
+
status: String(agent.status || agent.outcome || 'active'),
|
|
1429
|
+
detail: agent.role || agent.profile || multiAgent.mode || 'multi-agent',
|
|
1430
|
+
});
|
|
1431
|
+
generatedEdges.push({
|
|
1432
|
+
from: `run:${runState.run_id || 'active'}`,
|
|
1433
|
+
to: `agent:${agentId}`,
|
|
1434
|
+
type: 'handoff',
|
|
1435
|
+
status: String(agent.status || 'active'),
|
|
1436
|
+
reason: 'coordenação multi-agent',
|
|
1437
|
+
});
|
|
1438
|
+
}
|
|
1439
|
+
for (const item of ownership) {
|
|
1440
|
+
const taskId = String(item.work_item_id || item.task_id || '');
|
|
1441
|
+
const agentId = String(item.agent_id || '');
|
|
1442
|
+
if (!taskId || !agentId) continue;
|
|
1443
|
+
generatedNodes.push({
|
|
1444
|
+
id: `task:${taskId}`,
|
|
1445
|
+
label: taskId,
|
|
1446
|
+
kind: 'task',
|
|
1447
|
+
status: String(item.status || 'assigned'),
|
|
1448
|
+
detail: `owner ${agentId}`,
|
|
1449
|
+
});
|
|
1450
|
+
generatedEdges.push({
|
|
1451
|
+
from: `agent:${agentId}`,
|
|
1452
|
+
to: `task:${taskId}`,
|
|
1453
|
+
type: 'owns',
|
|
1454
|
+
status: String(item.status || 'assigned'),
|
|
1455
|
+
reason: 'ownership canónica',
|
|
1456
|
+
});
|
|
1457
|
+
}
|
|
1458
|
+
for (const handoff of handoffs) {
|
|
1459
|
+
const fromAgent = String(handoff.from_agent_id || handoff.from || '');
|
|
1460
|
+
const toAgent = String(handoff.to_agent_id || handoff.to || '');
|
|
1461
|
+
if (!fromAgent || !toAgent) continue;
|
|
1462
|
+
generatedEdges.push({
|
|
1463
|
+
from: `agent:${fromAgent}`,
|
|
1464
|
+
to: `agent:${toAgent}`,
|
|
1465
|
+
type: 'handoff',
|
|
1466
|
+
status: String(handoff.status || 'completed'),
|
|
1467
|
+
reason: handoff.reason || handoff.work_item_id || 'handoff cooperativo',
|
|
1468
|
+
});
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
if (azureContext) {
|
|
1472
|
+
const azureStatus = azureContext.login_active
|
|
1473
|
+
? (azureContext.pending_approval_count > 0 ? 'blocked' : 'active')
|
|
1474
|
+
: 'warning';
|
|
1475
|
+
generatedNodes.push({
|
|
1476
|
+
id: 'provider:azure',
|
|
1477
|
+
label: 'azure',
|
|
1478
|
+
kind: 'provider',
|
|
1479
|
+
status: azureStatus,
|
|
1480
|
+
detail: azureContext.subscription_name || azureContext.subscription_id || azureContext.cloud || 'contexto azure',
|
|
1481
|
+
});
|
|
1482
|
+
generatedEdges.push({
|
|
1483
|
+
from: `run:${runState.run_id || 'active'}`,
|
|
1484
|
+
to: 'provider:azure',
|
|
1485
|
+
type: 'provider',
|
|
1486
|
+
status: azureStatus,
|
|
1487
|
+
reason: 'contexto cloud ativo',
|
|
1488
|
+
});
|
|
1489
|
+
const lastOperation = azureContext.last_operation && typeof azureContext.last_operation === 'object'
|
|
1490
|
+
? azureContext.last_operation
|
|
1491
|
+
: null;
|
|
1492
|
+
if (lastOperation) {
|
|
1493
|
+
const capabilityId = String(lastOperation.capability_id || lastOperation.domain || 'azure-operation');
|
|
1494
|
+
generatedNodes.push({
|
|
1495
|
+
id: `capability:${capabilityId}`,
|
|
1496
|
+
label: capabilityId,
|
|
1497
|
+
kind: 'capability',
|
|
1498
|
+
status: String(lastOperation.phase || lastOperation.status || 'planned'),
|
|
1499
|
+
detail: lastOperation.command_display || lastOperation.operation || 'operação Azure',
|
|
1500
|
+
});
|
|
1501
|
+
generatedEdges.push({
|
|
1502
|
+
from: 'provider:azure',
|
|
1503
|
+
to: `capability:${capabilityId}`,
|
|
1504
|
+
type: 'tool_call',
|
|
1505
|
+
status: String(lastOperation.phase || lastOperation.status || 'planned'),
|
|
1506
|
+
reason: lastOperation.operation || lastOperation.domain || 'ação Azure',
|
|
1507
|
+
});
|
|
1508
|
+
const refs = Array.isArray(lastOperation.resource_refs) ? lastOperation.resource_refs : [];
|
|
1509
|
+
for (let index = 0; index < refs.length; index += 1) {
|
|
1510
|
+
const ref = refs[index];
|
|
1511
|
+
const normalized = ref && typeof ref === 'object' ? ref : { name: String(ref || 'resource') };
|
|
1512
|
+
const resourceId = normalized.id || normalized.name || normalized.resource_id || normalized.type || `resource-${index + 1}`;
|
|
1513
|
+
generatedNodes.push({
|
|
1514
|
+
id: `resource:${resourceId}`,
|
|
1515
|
+
label: normalized.name || normalized.type || resourceId,
|
|
1516
|
+
kind: 'resource',
|
|
1517
|
+
status: String(lastOperation.phase || lastOperation.status || 'planned'),
|
|
1518
|
+
detail: [normalized.type, normalized.resource_group, normalized.location].filter(Boolean).join(' · ') || azureContext.subscription_name || 'recurso Azure',
|
|
1519
|
+
});
|
|
1520
|
+
generatedEdges.push({
|
|
1521
|
+
from: `capability:${capabilityId}`,
|
|
1522
|
+
to: `resource:${resourceId}`,
|
|
1523
|
+
type: 'targets',
|
|
1524
|
+
status: String(lastOperation.phase || lastOperation.status || 'planned'),
|
|
1525
|
+
reason: normalized.scope || normalized.kind || 'recurso alvo',
|
|
1526
|
+
});
|
|
1527
|
+
}
|
|
1528
|
+
if (lastOperation.pending_checkpoint_id) {
|
|
1529
|
+
generatedNodes.push({
|
|
1530
|
+
id: `checkpoint:${lastOperation.pending_checkpoint_id}`,
|
|
1531
|
+
label: String(lastOperation.pending_checkpoint_id),
|
|
1532
|
+
kind: 'checkpoint',
|
|
1533
|
+
status: 'pending_approval',
|
|
1534
|
+
detail: 'gate Azure pendente',
|
|
1535
|
+
});
|
|
1536
|
+
generatedEdges.push({
|
|
1537
|
+
from: `capability:${capabilityId}`,
|
|
1538
|
+
to: `checkpoint:${lastOperation.pending_checkpoint_id}`,
|
|
1539
|
+
type: 'gate',
|
|
1540
|
+
status: 'blocked',
|
|
1541
|
+
reason: 'approval policy do provider Azure',
|
|
1542
|
+
});
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
return {
|
|
1547
|
+
nodes: dedupeNodes([...baseNodes, ...generatedNodes]),
|
|
1548
|
+
edges: dedupeEdges([...baseEdges, ...generatedEdges]),
|
|
1549
|
+
};
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
function appendEvent(projectRoot, activeSession, event = {}) {
|
|
1553
|
+
const p = operationalPaths(projectRoot, activeSession);
|
|
1554
|
+
ensureDirForFile(p.events);
|
|
1555
|
+
const entry = {
|
|
1556
|
+
event_id: event.event_id || `evt-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
|
|
1557
|
+
type: String(event.type || 'custom'),
|
|
1558
|
+
timestamp: event.timestamp || new Date().toISOString(),
|
|
1559
|
+
run_id: event.run_id || null,
|
|
1560
|
+
session_id: activeSession || event.session_id || null,
|
|
1561
|
+
wave_id: event.wave_id || null,
|
|
1562
|
+
task_id: event.task_id || event.work_item_id || null,
|
|
1563
|
+
work_item_id: event.work_item_id || event.task_id || null,
|
|
1564
|
+
attempt_id: event.attempt_id || null,
|
|
1565
|
+
agent_id: event.agent_id || null,
|
|
1566
|
+
causation_id: event.causation_id || null,
|
|
1567
|
+
correlation_id: event.correlation_id || null,
|
|
1568
|
+
payload: event.payload && typeof event.payload === 'object' ? event.payload : {},
|
|
1569
|
+
};
|
|
1570
|
+
fs.appendFileSync(p.events, `${JSON.stringify(entry)}\n`, 'utf8');
|
|
1571
|
+
return entry;
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
function readEvents(projectRoot, activeSession) {
|
|
1575
|
+
const p = operationalPaths(projectRoot, activeSession);
|
|
1576
|
+
const raw = readTextIfExists(p.events) || '';
|
|
1577
|
+
return raw
|
|
1578
|
+
.split(/\r?\n/)
|
|
1579
|
+
.map((line) => line.trim())
|
|
1580
|
+
.filter(Boolean)
|
|
1581
|
+
.map((line) => {
|
|
1582
|
+
try {
|
|
1583
|
+
return JSON.parse(line);
|
|
1584
|
+
} catch {
|
|
1585
|
+
return null;
|
|
1586
|
+
}
|
|
1587
|
+
})
|
|
1588
|
+
.filter(Boolean);
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
function summarizeEvents(events) {
|
|
1592
|
+
const byType = {};
|
|
1593
|
+
for (const event of events) {
|
|
1594
|
+
const key = String(event.type || 'custom');
|
|
1595
|
+
byType[key] = (byType[key] || 0) + 1;
|
|
1596
|
+
}
|
|
1597
|
+
return {
|
|
1598
|
+
total: events.length,
|
|
1599
|
+
byType,
|
|
1600
|
+
lastEvent: events.length ? events[events.length - 1] : null,
|
|
1601
|
+
};
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
function writeRunState(projectRoot, activeSession, runState = {}) {
|
|
1605
|
+
const p = operationalPaths(projectRoot, activeSession);
|
|
1606
|
+
const runId = String(runState.run_id || makeRunId());
|
|
1607
|
+
const canonicalState = runState.canonical_state ? serializeCanonicalState(hydrateCanonicalState(runState.canonical_state)) : null;
|
|
1608
|
+
const payload = {
|
|
1609
|
+
run_id: runId,
|
|
1610
|
+
status: VALID_RUN_STATUSES.has(String(runState.status || 'planned')) ? String(runState.status || 'planned') : 'planned',
|
|
1611
|
+
created_at: runState.created_at || new Date().toISOString(),
|
|
1612
|
+
updated_at: runState.updated_at || new Date().toISOString(),
|
|
1613
|
+
plan_ref: runState.plan_ref || 'PLAN.md',
|
|
1614
|
+
session_id: activeSession || null,
|
|
1615
|
+
current_wave: runState.current_wave == null ? null : Number(runState.current_wave),
|
|
1616
|
+
cursor: {
|
|
1617
|
+
wave: runState.cursor && runState.cursor.wave != null ? Number(runState.cursor.wave) : null,
|
|
1618
|
+
task: runState.cursor && runState.cursor.task ? String(runState.cursor.task) : null,
|
|
1619
|
+
mode: runState.cursor && runState.cursor.mode ? String(runState.cursor.mode) : null,
|
|
1620
|
+
},
|
|
1621
|
+
active_tasks: Array.isArray(runState.active_tasks) ? runState.active_tasks.map(String) : [],
|
|
1622
|
+
pending_checkpoints: Array.isArray(runState.pending_checkpoints) ? runState.pending_checkpoints.map(String) : [],
|
|
1623
|
+
retries: Array.isArray(runState.retries) ? runState.retries : [],
|
|
1624
|
+
failures: Array.isArray(runState.failures) ? runState.failures : [],
|
|
1625
|
+
evidence: Array.isArray(runState.evidence) ? runState.evidence : [],
|
|
1626
|
+
provider_context: runState.provider_context && typeof runState.provider_context === 'object'
|
|
1627
|
+
? runState.provider_context
|
|
1628
|
+
: {},
|
|
1629
|
+
graph: runState.graph && typeof runState.graph === 'object'
|
|
1630
|
+
? {
|
|
1631
|
+
nodes: Array.isArray(runState.graph.nodes) ? runState.graph.nodes : [],
|
|
1632
|
+
edges: Array.isArray(runState.graph.edges) ? runState.graph.edges : [],
|
|
1633
|
+
}
|
|
1634
|
+
: { nodes: [], edges: [] },
|
|
1635
|
+
compiled_graph: runState.compiled_graph && typeof runState.compiled_graph === 'object'
|
|
1636
|
+
? runState.compiled_graph
|
|
1637
|
+
: null,
|
|
1638
|
+
graph_version: runState.graph_version || null,
|
|
1639
|
+
canonical_state: canonicalState,
|
|
1640
|
+
verification_suite: runState.verification_suite && typeof runState.verification_suite === 'object'
|
|
1641
|
+
? runState.verification_suite
|
|
1642
|
+
: null,
|
|
1643
|
+
verification_results: Array.isArray(runState.verification_results) ? runState.verification_results : [],
|
|
1644
|
+
verification_check_results: Array.isArray(runState.verification_check_results) ? runState.verification_check_results : [],
|
|
1645
|
+
verification_manifest: runState.verification_manifest && typeof runState.verification_manifest === 'object'
|
|
1646
|
+
? runState.verification_manifest
|
|
1647
|
+
: null,
|
|
1648
|
+
residual_risks: runState.residual_risks && typeof runState.residual_risks === 'object'
|
|
1649
|
+
? runState.residual_risks
|
|
1650
|
+
: null,
|
|
1651
|
+
verification_evidence_coverage: runState.verification_evidence_coverage && typeof runState.verification_evidence_coverage === 'object'
|
|
1652
|
+
? runState.verification_evidence_coverage
|
|
1653
|
+
: null,
|
|
1654
|
+
verification_gaps: Array.isArray(runState.verification_gaps) ? runState.verification_gaps.map(String) : [],
|
|
1655
|
+
ci_checks: runState.ci_checks && typeof runState.ci_checks === 'object' ? runState.ci_checks : null,
|
|
1656
|
+
delivery: runState.delivery && typeof runState.delivery === 'object' ? runState.delivery : null,
|
|
1657
|
+
recovery_summary: runState.recovery_summary && typeof runState.recovery_summary === 'object' ? runState.recovery_summary : null,
|
|
1658
|
+
multi_agent: runState.multi_agent && typeof runState.multi_agent === 'object' ? runState.multi_agent : null,
|
|
1659
|
+
projections: runState.projections && typeof runState.projections === 'object' ? runState.projections : {},
|
|
1660
|
+
metrics: runState.metrics && typeof runState.metrics === 'object' ? runState.metrics : {},
|
|
1661
|
+
};
|
|
1662
|
+
payload.graph = buildOperationalGraph(payload);
|
|
1663
|
+
ensureDir(p.runsDir);
|
|
1664
|
+
fs.writeFileSync(path.join(p.runsDir, `${runId}.json`), JSON.stringify(payload, null, 2), 'utf8');
|
|
1665
|
+
fs.writeFileSync(
|
|
1666
|
+
p.activeRun,
|
|
1667
|
+
JSON.stringify(
|
|
1668
|
+
{
|
|
1669
|
+
run_id: runId,
|
|
1670
|
+
status: payload.status,
|
|
1671
|
+
updated_at: payload.updated_at,
|
|
1672
|
+
current_wave: payload.current_wave,
|
|
1673
|
+
cursor: payload.cursor,
|
|
1674
|
+
provider_context: payload.provider_context,
|
|
1675
|
+
canonical_state: payload.canonical_state,
|
|
1676
|
+
compiled_graph: payload.compiled_graph,
|
|
1677
|
+
graph_version: payload.graph_version,
|
|
1678
|
+
multi_agent: payload.multi_agent,
|
|
1679
|
+
},
|
|
1680
|
+
null,
|
|
1681
|
+
2
|
|
1682
|
+
),
|
|
1683
|
+
'utf8'
|
|
1684
|
+
);
|
|
1685
|
+
return payload;
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
function readRunState(projectRoot, activeSession) {
|
|
1689
|
+
const p = operationalPaths(projectRoot, activeSession);
|
|
1690
|
+
const activeRaw = readTextIfExists(p.activeRun);
|
|
1691
|
+
let runId = null;
|
|
1692
|
+
if (activeRaw) {
|
|
1693
|
+
try {
|
|
1694
|
+
runId = JSON.parse(activeRaw).run_id || null;
|
|
1695
|
+
} catch {
|
|
1696
|
+
runId = null;
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
if (!runId && fs.existsSync(p.runsDir)) {
|
|
1700
|
+
const files = fs.readdirSync(p.runsDir).filter((name) => name.endsWith('.json')).sort();
|
|
1701
|
+
if (files.length) runId = files[files.length - 1].replace(/\.json$/i, '');
|
|
1702
|
+
}
|
|
1703
|
+
if (!runId) return null;
|
|
1704
|
+
const filePath = path.join(p.runsDir, `${runId}.json`);
|
|
1705
|
+
const raw = readTextIfExists(filePath);
|
|
1706
|
+
if (!raw) return null;
|
|
1707
|
+
try {
|
|
1708
|
+
return JSON.parse(raw);
|
|
1709
|
+
} catch {
|
|
1710
|
+
return null;
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
function readGitActivity(projectRoot, since) {
|
|
1715
|
+
if (!projectRoot || !since) return [];
|
|
1716
|
+
try {
|
|
1717
|
+
const result = spawnSync(
|
|
1718
|
+
'git',
|
|
1719
|
+
['log', `--since=${since}`, '--pretty=format:%H %aI'],
|
|
1720
|
+
{ cwd: projectRoot, encoding: 'utf8', timeout: 5000 }
|
|
1721
|
+
);
|
|
1722
|
+
if (result.status !== 0 || result.error || !result.stdout) return [];
|
|
1723
|
+
return result.stdout
|
|
1724
|
+
.split(/\r?\n/)
|
|
1725
|
+
.map((line) => line.trim())
|
|
1726
|
+
.filter(Boolean)
|
|
1727
|
+
.map((line) => {
|
|
1728
|
+
const idx = line.indexOf(' ');
|
|
1729
|
+
return {
|
|
1730
|
+
hash: idx !== -1 ? line.slice(0, idx) : line,
|
|
1731
|
+
timestamp: idx !== -1 ? line.slice(idx + 1) : '',
|
|
1732
|
+
};
|
|
1733
|
+
});
|
|
1734
|
+
} catch {
|
|
1735
|
+
return [];
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1739
|
+
function verifyGitEvidence(runState, projectRoot) {
|
|
1740
|
+
const warns = [];
|
|
1741
|
+
if (!runState || !projectRoot || !runState.created_at) return warns;
|
|
1742
|
+
const commits = readGitActivity(projectRoot, runState.created_at);
|
|
1743
|
+
if (
|
|
1744
|
+
commits.length === 0 &&
|
|
1745
|
+
(String(runState.status || '') === 'completed' ||
|
|
1746
|
+
(Array.isArray(runState.evidence) && runState.evidence.length > 0))
|
|
1747
|
+
) {
|
|
1748
|
+
warns.push('Nenhum commit git encontrado desde o início do run — confirme se o trabalho foi commitado');
|
|
1749
|
+
}
|
|
1750
|
+
return warns;
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
function runtimeStateWarnings(runState, checkpoints = [], projectRoot = null) {
|
|
1754
|
+
const warns = [];
|
|
1755
|
+
if (!runState) return warns;
|
|
1756
|
+
if (!VALID_RUN_STATUSES.has(String(runState.status || ''))) {
|
|
1757
|
+
warns.push(`ACTIVE-RUN inválido: status "${runState.status}" fora do contrato`);
|
|
1758
|
+
}
|
|
1759
|
+
const cursorWave = runState.cursor && runState.cursor.wave != null ? Number(runState.cursor.wave) : null;
|
|
1760
|
+
if (cursorWave != null && runState.current_wave != null && Number(cursorWave) !== Number(runState.current_wave)) {
|
|
1761
|
+
warns.push('ACTIVE-RUN com cursor de onda divergente do current_wave');
|
|
1762
|
+
}
|
|
1763
|
+
const pending = new Set((runState.pending_checkpoints || []).map(String));
|
|
1764
|
+
for (const cp of checkpoints) {
|
|
1765
|
+
if (/pending_approval/i.test(String(cp.status || '')) && !pending.has(String(cp.id))) {
|
|
1766
|
+
warns.push(`Checkpoint ${cp.id} pendente no índice, mas ausente do ACTIVE-RUN`);
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
for (const edge of (((runState.graph || {}).edges) || [])) {
|
|
1770
|
+
if (edge.type === 'handoff' && (!edge.from || !edge.to)) {
|
|
1771
|
+
warns.push('Grafo operacional contém handoff sem origem ou destino');
|
|
1772
|
+
break;
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
if (projectRoot) {
|
|
1776
|
+
for (const w of verifyGitEvidence(runState, projectRoot)) {
|
|
1777
|
+
warns.push(w);
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
return warns;
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
function parseCapabilityManifest(text) {
|
|
1784
|
+
const fm = parseFrontmatter(text);
|
|
1785
|
+
const approval = String(fm.approval_policy || fm.policy || '').trim() || null;
|
|
1786
|
+
const sideEffects = parseArrayField(fm.side_effects || '');
|
|
1787
|
+
const envs = parseArrayField(fm.requires_env || '');
|
|
1788
|
+
const evidence = parseArrayField(fm.evidence_outputs || '');
|
|
1789
|
+
const sessionCompat = parseArrayField(fm.session_compatibility || fm.session_scope || '');
|
|
1790
|
+
const objectiveMatch = String(text || '').match(/##\s*Objetivo\s*\n+([\s\S]*?)(?=\n##\s|\n#[^\#]|$)/i);
|
|
1791
|
+
const desc = objectiveMatch ? objectiveMatch[1].split(/\r?\n/).map((line) => line.replace(/^-\s+/, '').trim()).filter(Boolean)[0] || '' : '';
|
|
1792
|
+
return {
|
|
1793
|
+
id: String(fm.id || '').trim(),
|
|
1794
|
+
version: String(fm.version || '').trim() || '1',
|
|
1795
|
+
type: String(fm.type || 'local').trim(),
|
|
1796
|
+
status: String(fm.status || 'active').trim(),
|
|
1797
|
+
scope: String(fm.scope || 'mixed').trim(),
|
|
1798
|
+
entrypoint: String(fm.entrypoint || '').trim(),
|
|
1799
|
+
approvalPolicy: approval,
|
|
1800
|
+
sideEffects,
|
|
1801
|
+
requiresEnv: envs,
|
|
1802
|
+
evidenceOutputs: evidence,
|
|
1803
|
+
sessionCompatibility: sessionCompat,
|
|
1804
|
+
description: desc || 'Capability local do projeto',
|
|
1805
|
+
};
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
function readCapabilityCatalog(projectRoot) {
|
|
1809
|
+
const p = operationalPaths(projectRoot, null);
|
|
1810
|
+
if (!fs.existsSync(p.capabilitiesDir)) return [];
|
|
1811
|
+
return fs
|
|
1812
|
+
.readdirSync(p.capabilitiesDir, { withFileTypes: true })
|
|
1813
|
+
.filter((entry) => entry.isDirectory())
|
|
1814
|
+
.map((entry) => {
|
|
1815
|
+
const manifestPath = path.join(p.capabilitiesDir, entry.name, 'CAPABILITY.md');
|
|
1816
|
+
const raw = readTextIfExists(manifestPath);
|
|
1817
|
+
if (!raw) return null;
|
|
1818
|
+
return { ...parseCapabilityManifest(raw), manifestPath };
|
|
1819
|
+
})
|
|
1820
|
+
.filter(Boolean)
|
|
1821
|
+
.sort((a, b) => a.id.localeCompare(b.id));
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
function capabilityCatalogWarnings(projectRoot) {
|
|
1825
|
+
const warns = [];
|
|
1826
|
+
for (const cap of readCapabilityCatalog(projectRoot)) {
|
|
1827
|
+
if (!cap.id) warns.push(`Capability em ${cap.manifestPath} sem id`);
|
|
1828
|
+
if (!VALID_CAPABILITY_TYPES.has(cap.type)) warns.push(`Capability ${cap.id || cap.manifestPath}: type "${cap.type}" inválido`);
|
|
1829
|
+
if (!cap.approvalPolicy || !VALID_APPROVAL_POLICIES.has(cap.approvalPolicy)) {
|
|
1830
|
+
warns.push(`Capability ${cap.id || cap.manifestPath}: approval_policy ausente ou inválida`);
|
|
1831
|
+
}
|
|
1832
|
+
if (!cap.entrypoint) warns.push(`Capability ${cap.id || cap.manifestPath}: entrypoint ausente`);
|
|
1833
|
+
if (!cap.evidenceOutputs.length) warns.push(`Capability ${cap.id || cap.manifestPath}: evidence_outputs ausente`);
|
|
1834
|
+
}
|
|
1835
|
+
return warns;
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
function buildMemoryLayers(projectRoot, activeSession) {
|
|
1839
|
+
const p = operationalPaths(projectRoot, activeSession);
|
|
1840
|
+
return {
|
|
1841
|
+
readOrder: ['runtime_state', 'session_memory', 'project_memory', 'evidence'],
|
|
1842
|
+
runtime_state: {
|
|
1843
|
+
source: activeSession ? path.join('.oxe', activeSession, 'execution', 'STATE.md') : '.oxe/STATE.md + ACTIVE-RUN.json',
|
|
1844
|
+
exists: fs.existsSync(activeSession ? path.join(projectRoot, '.oxe', activeSession, 'execution', 'STATE.md') : path.join(projectRoot, '.oxe', 'STATE.md')),
|
|
1845
|
+
},
|
|
1846
|
+
session_memory: {
|
|
1847
|
+
source: activeSession ? path.join('.oxe', activeSession, 'SESSION.md') : null,
|
|
1848
|
+
exists: Boolean(p.sessionManifest && fs.existsSync(p.sessionManifest)),
|
|
1849
|
+
},
|
|
1850
|
+
project_memory: {
|
|
1851
|
+
source: '.oxe/global/LESSONS.md',
|
|
1852
|
+
exists: fs.existsSync(p.projectLessons),
|
|
1853
|
+
},
|
|
1854
|
+
evidence: {
|
|
1855
|
+
source: [
|
|
1856
|
+
activeSession ? path.join('.oxe', activeSession, 'research', 'INVESTIGATIONS.md') : '.oxe/INVESTIGATIONS.md',
|
|
1857
|
+
activeSession ? path.join('.oxe', activeSession, 'verification', 'VERIFY.md') : '.oxe/VERIFY.md',
|
|
1858
|
+
],
|
|
1859
|
+
exists: fs.existsSync(p.investigations) || fs.existsSync(p.verify),
|
|
1860
|
+
},
|
|
1861
|
+
};
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
function applyRuntimeAction(projectRoot, activeSession, input = {}) {
|
|
1865
|
+
const action = String(input.action || 'status').toLowerCase();
|
|
1866
|
+
const now = new Date().toISOString();
|
|
1867
|
+
const current = readRunState(projectRoot, activeSession);
|
|
1868
|
+
const wave = input.wave == null || input.wave === '' ? null : Number(input.wave);
|
|
1869
|
+
const task = input.task ? String(input.task) : null;
|
|
1870
|
+
const mode = input.mode ? String(input.mode) : (task ? 'task' : wave != null ? 'wave' : 'complete');
|
|
1871
|
+
const pendingCheckpoints = Array.isArray(input.pending_checkpoints)
|
|
1872
|
+
? input.pending_checkpoints.map(String)
|
|
1873
|
+
: current && Array.isArray(current.pending_checkpoints)
|
|
1874
|
+
? current.pending_checkpoints
|
|
1875
|
+
: [];
|
|
1876
|
+
|
|
1877
|
+
if (action === 'start') {
|
|
1878
|
+
const next = writeRunState(projectRoot, activeSession, {
|
|
1879
|
+
run_id: input.run_id || makeRunId(),
|
|
1880
|
+
status: 'running',
|
|
1881
|
+
created_at: now,
|
|
1882
|
+
updated_at: now,
|
|
1883
|
+
plan_ref: input.plan_ref || 'PLAN.md',
|
|
1884
|
+
current_wave: wave,
|
|
1885
|
+
cursor: { wave, task, mode },
|
|
1886
|
+
active_tasks: task ? [task] : Array.isArray(input.active_tasks) ? input.active_tasks.map(String) : [],
|
|
1887
|
+
pending_checkpoints: pendingCheckpoints,
|
|
1888
|
+
retries: [],
|
|
1889
|
+
failures: [],
|
|
1890
|
+
evidence: [],
|
|
1891
|
+
metrics: {
|
|
1892
|
+
transitions: 1,
|
|
1893
|
+
pause_count: 0,
|
|
1894
|
+
replay_count: 0,
|
|
1895
|
+
last_action: 'start',
|
|
1896
|
+
},
|
|
1897
|
+
});
|
|
1898
|
+
appendEvent(projectRoot, activeSession, {
|
|
1899
|
+
type: 'run_started',
|
|
1900
|
+
run_id: next.run_id,
|
|
1901
|
+
wave_id: wave != null ? `wave-${wave}` : null,
|
|
1902
|
+
task_id: task,
|
|
1903
|
+
payload: { reason: input.reason || 'run inicializado', mode },
|
|
1904
|
+
});
|
|
1905
|
+
return next;
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
if (!current) {
|
|
1909
|
+
throw new Error('Nenhum ACTIVE-RUN disponível para esta ação.');
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
const next = {
|
|
1913
|
+
...current,
|
|
1914
|
+
updated_at: now,
|
|
1915
|
+
current_wave: wave != null ? wave : current.current_wave,
|
|
1916
|
+
cursor: {
|
|
1917
|
+
wave: wave != null ? wave : current.cursor && current.cursor.wave != null ? current.cursor.wave : current.current_wave,
|
|
1918
|
+
task: task || (current.cursor ? current.cursor.task : null),
|
|
1919
|
+
mode: input.mode ? String(input.mode) : (current.cursor && current.cursor.mode) || mode,
|
|
1920
|
+
},
|
|
1921
|
+
active_tasks: task ? [task] : Array.isArray(input.active_tasks) ? input.active_tasks.map(String) : current.active_tasks || [],
|
|
1922
|
+
pending_checkpoints: pendingCheckpoints,
|
|
1923
|
+
metrics: {
|
|
1924
|
+
...(current.metrics || {}),
|
|
1925
|
+
transitions: Number((current.metrics || {}).transitions || 0) + 1,
|
|
1926
|
+
last_action: action,
|
|
1927
|
+
},
|
|
1928
|
+
};
|
|
1929
|
+
|
|
1930
|
+
if (action === 'pause') {
|
|
1931
|
+
next.status = 'paused';
|
|
1932
|
+
next.metrics.pause_count = Number((current.metrics || {}).pause_count || 0) + 1;
|
|
1933
|
+
} else if (action === 'resume') {
|
|
1934
|
+
next.status = pendingCheckpoints.length ? 'waiting_approval' : 'running';
|
|
1935
|
+
} else if (action === 'replay') {
|
|
1936
|
+
next.status = 'replaying';
|
|
1937
|
+
next.metrics.replay_count = Number((current.metrics || {}).replay_count || 0) + 1;
|
|
1938
|
+
} else {
|
|
1939
|
+
throw new Error(`Ação de runtime desconhecida: ${action}`);
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
const saved = writeRunState(projectRoot, activeSession, next);
|
|
1943
|
+
appendEvent(projectRoot, activeSession, {
|
|
1944
|
+
type: action === 'pause' ? 'run_paused' : action === 'resume' ? 'run_resumed' : 'run_replay_requested',
|
|
1945
|
+
run_id: saved.run_id,
|
|
1946
|
+
wave_id: saved.current_wave != null ? `wave-${saved.current_wave}` : null,
|
|
1947
|
+
task_id: saved.cursor && saved.cursor.task ? saved.cursor.task : null,
|
|
1948
|
+
payload: {
|
|
1949
|
+
reason: input.reason || '',
|
|
1950
|
+
mode: saved.cursor && saved.cursor.mode ? saved.cursor.mode : null,
|
|
1951
|
+
pending_checkpoints: saved.pending_checkpoints || [],
|
|
1952
|
+
},
|
|
1953
|
+
});
|
|
1954
|
+
return saved;
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
/**
|
|
1958
|
+
* Reconstrói a timeline de eventos de OXE-EVENTS.ndjson com deltas entre transições.
|
|
1959
|
+
* @param {string} projectRoot
|
|
1960
|
+
* @param {string|null} activeSession
|
|
1961
|
+
* @param {{ fromEventId?: string, runId?: string, waveId?: number, limit?: number, writeReport?: boolean }} [options]
|
|
1962
|
+
*/
|
|
1963
|
+
function replayEvents(projectRoot, activeSession, options = {}) {
|
|
1964
|
+
let events = readEvents(projectRoot, activeSession);
|
|
1965
|
+
|
|
1966
|
+
if (options.runId) {
|
|
1967
|
+
events = events.filter((e) => e.run_id === options.runId);
|
|
1968
|
+
}
|
|
1969
|
+
if (options.fromEventId) {
|
|
1970
|
+
const idx = events.findIndex((e) => e.event_id === options.fromEventId);
|
|
1971
|
+
if (idx >= 0) events = events.slice(idx);
|
|
1972
|
+
}
|
|
1973
|
+
if (options.waveId != null) {
|
|
1974
|
+
const waveTag = `wave-${options.waveId}`;
|
|
1975
|
+
events = events.filter((e) => e.wave_id === waveTag || String(e.wave_id) === String(options.waveId));
|
|
1976
|
+
}
|
|
1977
|
+
if (options.limit && options.limit > 0) {
|
|
1978
|
+
events = events.slice(0, options.limit);
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
// Ordenar por timestamp
|
|
1982
|
+
events.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
|
|
1983
|
+
|
|
1984
|
+
// Calcular deltas
|
|
1985
|
+
for (let i = 0; i < events.length; i++) {
|
|
1986
|
+
if (i === 0) {
|
|
1987
|
+
events[i]._delta_ms = 0;
|
|
1988
|
+
} else {
|
|
1989
|
+
const prev = new Date(events[i - 1].timestamp).getTime();
|
|
1990
|
+
const curr = new Date(events[i].timestamp).getTime();
|
|
1991
|
+
events[i]._delta_ms = curr - prev;
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
const firstTs = events.length ? new Date(events[0].timestamp).getTime() : null;
|
|
1996
|
+
const lastTs = events.length ? new Date(events[events.length - 1].timestamp).getTime() : null;
|
|
1997
|
+
const duration_ms = firstTs != null && lastTs != null ? lastTs - firstTs : null;
|
|
1998
|
+
|
|
1999
|
+
const waveIds = [...new Set(
|
|
2000
|
+
events
|
|
2001
|
+
.filter((e) => e.wave_id != null)
|
|
2002
|
+
.map((e) => {
|
|
2003
|
+
const m = String(e.wave_id).match(/^wave-(\d+)$/);
|
|
2004
|
+
return m ? Number(m[1]) : null;
|
|
2005
|
+
})
|
|
2006
|
+
.filter((n) => n != null)
|
|
2007
|
+
)].sort((a, b) => a - b);
|
|
2008
|
+
|
|
2009
|
+
const taskSequence = [...new Set(events.filter((e) => e.task_id || e.work_item_id).map((e) => e.task_id || e.work_item_id))];
|
|
2010
|
+
const checkpointSequence = events
|
|
2011
|
+
.filter((e) => String(e.type).includes('checkpoint'))
|
|
2012
|
+
.map((e) => e.event_id);
|
|
2013
|
+
const failureEvents = events.filter((e) =>
|
|
2014
|
+
String(e.type).includes('fail') || String(e.type).includes('error')
|
|
2015
|
+
);
|
|
2016
|
+
|
|
2017
|
+
const report = {
|
|
2018
|
+
events,
|
|
2019
|
+
totalEvents: events.length,
|
|
2020
|
+
duration_ms,
|
|
2021
|
+
runId: options.runId || (events.length ? events[0].run_id : null),
|
|
2022
|
+
waveIds,
|
|
2023
|
+
taskSequence,
|
|
2024
|
+
checkpointSequence,
|
|
2025
|
+
failureEvents,
|
|
2026
|
+
};
|
|
2027
|
+
|
|
2028
|
+
if (options.writeReport) {
|
|
2029
|
+
const p = operationalPaths(projectRoot, activeSession);
|
|
2030
|
+
const scopeRoot = path.dirname(p.events);
|
|
2031
|
+
const reportPath = path.join(scopeRoot, 'REPLAY-SESSION.md');
|
|
2032
|
+
const lines = [
|
|
2033
|
+
'# OXE — Replay Session',
|
|
2034
|
+
'',
|
|
2035
|
+
`- **Data:** ${new Date().toISOString().slice(0, 10)}`,
|
|
2036
|
+
`- **Run:** ${report.runId || '—'}`,
|
|
2037
|
+
`- **Total eventos:** ${report.totalEvents}`,
|
|
2038
|
+
`- **Duração:** ${report.duration_ms != null ? `${(report.duration_ms / 1000).toFixed(1)}s` : '—'}`,
|
|
2039
|
+
`- **Ondas:** ${report.waveIds.join(', ') || '—'}`,
|
|
2040
|
+
`- **Tarefas:** ${report.taskSequence.join(', ') || '—'}`,
|
|
2041
|
+
`- **Falhas:** ${report.failureEvents.length}`,
|
|
2042
|
+
'',
|
|
2043
|
+
'## Timeline',
|
|
2044
|
+
'',
|
|
2045
|
+
'| # | Tipo | Wave | Task | Delta | Timestamp |',
|
|
2046
|
+
'|---|------|------|------|-------|-----------|',
|
|
2047
|
+
];
|
|
2048
|
+
for (let i = 0; i < events.length; i++) {
|
|
2049
|
+
const e = events[i];
|
|
2050
|
+
const delta = i > 0 ? `+${(e._delta_ms / 1000).toFixed(1)}s` : '—';
|
|
2051
|
+
lines.push(`| ${i + 1} | ${e.type} | ${e.wave_id || '—'} | ${e.task_id || e.work_item_id || '—'} | ${delta} | ${e.timestamp} |`);
|
|
2052
|
+
}
|
|
2053
|
+
ensureDirForFile(reportPath);
|
|
2054
|
+
fs.writeFileSync(reportPath, lines.join('\n') + '\n', 'utf8');
|
|
2055
|
+
report._reportPath = reportPath;
|
|
2056
|
+
}
|
|
2057
|
+
|
|
2058
|
+
return report;
|
|
2059
|
+
}
|
|
2060
|
+
|
|
2061
|
+
function replayRuntimeState(projectRoot, activeSession, options = {}) {
|
|
2062
|
+
const current = options.runState || readRunState(projectRoot, activeSession);
|
|
2063
|
+
const runId = options.runId || (current && current.run_id) || null;
|
|
2064
|
+
const replay = replayEvents(projectRoot, activeSession, {
|
|
2065
|
+
runId: runId || undefined,
|
|
2066
|
+
fromEventId: options.fromEventId || undefined,
|
|
2067
|
+
waveId: options.waveId != null ? options.waveId : options.wave,
|
|
2068
|
+
limit: options.limit,
|
|
2069
|
+
writeReport: options.writeReport || false,
|
|
2070
|
+
});
|
|
2071
|
+
const journalPath = runId ? path.join(projectRoot, '.oxe', 'runs', runId, 'journal.json') : null;
|
|
2072
|
+
const journal = journalPath ? readJsonIfExists(journalPath) : null;
|
|
2073
|
+
const verificationArtifacts = current ? loadRuntimeVerificationArtifacts(projectRoot, current) : {
|
|
2074
|
+
manifest: null,
|
|
2075
|
+
residualRisks: null,
|
|
2076
|
+
evidenceCoverage: null,
|
|
2077
|
+
};
|
|
2078
|
+
const consistency = current && journal
|
|
2079
|
+
? buildRecoveryConsistency(projectRoot, activeSession, current, journal, verificationArtifacts)
|
|
2080
|
+
: null;
|
|
2081
|
+
const gateQueue = readRuntimeGates(projectRoot, activeSession, { runId });
|
|
2082
|
+
const policyDecisionsPath = runId ? path.join(projectRoot, '.oxe', 'runs', runId, 'policy-decisions.json') : null;
|
|
2083
|
+
const policyDecisions = policyDecisionsPath ? readJsonIfExists(policyDecisionsPath) : null;
|
|
2084
|
+
const promotionRecordPath = runId ? path.join(projectRoot, '.oxe', 'runs', runId, 'promotion-record.json') : null;
|
|
2085
|
+
const promotionRecord = promotionRecordPath ? readJsonIfExists(promotionRecordPath) : null;
|
|
2086
|
+
const summary = {
|
|
2087
|
+
run_id: runId,
|
|
2088
|
+
generated_at: new Date().toISOString(),
|
|
2089
|
+
replay,
|
|
2090
|
+
gateQueue,
|
|
2091
|
+
policyDecisions: Array.isArray(policyDecisions) ? policyDecisions : [],
|
|
2092
|
+
verification: verificationArtifacts,
|
|
2093
|
+
promotion: promotionRecord || null,
|
|
2094
|
+
consistency,
|
|
2095
|
+
};
|
|
2096
|
+
if (options.writeReport && runId) {
|
|
2097
|
+
const reportPath = path.join(projectRoot, '.oxe', 'runs', runId, 'replay-report.json');
|
|
2098
|
+
ensureDirForFile(reportPath);
|
|
2099
|
+
fs.writeFileSync(reportPath, JSON.stringify(summary, null, 2), 'utf8');
|
|
2100
|
+
summary.report_path = reportPath;
|
|
2101
|
+
}
|
|
2102
|
+
return summary;
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
module.exports = {
|
|
2106
|
+
VALID_RUN_STATUSES,
|
|
2107
|
+
VALID_APPROVAL_POLICIES,
|
|
2108
|
+
VALID_CAPABILITY_TYPES,
|
|
2109
|
+
operationalPaths,
|
|
2110
|
+
makeRunId,
|
|
2111
|
+
appendEvent,
|
|
2112
|
+
readEvents,
|
|
2113
|
+
summarizeEvents,
|
|
2114
|
+
writeRunState,
|
|
2115
|
+
readRunState,
|
|
2116
|
+
readGitActivity,
|
|
2117
|
+
verifyGitEvidence,
|
|
2118
|
+
runtimeStateWarnings,
|
|
2119
|
+
parseCapabilityManifest,
|
|
2120
|
+
readCapabilityCatalog,
|
|
2121
|
+
capabilityCatalogWarnings,
|
|
2122
|
+
buildMemoryLayers,
|
|
2123
|
+
buildOperationalGraph,
|
|
2124
|
+
serializeCanonicalState,
|
|
2125
|
+
hydrateCanonicalState,
|
|
2126
|
+
reduceCanonicalRunState,
|
|
2127
|
+
compileExecutionGraphFromArtifacts,
|
|
2128
|
+
compileVerificationSuiteFromArtifacts,
|
|
2129
|
+
buildRuntimePluginRegistry,
|
|
2130
|
+
buildRuntimeModeStatus,
|
|
2131
|
+
buildRuntimeProviderCatalog,
|
|
2132
|
+
buildRecoveryConsistency,
|
|
2133
|
+
readRuntimeGates,
|
|
2134
|
+
resolveRuntimeGate,
|
|
2135
|
+
runRuntimeVerify,
|
|
2136
|
+
projectRuntimeArtifacts,
|
|
2137
|
+
runRuntimeCiChecks,
|
|
2138
|
+
runRuntimePromotion,
|
|
2139
|
+
recoverRuntimeState,
|
|
2140
|
+
applyRuntimeAction,
|
|
2141
|
+
replayEvents,
|
|
2142
|
+
replayRuntimeState,
|
|
2143
|
+
readRuntimeMultiAgentStatus,
|
|
2144
|
+
};
|