agentxchain 2.104.0 → 2.105.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/README.md +12 -6
- package/bin/agentxchain.js +5 -5
- package/dashboard/app.js +111 -7
- package/dashboard/components/blocked.js +95 -11
- package/dashboard/components/blockers.js +85 -86
- package/dashboard/components/coordinator-timeouts.js +13 -0
- package/dashboard/components/cross-repo.js +17 -12
- package/dashboard/components/gate.js +31 -11
- package/dashboard/components/initiative.js +173 -78
- package/dashboard/components/ledger.js +28 -0
- package/dashboard/components/live-status.js +39 -0
- package/dashboard/components/run-history.js +76 -1
- package/dashboard/components/timeline.js +5 -1
- package/dashboard/index.html +21 -0
- package/dashboard/live-observer.js +91 -0
- package/package.json +1 -1
- package/scripts/release-bump.sh +26 -3
- package/src/commands/accept-turn.js +3 -3
- package/src/commands/decisions.js +98 -29
- package/src/commands/diff.js +27 -4
- package/src/commands/doctor.js +48 -16
- package/src/commands/history.js +21 -3
- package/src/commands/multi.js +223 -54
- package/src/commands/phase.js +11 -13
- package/src/commands/reject-turn.js +1 -1
- package/src/commands/restart.js +28 -11
- package/src/commands/resume.js +6 -6
- package/src/commands/role.js +51 -14
- package/src/commands/run.js +5 -11
- package/src/commands/status.js +145 -13
- package/src/commands/step.js +36 -29
- package/src/lib/admission-control.js +14 -12
- package/src/lib/blocked-state.js +150 -0
- package/src/lib/conflict-actions.js +17 -0
- package/src/lib/context-section-parser.js +2 -0
- package/src/lib/continuity-status.js +1 -1
- package/src/lib/coordinator-blocker-presentation.js +127 -0
- package/src/lib/coordinator-event-narrative.js +43 -0
- package/src/lib/coordinator-gate-approval.js +98 -0
- package/src/lib/coordinator-gate-evaluation-presentation.js +57 -0
- package/src/lib/coordinator-next-actions.js +128 -0
- package/src/lib/coordinator-pending-gate-presentation.js +79 -0
- package/src/lib/coordinator-presentation-detail.js +11 -0
- package/src/lib/coordinator-repo-snapshots.js +53 -0
- package/src/lib/coordinator-repo-status-presentation.js +134 -0
- package/src/lib/dashboard/actions.js +105 -29
- package/src/lib/dashboard/bridge-server.js +7 -0
- package/src/lib/dashboard/coordinator-blockers.js +17 -0
- package/src/lib/dashboard/coordinator-repo-status.js +50 -0
- package/src/lib/dashboard/coordinator-timeout-status.js +34 -11
- package/src/lib/dashboard/state-reader.js +36 -1
- package/src/lib/dispatch-bundle.js +23 -0
- package/src/lib/export-diff.js +70 -38
- package/src/lib/export-verifier.js +3 -0
- package/src/lib/history-diff-summary.js +249 -0
- package/src/lib/manual-qa-fallback.js +18 -0
- package/src/lib/normalized-config.js +27 -22
- package/src/lib/recent-event-summary.js +132 -0
- package/src/lib/repo-decisions.js +69 -28
- package/src/lib/report.js +353 -145
- package/src/lib/run-diff.js +4 -0
- package/src/lib/runtime-capabilities.js +222 -0
|
@@ -9,6 +9,13 @@
|
|
|
9
9
|
* server-side gate evaluators used by `multi step` and `multi approve-gate`.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
import {
|
|
13
|
+
getCoordinatorAttentionStatusCard,
|
|
14
|
+
getCoordinatorBlockerDetails,
|
|
15
|
+
} from '../../src/lib/coordinator-blocker-presentation.js';
|
|
16
|
+
import { buildCoordinatorGateEvaluationPresentation } from '../../src/lib/coordinator-gate-evaluation-presentation.js';
|
|
17
|
+
import { getCoordinatorPendingGateDetails } from '../../src/lib/coordinator-pending-gate-presentation.js';
|
|
18
|
+
|
|
12
19
|
function esc(str) {
|
|
13
20
|
if (!str) return '';
|
|
14
21
|
return String(str)
|
|
@@ -41,6 +48,7 @@ function blockerColor(code) {
|
|
|
41
48
|
function renderBlockerRow(blocker) {
|
|
42
49
|
const code = blocker.code || 'unknown';
|
|
43
50
|
const color = blockerColor(code);
|
|
51
|
+
const details = getCoordinatorBlockerDetails(blocker);
|
|
44
52
|
let html = `<div class="turn-card" style="border-left: 3px solid ${color}">
|
|
45
53
|
<div class="turn-header">
|
|
46
54
|
${badge(code, color)}
|
|
@@ -50,62 +58,55 @@ function renderBlockerRow(blocker) {
|
|
|
50
58
|
html += `<div class="turn-summary">${esc(blocker.message)}</div>`;
|
|
51
59
|
}
|
|
52
60
|
|
|
53
|
-
if (
|
|
54
|
-
html += `<dl class="detail-list"
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
html += `<dt>Expected</dt><dd class="mono">${esc(blocker.expected_run_id)}</dd>`;
|
|
58
|
-
}
|
|
59
|
-
if (blocker.actual_run_id) {
|
|
60
|
-
html += `<dt>Actual</dt><dd class="mono">${esc(blocker.actual_run_id)}</dd>`;
|
|
61
|
+
if (details.length > 0) {
|
|
62
|
+
html += `<dl class="detail-list">`;
|
|
63
|
+
for (const detail of details) {
|
|
64
|
+
html += `<dt>${esc(detail.label)}</dt><dd${detail.mono ? ' class="mono"' : ''}>${esc(detail.value)}</dd>`;
|
|
61
65
|
}
|
|
62
66
|
html += `</dl>`;
|
|
63
67
|
}
|
|
64
68
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
html += `<dt>Required Phase</dt><dd>${esc(blocker.required_phase)}</dd>`;
|
|
73
|
-
}
|
|
74
|
-
html += `</dl>`;
|
|
69
|
+
html += `</div>`;
|
|
70
|
+
return html;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function renderDetailRows(details) {
|
|
74
|
+
if (!Array.isArray(details) || details.length === 0) {
|
|
75
|
+
return '';
|
|
75
76
|
}
|
|
76
77
|
|
|
77
|
-
html
|
|
78
|
+
let html = '';
|
|
79
|
+
for (const detail of details) {
|
|
80
|
+
html += `<dt>${esc(detail.label)}</dt><dd${detail.mono ? ' class="mono"' : ''}>${esc(detail.value)}</dd>`;
|
|
81
|
+
}
|
|
78
82
|
return html;
|
|
79
83
|
}
|
|
80
84
|
|
|
81
|
-
function renderActiveGate(active) {
|
|
85
|
+
function renderActiveGate(active, coordinatorBlockers = null) {
|
|
82
86
|
if (!active) return '';
|
|
83
87
|
|
|
88
|
+
const pendingGateDetails = active.pending === true
|
|
89
|
+
? getCoordinatorPendingGateDetails({
|
|
90
|
+
pendingGate: coordinatorBlockers?.pending_gate,
|
|
91
|
+
active,
|
|
92
|
+
})
|
|
93
|
+
: [];
|
|
94
|
+
const evaluationPresentation = active.pending === true
|
|
95
|
+
? null
|
|
96
|
+
: buildCoordinatorGateEvaluationPresentation({
|
|
97
|
+
gateType: active.gate_type,
|
|
98
|
+
evaluation: active,
|
|
99
|
+
includeReady: true,
|
|
100
|
+
includeBlockerCount: false,
|
|
101
|
+
});
|
|
84
102
|
let html = `<div class="gate-card">
|
|
85
103
|
<h3>Active Gate</h3>
|
|
86
|
-
<dl class="detail-list"
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
html += `<dt>
|
|
91
|
-
|
|
92
|
-
if (active.current_phase) {
|
|
93
|
-
html += `<dt>Current Phase</dt><dd>${esc(active.current_phase)}</dd>`;
|
|
94
|
-
}
|
|
95
|
-
if (active.target_phase) {
|
|
96
|
-
html += `<dt>Target Phase</dt><dd>${esc(active.target_phase)}</dd>`;
|
|
97
|
-
}
|
|
98
|
-
if (typeof active.ready === 'boolean') {
|
|
99
|
-
html += `<dt>Ready</dt><dd>${active.ready ? 'Yes' : 'No'}</dd>`;
|
|
100
|
-
}
|
|
101
|
-
if (active.pending === true) {
|
|
102
|
-
html += `<dt>Pending Approval</dt><dd>Yes</dd>`;
|
|
103
|
-
}
|
|
104
|
-
if (Array.isArray(active.required_repos) && active.required_repos.length > 0) {
|
|
105
|
-
html += `<dt>Required Repos</dt><dd>${esc(active.required_repos.join(', '))}</dd>`;
|
|
106
|
-
}
|
|
107
|
-
if (Array.isArray(active.human_barriers) && active.human_barriers.length > 0) {
|
|
108
|
-
html += `<dt>Human Barriers</dt><dd>${esc(active.human_barriers.join(', '))}</dd>`;
|
|
104
|
+
<dl class="detail-list">`;
|
|
105
|
+
if (pendingGateDetails.length > 0) {
|
|
106
|
+
html += renderDetailRows(pendingGateDetails);
|
|
107
|
+
} else {
|
|
108
|
+
html += `<dt>Type</dt><dd>${esc(active.gate_type)}</dd>`;
|
|
109
|
+
html += renderDetailRows(evaluationPresentation?.details || []);
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
html += `</dl>`;
|
|
@@ -124,24 +125,27 @@ function renderActiveGate(active) {
|
|
|
124
125
|
}
|
|
125
126
|
|
|
126
127
|
function renderRecoveryCommand(data) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
<pre class="recovery-command mono" data-copy="agentxchain multi approve-gate">agentxchain multi approve-gate</pre>
|
|
131
|
-
</div>`;
|
|
128
|
+
const nextActions = Array.isArray(data.next_actions) ? data.next_actions : [];
|
|
129
|
+
if (nextActions.length === 0) {
|
|
130
|
+
return '';
|
|
132
131
|
}
|
|
133
132
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
133
|
+
let html = `<div class="section"><h3>Next Actions</h3><ol class="action-list">`;
|
|
134
|
+
for (const action of nextActions) {
|
|
135
|
+
html += `<li class="turn-card">
|
|
136
|
+
<div class="turn-header">
|
|
137
|
+
<span class="mono">${esc(action.command || 'n/a')}</span>
|
|
138
|
+
</div>`;
|
|
139
|
+
if (action.reason) {
|
|
140
|
+
html += `<div class="turn-summary">${esc(action.reason)}</div>`;
|
|
141
|
+
}
|
|
142
|
+
if (action.command) {
|
|
143
|
+
html += `<pre class="recovery-command mono" data-copy="${esc(action.command)}">${esc(action.command)}</pre>`;
|
|
144
|
+
}
|
|
145
|
+
html += `</li>`;
|
|
142
146
|
}
|
|
143
|
-
|
|
144
|
-
return
|
|
147
|
+
html += `</ol></div>`;
|
|
148
|
+
return html;
|
|
145
149
|
}
|
|
146
150
|
|
|
147
151
|
export function render({ coordinatorBlockers }) {
|
|
@@ -156,6 +160,7 @@ export function render({ coordinatorBlockers }) {
|
|
|
156
160
|
const data = coordinatorBlockers;
|
|
157
161
|
const blockers = data.active?.blockers || [];
|
|
158
162
|
const hasBlockers = blockers.length > 0 && !blockers.every(b => b.code === 'no_next_phase');
|
|
163
|
+
const statusCard = getCoordinatorAttentionStatusCard(data);
|
|
159
164
|
|
|
160
165
|
let html = `<div class="blockers-view">`;
|
|
161
166
|
|
|
@@ -188,16 +193,13 @@ export function render({ coordinatorBlockers }) {
|
|
|
188
193
|
}
|
|
189
194
|
|
|
190
195
|
// Status summary
|
|
191
|
-
if (!hasBlockers &&
|
|
192
|
-
html += `<div class="gate-card"><h3
|
|
193
|
-
<p class="turn-summary"
|
|
194
|
-
} else if (!hasBlockers) {
|
|
195
|
-
html += `<div class="gate-card"><h3>No Blockers</h3>
|
|
196
|
-
<p class="turn-summary">The coordinator gate has no outstanding blockers.</p></div>`;
|
|
196
|
+
if (!hasBlockers && statusCard) {
|
|
197
|
+
html += `<div class="gate-card"><h3>${esc(statusCard.title)}</h3>
|
|
198
|
+
<p class="turn-summary">${esc(statusCard.message)}</p></div>`;
|
|
197
199
|
}
|
|
198
200
|
|
|
199
201
|
// Active gate detail
|
|
200
|
-
html += renderActiveGate(data.active);
|
|
202
|
+
html += renderActiveGate(data.active, data);
|
|
201
203
|
|
|
202
204
|
// Recovery
|
|
203
205
|
html += renderRecoveryCommand(data);
|
|
@@ -208,21 +210,20 @@ export function render({ coordinatorBlockers }) {
|
|
|
208
210
|
const { phase_transition, run_completion } = data.evaluations;
|
|
209
211
|
|
|
210
212
|
if (phase_transition) {
|
|
213
|
+
const phasePresentation = buildCoordinatorGateEvaluationPresentation({
|
|
214
|
+
gateType: 'phase_transition',
|
|
215
|
+
evaluation: phase_transition,
|
|
216
|
+
});
|
|
211
217
|
html += `<div class="turn-card" data-turn-expand>
|
|
212
218
|
<div class="turn-header">
|
|
213
|
-
<span
|
|
214
|
-
${badge(
|
|
219
|
+
<span>${esc(phasePresentation.title)}</span>
|
|
220
|
+
${badge(phasePresentation.statusLabel, phase_transition.ready ? 'var(--green)' : 'var(--yellow)')}
|
|
215
221
|
</div>
|
|
216
222
|
<div class="turn-detail-panel">
|
|
217
|
-
<dl class="detail-list">`;
|
|
218
|
-
if (
|
|
219
|
-
if (phase_transition.target_phase) html += `<dt>Target</dt><dd>${esc(phase_transition.target_phase)}</dd>`;
|
|
220
|
-
if (phase_transition.gate_id) html += `<dt>Gate</dt><dd class="mono">${esc(phase_transition.gate_id)}</dd>`;
|
|
221
|
-
html += `<dt>Blockers</dt><dd>${phase_transition.blockers?.length || 0}</dd>`;
|
|
222
|
-
html += `</dl>`;
|
|
223
|
-
if (phase_transition.blockers?.length > 0) {
|
|
223
|
+
<dl class="detail-list">${renderDetailRows(phasePresentation.details)}</dl>`;
|
|
224
|
+
if (phasePresentation.blockers.length > 0) {
|
|
224
225
|
html += `<div class="annotation-list" style="margin-top:8px">`;
|
|
225
|
-
for (const b of
|
|
226
|
+
for (const b of phasePresentation.blockers) {
|
|
226
227
|
html += `<div class="annotation-card">
|
|
227
228
|
<span class="mono">${esc(b.code || 'unknown')}</span>
|
|
228
229
|
<span>${esc(b.message || '')}</span>
|
|
@@ -234,22 +235,20 @@ export function render({ coordinatorBlockers }) {
|
|
|
234
235
|
}
|
|
235
236
|
|
|
236
237
|
if (run_completion) {
|
|
238
|
+
const completionPresentation = buildCoordinatorGateEvaluationPresentation({
|
|
239
|
+
gateType: 'run_completion',
|
|
240
|
+
evaluation: run_completion,
|
|
241
|
+
});
|
|
237
242
|
html += `<div class="turn-card" data-turn-expand>
|
|
238
243
|
<div class="turn-header">
|
|
239
|
-
<span
|
|
240
|
-
${badge(
|
|
244
|
+
<span>${esc(completionPresentation.title)}</span>
|
|
245
|
+
${badge(completionPresentation.statusLabel, run_completion.ready ? 'var(--green)' : 'var(--yellow)')}
|
|
241
246
|
</div>
|
|
242
247
|
<div class="turn-detail-panel">
|
|
243
|
-
<dl class="detail-list">`;
|
|
244
|
-
if (
|
|
245
|
-
html += `<dt>Blockers</dt><dd>${run_completion.blockers?.length || 0}</dd>`;
|
|
246
|
-
if (typeof run_completion.requires_human_approval === 'boolean') {
|
|
247
|
-
html += `<dt>Human Approval</dt><dd>${run_completion.requires_human_approval ? 'Required' : 'Not required'}</dd>`;
|
|
248
|
-
}
|
|
249
|
-
html += `</dl>`;
|
|
250
|
-
if (run_completion.blockers?.length > 0) {
|
|
248
|
+
<dl class="detail-list">${renderDetailRows(completionPresentation.details)}</dl>`;
|
|
249
|
+
if (completionPresentation.blockers.length > 0) {
|
|
251
250
|
html += `<div class="annotation-list" style="margin-top:8px">`;
|
|
252
|
-
for (const b of
|
|
251
|
+
for (const b of completionPresentation.blockers) {
|
|
253
252
|
html += `<div class="annotation-card">
|
|
254
253
|
<span class="mono">${esc(b.code || 'unknown')}</span>
|
|
255
254
|
<span>${esc(b.message || '')}</span>
|
|
@@ -152,6 +152,18 @@ function renderEventTable(events, title) {
|
|
|
152
152
|
return html;
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
function renderDetailRows(details) {
|
|
156
|
+
if (!Array.isArray(details) || details.length === 0) {
|
|
157
|
+
return '';
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let html = '';
|
|
161
|
+
for (const detail of details) {
|
|
162
|
+
html += `<dt>${esc(detail.label)}</dt><dd${detail.mono ? ' class="mono"' : ''}>${esc(detail.value)}</dd>`;
|
|
163
|
+
}
|
|
164
|
+
return html;
|
|
165
|
+
}
|
|
166
|
+
|
|
155
167
|
function renderRepoCard(repo) {
|
|
156
168
|
let html = `<div class="turn-card">
|
|
157
169
|
<div class="turn-header">
|
|
@@ -168,6 +180,7 @@ function renderRepoCard(repo) {
|
|
|
168
180
|
if (repo.phase) {
|
|
169
181
|
html += `<dt>Phase</dt><dd>${esc(repo.phase)}</dd>`;
|
|
170
182
|
}
|
|
183
|
+
html += renderDetailRows(repo.details);
|
|
171
184
|
html += `</dl>`;
|
|
172
185
|
|
|
173
186
|
if (repo.error) {
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { renderLiveStatus } from './live-status.js';
|
|
2
|
+
import { summarizeCoordinatorEvent } from '../../src/lib/coordinator-event-narrative.js';
|
|
3
|
+
|
|
1
4
|
function esc(str) {
|
|
2
5
|
if (!str) return '';
|
|
3
6
|
return String(str)
|
|
@@ -26,57 +29,57 @@ function describeEvent(entry) {
|
|
|
26
29
|
case 'run_initialized':
|
|
27
30
|
return {
|
|
28
31
|
title: 'Coordinator Initialized',
|
|
29
|
-
detail:
|
|
32
|
+
detail: summarizeCoordinatorEvent(entry),
|
|
30
33
|
};
|
|
31
34
|
case 'turn_dispatched':
|
|
32
35
|
return {
|
|
33
36
|
title: 'Turn Dispatched',
|
|
34
|
-
detail:
|
|
37
|
+
detail: summarizeCoordinatorEvent(entry),
|
|
35
38
|
};
|
|
36
39
|
case 'acceptance_projection':
|
|
37
40
|
return {
|
|
38
41
|
title: 'Acceptance Projected',
|
|
39
|
-
detail: entry
|
|
42
|
+
detail: summarizeCoordinatorEvent(entry),
|
|
40
43
|
};
|
|
41
44
|
case 'context_generated':
|
|
42
45
|
return {
|
|
43
46
|
title: 'Context Generated',
|
|
44
|
-
detail:
|
|
47
|
+
detail: summarizeCoordinatorEvent(entry),
|
|
45
48
|
};
|
|
46
49
|
case 'phase_transition_requested':
|
|
47
50
|
return {
|
|
48
51
|
title: 'Phase Gate Requested',
|
|
49
|
-
detail:
|
|
52
|
+
detail: summarizeCoordinatorEvent(entry),
|
|
50
53
|
};
|
|
51
54
|
case 'phase_transition_approved':
|
|
52
55
|
return {
|
|
53
56
|
title: 'Phase Gate Approved',
|
|
54
|
-
detail:
|
|
57
|
+
detail: summarizeCoordinatorEvent(entry),
|
|
55
58
|
};
|
|
56
59
|
case 'run_completion_requested':
|
|
57
60
|
return {
|
|
58
61
|
title: 'Completion Gate Requested',
|
|
59
|
-
detail: entry
|
|
62
|
+
detail: summarizeCoordinatorEvent(entry),
|
|
60
63
|
};
|
|
61
64
|
case 'run_completed':
|
|
62
65
|
return {
|
|
63
66
|
title: 'Initiative Completed',
|
|
64
|
-
detail: entry
|
|
67
|
+
detail: summarizeCoordinatorEvent(entry),
|
|
65
68
|
};
|
|
66
69
|
case 'state_resynced':
|
|
67
70
|
return {
|
|
68
71
|
title: 'Coordinator Resynced',
|
|
69
|
-
detail:
|
|
72
|
+
detail: summarizeCoordinatorEvent(entry),
|
|
70
73
|
};
|
|
71
74
|
default:
|
|
72
75
|
return {
|
|
73
76
|
title: entry?.type || 'Unknown Event',
|
|
74
|
-
detail: entry
|
|
77
|
+
detail: summarizeCoordinatorEvent(entry),
|
|
75
78
|
};
|
|
76
79
|
}
|
|
77
80
|
}
|
|
78
81
|
|
|
79
|
-
export function render({ coordinatorState, coordinatorHistory = [] }) {
|
|
82
|
+
export function render({ coordinatorState, coordinatorHistory = [], liveMeta = null }) {
|
|
80
83
|
if (!coordinatorState) {
|
|
81
84
|
return `<div class="placeholder"><h2>No Cross-Repo Timeline</h2><p>No coordinator run found. Start one with <code class="mono">agentxchain multi init</code></p></div>`;
|
|
82
85
|
}
|
|
@@ -86,7 +89,9 @@ export function render({ coordinatorState, coordinatorHistory = [] }) {
|
|
|
86
89
|
return `<div class="placeholder"><h2>Cross-Repo Timeline</h2><p>No coordinator history recorded yet.</p></div>`;
|
|
87
90
|
}
|
|
88
91
|
|
|
89
|
-
let html = `<div class="timeline-view"
|
|
92
|
+
let html = `<div class="timeline-view">`;
|
|
93
|
+
html += renderLiveStatus(liveMeta);
|
|
94
|
+
html += `<div class="section"><h3>Cross-Repo Timeline</h3><div class="turn-list">`;
|
|
90
95
|
for (const entry of events) {
|
|
91
96
|
const event = describeEvent(entry);
|
|
92
97
|
const repoId = entry.repo_id || entry.target_repo_id || null;
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* Shows a narrow local approve action plus the exact CLI fallback command.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { getCoordinatorPendingGateDetails } from '../../src/lib/coordinator-pending-gate-presentation.js';
|
|
9
|
+
|
|
8
10
|
function esc(str) {
|
|
9
11
|
if (!str) return '';
|
|
10
12
|
return String(str)
|
|
@@ -126,6 +128,18 @@ function renderApproveControls({ buttonLabel, cliCommand }) {
|
|
|
126
128
|
</div>`;
|
|
127
129
|
}
|
|
128
130
|
|
|
131
|
+
function renderDetailRows(details) {
|
|
132
|
+
if (!Array.isArray(details) || details.length === 0) {
|
|
133
|
+
return '';
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
let html = '';
|
|
137
|
+
for (const detail of details) {
|
|
138
|
+
html += `<dt>${esc(detail.label)}</dt><dd${detail.mono ? ' class="mono"' : ''}>${esc(detail.value)}</dd>`;
|
|
139
|
+
}
|
|
140
|
+
return html;
|
|
141
|
+
}
|
|
142
|
+
|
|
129
143
|
export { findPostGateTurns, aggregateEvidence };
|
|
130
144
|
|
|
131
145
|
function findCoordinatorGateRequest(history, pendingGate) {
|
|
@@ -227,13 +241,20 @@ export function render({
|
|
|
227
241
|
const evidence = isCoordinator
|
|
228
242
|
? aggregateCoordinatorEvidence(postGateTurns)
|
|
229
243
|
: aggregateEvidence(postGateTurns);
|
|
244
|
+
const coordinatorDetails = isCoordinator
|
|
245
|
+
? getCoordinatorPendingGateDetails({ pendingGate: pendingTransition, includeHumanBarriers: false })
|
|
246
|
+
: [];
|
|
230
247
|
html += `<div class="gate-card">
|
|
231
248
|
<h3>Phase Transition Gate</h3>
|
|
232
|
-
<dl class="detail-list"
|
|
233
|
-
|
|
249
|
+
<dl class="detail-list">`;
|
|
250
|
+
if (isCoordinator) {
|
|
251
|
+
html += renderDetailRows(coordinatorDetails);
|
|
252
|
+
} else {
|
|
253
|
+
html += `<dt>From</dt><dd>${esc(pendingTransition.from || state?.phase || coordinatorState?.phase)}</dd>
|
|
234
254
|
<dt>To</dt><dd>${esc(pendingTransition.to)}</dd>`;
|
|
235
|
-
|
|
236
|
-
|
|
255
|
+
if (pendingTransition.gate) {
|
|
256
|
+
html += `<dt>Gate</dt><dd class="mono">${esc(pendingTransition.gate)}</dd>`;
|
|
257
|
+
}
|
|
237
258
|
}
|
|
238
259
|
if (pendingTransition.requested_by_turn) {
|
|
239
260
|
html += `<dt>Requested By</dt><dd class="mono">${esc(pendingTransition.requested_by_turn)}</dd>`;
|
|
@@ -241,9 +262,6 @@ export function render({
|
|
|
241
262
|
if (postGateTurns.length > 0) {
|
|
242
263
|
html += `<dt>Evidence Turns</dt><dd>${postGateTurns.length} turn${postGateTurns.length !== 1 ? 's' : ''}</dd>`;
|
|
243
264
|
}
|
|
244
|
-
if (isCoordinator && Array.isArray(pendingTransition.required_repos) && pendingTransition.required_repos.length > 0) {
|
|
245
|
-
html += `<dt>Required Repos</dt><dd>${esc(pendingTransition.required_repos.join(', '))}</dd>`;
|
|
246
|
-
}
|
|
247
265
|
html += `</dl>`;
|
|
248
266
|
if (evidence.summaries.length > 0) {
|
|
249
267
|
html += `<div class="gate-evidence"><h4>Agent Summaries</h4><ul>`;
|
|
@@ -280,10 +298,15 @@ export function render({
|
|
|
280
298
|
const evidence = isCoordinator
|
|
281
299
|
? aggregateCoordinatorEvidence(postGateTurns)
|
|
282
300
|
: aggregateEvidence(postGateTurns);
|
|
301
|
+
const coordinatorDetails = isCoordinator
|
|
302
|
+
? getCoordinatorPendingGateDetails({ pendingGate: pendingCompletion, includeHumanBarriers: false })
|
|
303
|
+
: [];
|
|
283
304
|
html += `<div class="gate-card">
|
|
284
305
|
<h3>Run Completion Gate</h3>
|
|
285
306
|
<dl class="detail-list">`;
|
|
286
|
-
if (
|
|
307
|
+
if (isCoordinator) {
|
|
308
|
+
html += renderDetailRows(coordinatorDetails);
|
|
309
|
+
} else if (pendingCompletion.gate) {
|
|
287
310
|
html += `<dt>Gate</dt><dd class="mono">${esc(pendingCompletion.gate)}</dd>`;
|
|
288
311
|
}
|
|
289
312
|
if (pendingCompletion.requested_by_turn) {
|
|
@@ -292,9 +315,6 @@ export function render({
|
|
|
292
315
|
if (postGateTurns.length > 0) {
|
|
293
316
|
html += `<dt>Evidence Turns</dt><dd>${postGateTurns.length} turn${postGateTurns.length !== 1 ? 's' : ''}</dd>`;
|
|
294
317
|
}
|
|
295
|
-
if (isCoordinator && Array.isArray(pendingCompletion.required_repos) && pendingCompletion.required_repos.length > 0) {
|
|
296
|
-
html += `<dt>Required Repos</dt><dd>${esc(pendingCompletion.required_repos.join(', '))}</dd>`;
|
|
297
|
-
}
|
|
298
318
|
html += `</dl>`;
|
|
299
319
|
if (evidence.summaries.length > 0) {
|
|
300
320
|
html += `<div class="gate-evidence"><h4>Agent Summaries</h4><ul>`;
|