agentxchain 2.70.0 → 2.72.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/dashboard/app.js +2 -1
- package/dashboard/components/ledger.js +112 -57
- package/package.json +1 -1
- package/src/commands/multi.js +56 -2
- package/src/lib/coordinator-state.js +4 -0
package/dashboard/app.js
CHANGED
|
@@ -20,7 +20,7 @@ import { render as renderCoordinatorTimeouts } from './components/coordinator-ti
|
|
|
20
20
|
|
|
21
21
|
const VIEWS = {
|
|
22
22
|
timeline: { fetch: ['state', 'continuity', 'history', 'audit', 'annotations', 'connectors'], render: renderTimeline },
|
|
23
|
-
ledger: { fetch: ['ledger'], render: renderLedger },
|
|
23
|
+
ledger: { fetch: ['state', 'ledger', 'coordinatorState', 'coordinatorLedger'], render: renderLedger },
|
|
24
24
|
hooks: { fetch: ['audit', 'annotations'], render: renderHooks },
|
|
25
25
|
blocked: { fetch: ['state', 'audit', 'coordinatorState', 'coordinatorAudit'], render: renderBlocked },
|
|
26
26
|
gate: { fetch: ['state', 'history', 'coordinatorState', 'coordinatorHistory', 'coordinatorBarriers'], render: renderGate },
|
|
@@ -38,6 +38,7 @@ const API_MAP = {
|
|
|
38
38
|
continuity: '/api/continuity',
|
|
39
39
|
history: '/api/history',
|
|
40
40
|
ledger: '/api/ledger',
|
|
41
|
+
coordinatorLedger: '/api/coordinator/ledger',
|
|
41
42
|
audit: '/api/hooks/audit',
|
|
42
43
|
annotations: '/api/hooks/annotations',
|
|
43
44
|
coordinatorState: '/api/coordinator/state',
|
|
@@ -79,12 +79,7 @@ function hasObjections(entry) {
|
|
|
79
79
|
return Array.isArray(entry.objections) && entry.objections.length > 0;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
|
|
83
|
-
if (!ledger || ledger.length === 0) {
|
|
84
|
-
return `<div class="placeholder"><h2>Decision Ledger</h2><p>No decisions recorded yet.</p></div>`;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const filtered = filterEntries(ledger, filter);
|
|
82
|
+
function renderFilterBar(ledger, filter) {
|
|
88
83
|
const selectedAgent = filter.agent || 'all';
|
|
89
84
|
const selectedPhase = filter.phase || 'all';
|
|
90
85
|
const dateFrom = filter.dateFrom || '';
|
|
@@ -93,56 +88,57 @@ export function render({ ledger, filter = {} }) {
|
|
|
93
88
|
const agents = collectAgents(ledger);
|
|
94
89
|
const phases = collectPhases(ledger);
|
|
95
90
|
|
|
96
|
-
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
91
|
+
return `<div class="filter-bar">
|
|
92
|
+
<label class="filter-control">
|
|
93
|
+
<span>Agent</span>
|
|
94
|
+
<select data-view-control="ledger-agent">
|
|
95
|
+
<option value="all"${selectedAgent === 'all' ? ' selected' : ''}>All roles</option>
|
|
96
|
+
${agents.map((agent) => `<option value="${esc(agent)}"${selectedAgent === agent ? ' selected' : ''}>${esc(agent)}</option>`).join('')}
|
|
97
|
+
</select>
|
|
98
|
+
</label>
|
|
99
|
+
<label class="filter-control">
|
|
100
|
+
<span>Phase</span>
|
|
101
|
+
<select data-view-control="ledger-phase">
|
|
102
|
+
<option value="all"${selectedPhase === 'all' ? ' selected' : ''}>All phases</option>
|
|
103
|
+
${phases.map((p) => `<option value="${esc(p)}"${selectedPhase === p ? ' selected' : ''}>${esc(p)}</option>`).join('')}
|
|
104
|
+
</select>
|
|
105
|
+
</label>
|
|
106
|
+
<label class="filter-control">
|
|
107
|
+
<span>Search</span>
|
|
108
|
+
<input
|
|
109
|
+
type="search"
|
|
110
|
+
data-view-control="ledger-query"
|
|
111
|
+
value="${esc(query)}"
|
|
112
|
+
placeholder="Filter by role or decision"
|
|
113
|
+
autocomplete="off"
|
|
114
|
+
>
|
|
115
|
+
</label>
|
|
116
|
+
<label class="filter-control">
|
|
117
|
+
<span>From</span>
|
|
118
|
+
<input
|
|
119
|
+
type="date"
|
|
120
|
+
data-view-control="ledger-date-from"
|
|
121
|
+
value="${esc(dateFrom)}"
|
|
122
|
+
autocomplete="off"
|
|
123
|
+
>
|
|
124
|
+
</label>
|
|
125
|
+
<label class="filter-control">
|
|
126
|
+
<span>To</span>
|
|
127
|
+
<input
|
|
128
|
+
type="date"
|
|
129
|
+
data-view-control="ledger-date-to"
|
|
130
|
+
value="${esc(dateTo)}"
|
|
131
|
+
autocomplete="off"
|
|
132
|
+
>
|
|
133
|
+
</label>
|
|
134
|
+
</div>`;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function renderLedgerTable(entries, filter) {
|
|
138
|
+
const filtered = filterEntries(entries, filter);
|
|
139
|
+
let html = `<table class="data-table">
|
|
140
|
+
<thead><tr><th>Turn</th><th>Agent</th><th>Decision</th><th>Timestamp</th></tr></thead>
|
|
141
|
+
<tbody>`;
|
|
146
142
|
|
|
147
143
|
if (filtered.length === 0) {
|
|
148
144
|
html += `<tr><td colspan="4">No decisions match the current filters.</td></tr>`;
|
|
@@ -160,6 +156,65 @@ export function render({ ledger, filter = {} }) {
|
|
|
160
156
|
}
|
|
161
157
|
}
|
|
162
158
|
|
|
163
|
-
html += `</tbody></table
|
|
159
|
+
html += `</tbody></table>`;
|
|
160
|
+
return { html, filteredCount: filtered.length };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function buildSections({ ledger, coordinatorLedger, state, coordinatorState }) {
|
|
164
|
+
const sections = [];
|
|
165
|
+
const hasRepoContext = Boolean(state) || (Array.isArray(ledger) && ledger.length > 0);
|
|
166
|
+
const hasCoordinatorContext = Boolean(coordinatorState) || (Array.isArray(coordinatorLedger) && coordinatorLedger.length > 0);
|
|
167
|
+
|
|
168
|
+
if (hasRepoContext) {
|
|
169
|
+
sections.push({
|
|
170
|
+
title: hasCoordinatorContext ? 'Repo Decision Ledger' : 'Decision Ledger',
|
|
171
|
+
entries: Array.isArray(ledger) ? ledger : [],
|
|
172
|
+
emptyMessage: 'No repo-local decisions recorded yet.',
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (hasCoordinatorContext) {
|
|
177
|
+
sections.push({
|
|
178
|
+
title: hasRepoContext ? 'Coordinator Decision Ledger' : 'Coordinator Decision Ledger',
|
|
179
|
+
entries: Array.isArray(coordinatorLedger) ? coordinatorLedger : [],
|
|
180
|
+
emptyMessage: 'No coordinator decisions recorded yet.',
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return sections;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function render({
|
|
188
|
+
ledger,
|
|
189
|
+
coordinatorLedger = null,
|
|
190
|
+
state = null,
|
|
191
|
+
coordinatorState = null,
|
|
192
|
+
filter = {},
|
|
193
|
+
}) {
|
|
194
|
+
const sections = buildSections({ ledger, coordinatorLedger, state, coordinatorState });
|
|
195
|
+
if (sections.length === 0) {
|
|
196
|
+
return `<div class="placeholder"><h2>Decision Ledger</h2><p>No decisions recorded yet.</p></div>`;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const combinedLedger = sections.flatMap((section) => section.entries);
|
|
200
|
+
let html = `<div class="ledger-view">
|
|
201
|
+
<div class="section"><h3>Decisions</h3>
|
|
202
|
+
<p class="section-subtitle">${sections.length === 1 ? 'Decision ledger surface' : 'Repo-local and coordinator decision ledgers'}</p>
|
|
203
|
+
${renderFilterBar(combinedLedger, filter)}
|
|
204
|
+
</div>`;
|
|
205
|
+
|
|
206
|
+
for (const section of sections) {
|
|
207
|
+
const { html: tableHtml, filteredCount } = renderLedgerTable(section.entries, filter);
|
|
208
|
+
html += `<div class="section"><h3>${section.title}</h3>
|
|
209
|
+
<p class="section-subtitle">${filteredCount} of ${section.entries.length} decision${section.entries.length !== 1 ? 's' : ''} shown</p>`;
|
|
210
|
+
if (section.entries.length === 0) {
|
|
211
|
+
html += `<div class="placeholder compact"><p>${section.emptyMessage}</p></div>`;
|
|
212
|
+
} else {
|
|
213
|
+
html += tableHtml;
|
|
214
|
+
}
|
|
215
|
+
html += `</div>`;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
html += `</div>`;
|
|
164
219
|
return html;
|
|
165
220
|
}
|
package/package.json
CHANGED
package/src/commands/multi.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* multi resync — detect divergence and rebuild coordinator state from repo authority
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import chalk from 'chalk';
|
|
13
14
|
import { loadCoordinatorConfig } from '../lib/coordinator-config.js';
|
|
14
15
|
import {
|
|
15
16
|
initializeCoordinatorRun,
|
|
@@ -105,11 +106,44 @@ export async function multiStatusCommand(options) {
|
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
console.log(`Super Run: ${status.super_run_id}`);
|
|
108
|
-
console.log(`Status: ${status.status}`);
|
|
109
|
+
console.log(`Status: ${formatCoordinatorStatus(status.status)}`);
|
|
109
110
|
console.log(`Phase: ${status.phase}`);
|
|
110
111
|
|
|
112
|
+
// Elapsed time for active runs
|
|
113
|
+
if (status.created_at && status.status === 'active') {
|
|
114
|
+
const elapsedMs = Date.now() - new Date(status.created_at).getTime();
|
|
115
|
+
if (elapsedMs >= 0) {
|
|
116
|
+
const secs = Math.floor(elapsedMs / 1000);
|
|
117
|
+
const mins = Math.floor(secs / 60);
|
|
118
|
+
const remainSecs = secs % 60;
|
|
119
|
+
const elapsed = mins > 0 ? `${mins}m ${remainSecs}s` : `${remainSecs}s`;
|
|
120
|
+
console.log(`Elapsed: ${elapsed}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Blocked state with reason
|
|
125
|
+
if (status.status === 'blocked') {
|
|
126
|
+
const reason = (typeof status.blocked_reason === 'string' && status.blocked_reason.trim())
|
|
127
|
+
? status.blocked_reason.trim()
|
|
128
|
+
: 'unknown reason';
|
|
129
|
+
console.log(`Blocked: ${chalk.red.bold('BLOCKED')} — ${reason}`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Pending gate with phase transition direction
|
|
111
133
|
if (status.pending_gate) {
|
|
112
|
-
|
|
134
|
+
const pg = status.pending_gate;
|
|
135
|
+
const fromTo = pg.from && pg.to ? ` ${pg.from} → ${pg.to}` : '';
|
|
136
|
+
console.log(`Pending Gate: ${pg.gate} (${pg.gate_type})${fromTo}`);
|
|
137
|
+
console.log(`Action: Run ${chalk.cyan('agentxchain multi approve-gate')} to advance`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Completed state
|
|
141
|
+
if (status.status === 'completed') {
|
|
142
|
+
console.log('');
|
|
143
|
+
console.log(` ${chalk.green.bold('✓ Coordinator run completed')}`);
|
|
144
|
+
if (status.updated_at) {
|
|
145
|
+
console.log(` ${chalk.dim('Completed:')} ${status.updated_at}`);
|
|
146
|
+
}
|
|
113
147
|
}
|
|
114
148
|
|
|
115
149
|
console.log('');
|
|
@@ -119,6 +153,17 @@ export async function multiStatusCommand(options) {
|
|
|
119
153
|
console.log(` ${repoId}: ${info.status || 'unknown'}${phase} (run: ${info.run_id})`);
|
|
120
154
|
}
|
|
121
155
|
|
|
156
|
+
// Phase gate status
|
|
157
|
+
const gateEntries = Object.entries(status.phase_gate_status || {});
|
|
158
|
+
if (gateEntries.length > 0) {
|
|
159
|
+
console.log('');
|
|
160
|
+
console.log('Gates:');
|
|
161
|
+
for (const [gate, gateStatus] of gateEntries) {
|
|
162
|
+
const icon = gateStatus === 'passed' ? chalk.green('✓') : chalk.dim('○');
|
|
163
|
+
console.log(` ${icon} ${gate}: ${gateStatus}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
122
167
|
const barrierEntries = Object.entries(barriers || {});
|
|
123
168
|
if (barrierEntries.length > 0) {
|
|
124
169
|
console.log('');
|
|
@@ -129,6 +174,15 @@ export async function multiStatusCommand(options) {
|
|
|
129
174
|
}
|
|
130
175
|
}
|
|
131
176
|
|
|
177
|
+
function formatCoordinatorStatus(status) {
|
|
178
|
+
switch (status) {
|
|
179
|
+
case 'active': return chalk.green(status);
|
|
180
|
+
case 'blocked': return chalk.red.bold(status);
|
|
181
|
+
case 'completed': return chalk.cyan(status);
|
|
182
|
+
default: return status;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
132
186
|
// ── multi step ─────────────────────────────────────────────────────────────
|
|
133
187
|
|
|
134
188
|
export async function multiStepCommand(options) {
|
|
@@ -401,6 +401,10 @@ export function getCoordinatorStatus(workspacePath) {
|
|
|
401
401
|
repo_runs: state.repo_runs,
|
|
402
402
|
pending_barriers: pendingBarriers,
|
|
403
403
|
pending_gate: state.pending_gate ?? null,
|
|
404
|
+
blocked_reason: state.blocked_reason ?? null,
|
|
405
|
+
created_at: state.created_at ?? null,
|
|
406
|
+
updated_at: state.updated_at ?? null,
|
|
407
|
+
phase_gate_status: state.phase_gate_status ?? null,
|
|
404
408
|
};
|
|
405
409
|
}
|
|
406
410
|
|