agentxchain 2.69.0 → 2.71.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentxchain",
3
- "version": "2.69.0",
3
+ "version": "2.71.0",
4
4
  "description": "CLI for AgentXchain — governed multi-agent software delivery",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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
- console.log(`Pending Gate: ${status.pending_gate.gate} (${status.pending_gate.gate_type})`);
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
 
package/src/lib/report.js CHANGED
@@ -20,6 +20,76 @@ function summarizeBlockedOn(blockedOn) {
20
20
  return 'present';
21
21
  }
22
22
 
23
+ function renderGovernanceEventDetailText(lines, evt, indent) {
24
+ switch (evt.type) {
25
+ case 'policy_escalation':
26
+ for (const v of evt.violations || []) {
27
+ lines.push(`${indent}violation: ${v.policy_id || '?'} / ${v.rule || '?'} — ${v.message || 'n/a'}`);
28
+ }
29
+ break;
30
+ case 'conflict_detected':
31
+ if (evt.conflicting_files?.length > 0) {
32
+ lines.push(`${indent}files: ${evt.conflicting_files.join(', ')}`);
33
+ }
34
+ if (evt.overlap_ratio != null) {
35
+ lines.push(`${indent}overlap: ${(evt.overlap_ratio * 100).toFixed(0)}%`);
36
+ }
37
+ break;
38
+ case 'conflict_rejected':
39
+ if (evt.conflicting_files?.length > 0) {
40
+ lines.push(`${indent}files: ${evt.conflicting_files.join(', ')}`);
41
+ }
42
+ break;
43
+ case 'conflict_resolution_selected':
44
+ if (evt.resolution_method) {
45
+ lines.push(`${indent}resolution: ${evt.resolution_method}`);
46
+ }
47
+ break;
48
+ case 'operator_escalated':
49
+ if (evt.reason) lines.push(`${indent}reason: ${evt.reason}`);
50
+ if (evt.blocked_on) lines.push(`${indent}blocked_on: ${evt.blocked_on}`);
51
+ break;
52
+ case 'escalation_resolved':
53
+ if (evt.resolved_via) lines.push(`${indent}resolved via: ${evt.resolved_via}`);
54
+ if (evt.previous_blocked_on) lines.push(`${indent}was blocked on: ${evt.previous_blocked_on}`);
55
+ break;
56
+ }
57
+ }
58
+
59
+ function renderGovernanceEventDetailMarkdown(lines, evt) {
60
+ switch (evt.type) {
61
+ case 'policy_escalation':
62
+ for (const v of evt.violations || []) {
63
+ lines.push(` - Violation: \`${v.policy_id || '?'}\` / \`${v.rule || '?'}\` — ${v.message || 'n/a'}`);
64
+ }
65
+ break;
66
+ case 'conflict_detected':
67
+ if (evt.conflicting_files?.length > 0) {
68
+ lines.push(` - Files: ${evt.conflicting_files.map((f) => `\`${f}\``).join(', ')}`);
69
+ }
70
+ if (evt.overlap_ratio != null) {
71
+ lines.push(` - Overlap: ${(evt.overlap_ratio * 100).toFixed(0)}%`);
72
+ }
73
+ break;
74
+ case 'conflict_rejected':
75
+ if (evt.conflicting_files?.length > 0) {
76
+ lines.push(` - Files: ${evt.conflicting_files.map((f) => `\`${f}\``).join(', ')}`);
77
+ }
78
+ break;
79
+ case 'conflict_resolution_selected':
80
+ if (evt.resolution_method) lines.push(` - Resolution: \`${evt.resolution_method}\``);
81
+ break;
82
+ case 'operator_escalated':
83
+ if (evt.reason) lines.push(` - Reason: ${evt.reason}`);
84
+ if (evt.blocked_on) lines.push(` - Blocked on: \`${evt.blocked_on}\``);
85
+ break;
86
+ case 'escalation_resolved':
87
+ if (evt.resolved_via) lines.push(` - Resolved via: \`${evt.resolved_via}\``);
88
+ if (evt.previous_blocked_on) lines.push(` - Was blocked on: \`${evt.previous_blocked_on}\``);
89
+ break;
90
+ }
91
+ }
92
+
23
93
  function summarizeBlockedState(run) {
24
94
  const blockedReason = run?.blocked_reason;
25
95
  if (blockedReason && typeof blockedReason === 'object' && !Array.isArray(blockedReason)) {
@@ -175,6 +245,60 @@ function extractGateFailureDigest(artifact) {
175
245
  }));
176
246
  }
177
247
 
248
+ const GOVERNANCE_EVENT_TYPES = new Set([
249
+ 'policy_escalation',
250
+ 'conflict_detected',
251
+ 'conflict_rejected',
252
+ 'conflict_resolution_selected',
253
+ 'operator_escalated',
254
+ 'escalation_resolved',
255
+ ]);
256
+
257
+ function extractGovernanceEventDigest(artifact, relPath = '.agentxchain/decision-ledger.jsonl') {
258
+ const data = extractFileData(artifact, relPath);
259
+ if (!Array.isArray(data) || data.length === 0) return [];
260
+ return data
261
+ .filter((d) => typeof d?.decision === 'string' && GOVERNANCE_EVENT_TYPES.has(d.decision))
262
+ .map((d) => {
263
+ const base = {
264
+ type: d.decision,
265
+ timestamp: d.timestamp || null,
266
+ turn_id: d.turn_id || null,
267
+ role: d.role || null,
268
+ phase: d.phase || null,
269
+ };
270
+ switch (d.decision) {
271
+ case 'policy_escalation':
272
+ base.violations = Array.isArray(d.violations) ? d.violations.map((v) => ({
273
+ policy_id: v.policy_id || null,
274
+ rule: v.rule || null,
275
+ message: v.message || null,
276
+ })) : [];
277
+ break;
278
+ case 'conflict_detected':
279
+ base.conflicting_files = Array.isArray(d.conflict?.files) ? d.conflict.files : [];
280
+ base.overlap_ratio = typeof d.conflict?.overlap_ratio === 'number' ? d.conflict.overlap_ratio : null;
281
+ break;
282
+ case 'conflict_rejected':
283
+ base.conflicting_files = Array.isArray(d.conflict?.files) ? d.conflict.files : [];
284
+ break;
285
+ case 'conflict_resolution_selected':
286
+ base.resolution_method = d.conflict?.resolution || null;
287
+ break;
288
+ case 'operator_escalated':
289
+ base.blocked_on = d.blocked_on || null;
290
+ base.reason = d.escalation?.reason || null;
291
+ break;
292
+ case 'escalation_resolved':
293
+ base.resolved_via = d.resolved_via || null;
294
+ base.previous_blocked_on = d.blocked_on || null;
295
+ break;
296
+ }
297
+ return base;
298
+ })
299
+ .sort((a, b) => (a.timestamp || '').localeCompare(b.timestamp || ''));
300
+ }
301
+
178
302
  function extractTimeoutEventDigest(artifact, relPath = '.agentxchain/decision-ledger.jsonl') {
179
303
  const data = extractFileData(artifact, relPath);
180
304
  if (!Array.isArray(data) || data.length === 0) return [];
@@ -658,6 +782,7 @@ function buildRunSubject(artifact) {
658
782
  const intakeLinks = extractIntakeLinks(artifact);
659
783
  const recoverySummary = extractRecoverySummary(artifact);
660
784
  const continuity = extractContinuityMetadata(artifact);
785
+ const governanceEvents = extractGovernanceEventDigest(artifact);
661
786
 
662
787
  return {
663
788
  kind: 'governed_run',
@@ -689,6 +814,7 @@ function buildRunSubject(artifact) {
689
814
  turns,
690
815
  decisions,
691
816
  approval_policy_events: approvalPolicyEvents,
817
+ governance_events: governanceEvents,
692
818
  gate_failures: gateFailures,
693
819
  timeout_events: timeoutEvents,
694
820
  hook_summary: hookSummary,
@@ -764,6 +890,7 @@ function buildCoordinatorSubject(artifact) {
764
890
  base.turns = extractHistoryTimeline(childExport);
765
891
  base.decisions = extractDecisionDigest(childExport);
766
892
  base.approval_policy_events = extractApprovalPolicyDigest(childExport);
893
+ base.governance_events = extractGovernanceEventDigest(childExport);
767
894
  base.gate_failures = extractGateFailureDigest(childExport);
768
895
  base.timeout_events = extractTimeoutEventDigest(childExport);
769
896
  base.hook_summary = extractHookSummary(childExport);
@@ -780,6 +907,7 @@ function buildCoordinatorSubject(artifact) {
780
907
  const barrierLedgerTimeline = extractBarrierLedgerTimeline(artifact);
781
908
  const decisionDigest = extractCoordinatorDecisionDigest(artifact);
782
909
  const coordinatorApprovalPolicyEvents = extractCoordinatorApprovalPolicyDigest(artifact);
910
+ const coordinatorGovernanceEvents = extractGovernanceEventDigest(artifact, '.agentxchain/multirepo/decision-ledger.jsonl');
783
911
  const coordinatorTimeoutEvents = extractTimeoutEventDigest(artifact, '.agentxchain/multirepo/decision-ledger.jsonl');
784
912
  const timing = computeCoordinatorTiming(artifact, coordinatorTimeline);
785
913
  const blockedReason = normalizeCoordinatorBlockedReason(coordinatorState.blocked_reason);
@@ -824,6 +952,7 @@ function buildCoordinatorSubject(artifact) {
824
952
  barrier_ledger_timeline: barrierLedgerTimeline,
825
953
  decision_digest: decisionDigest,
826
954
  approval_policy_events: coordinatorApprovalPolicyEvents,
955
+ governance_events: coordinatorGovernanceEvents,
827
956
  timeout_events: coordinatorTimeoutEvents,
828
957
  recovery_report: extractRecoveryReportSummary(artifact),
829
958
  repos,
@@ -1008,6 +1137,14 @@ export function formatGovernanceReportText(report) {
1008
1137
  }
1009
1138
  }
1010
1139
 
1140
+ if (run.governance_events && run.governance_events.length > 0) {
1141
+ lines.push('', 'Governance Events:');
1142
+ for (const evt of run.governance_events) {
1143
+ lines.push(` - ${evt.type} | ${evt.role || '?'} | ${evt.phase || '?'} | at: ${evt.timestamp || 'n/a'}`);
1144
+ renderGovernanceEventDetailText(lines, evt, ' ');
1145
+ }
1146
+ }
1147
+
1011
1148
  if (run.timeout_events && run.timeout_events.length > 0) {
1012
1149
  lines.push('', 'Timeout Events:');
1013
1150
  for (const evt of run.timeout_events) {
@@ -1082,6 +1219,7 @@ export function formatGovernanceReportText(report) {
1082
1219
  barrier_ledger_timeline,
1083
1220
  decision_digest,
1084
1221
  approval_policy_events,
1222
+ governance_events,
1085
1223
  timeout_events,
1086
1224
  recovery_report,
1087
1225
  } = report.subject;
@@ -1179,6 +1317,14 @@ export function formatGovernanceReportText(report) {
1179
1317
  }
1180
1318
  }
1181
1319
 
1320
+ if (governance_events && governance_events.length > 0) {
1321
+ lines.push('', 'Governance Events:');
1322
+ for (const evt of governance_events) {
1323
+ lines.push(` - ${evt.type} | ${evt.role || '?'} | ${evt.phase || '?'} | at: ${evt.timestamp || 'n/a'}`);
1324
+ renderGovernanceEventDetailText(lines, evt, ' ');
1325
+ }
1326
+ }
1327
+
1182
1328
  if (timeout_events && timeout_events.length > 0) {
1183
1329
  lines.push('', 'Timeout Events:');
1184
1330
  for (const evt of timeout_events) {
@@ -1254,6 +1400,13 @@ export function formatGovernanceReportText(report) {
1254
1400
  repoLines.push(` - ${evt.action || 'unknown'}: ${evt.gate_type || 'unknown'} | ${transition} | ${evt.timestamp || 'n/a'}`);
1255
1401
  }
1256
1402
  }
1403
+ if (repo.governance_events && repo.governance_events.length > 0) {
1404
+ repoLines.push(' Governance Events:');
1405
+ for (const evt of repo.governance_events) {
1406
+ repoLines.push(` - ${evt.type} | ${evt.role || '?'} | ${evt.phase || '?'} | at: ${evt.timestamp || 'n/a'}`);
1407
+ renderGovernanceEventDetailText(repoLines, evt, ' ');
1408
+ }
1409
+ }
1257
1410
  if (repo.timeout_events && repo.timeout_events.length > 0) {
1258
1411
  repoLines.push(' Timeout Events:');
1259
1412
  for (const evt of repo.timeout_events) {
@@ -1417,6 +1570,14 @@ export function formatGovernanceReportMarkdown(report) {
1417
1570
  }
1418
1571
  }
1419
1572
 
1573
+ if (run.governance_events && run.governance_events.length > 0) {
1574
+ lines.push('', '## Governance Events', '');
1575
+ for (const evt of run.governance_events) {
1576
+ lines.push(`- **${evt.type}** (\`${evt.role || '?'}\`, \`${evt.phase || '?'}\` phase) at \`${evt.timestamp || 'n/a'}\``);
1577
+ renderGovernanceEventDetailMarkdown(lines, evt);
1578
+ }
1579
+ }
1580
+
1420
1581
  if (run.timeout_events && run.timeout_events.length > 0) {
1421
1582
  lines.push('', '## Timeout Events', '');
1422
1583
  for (const evt of run.timeout_events) {
@@ -1494,6 +1655,7 @@ export function formatGovernanceReportMarkdown(report) {
1494
1655
  barrier_ledger_timeline,
1495
1656
  decision_digest,
1496
1657
  approval_policy_events,
1658
+ governance_events,
1497
1659
  timeout_events,
1498
1660
  recovery_report: coordRecoveryReport,
1499
1661
  } = report.subject;
@@ -1592,6 +1754,14 @@ export function formatGovernanceReportMarkdown(report) {
1592
1754
  }
1593
1755
  }
1594
1756
 
1757
+ if (governance_events && governance_events.length > 0) {
1758
+ mdLines.push('', '## Governance Events', '');
1759
+ for (const evt of governance_events) {
1760
+ mdLines.push(`- **${evt.type}** (\`${evt.role || '?'}\`, \`${evt.phase || '?'}\` phase) at \`${evt.timestamp || 'n/a'}\``);
1761
+ renderGovernanceEventDetailMarkdown(mdLines, evt);
1762
+ }
1763
+ }
1764
+
1595
1765
  if (timeout_events && timeout_events.length > 0) {
1596
1766
  mdLines.push('', '## Timeout Events', '');
1597
1767
  for (const evt of timeout_events) {
@@ -1670,6 +1840,13 @@ export function formatGovernanceReportMarkdown(report) {
1670
1840
  if (evt.reason) repoLines.push(` - ${evt.reason}`);
1671
1841
  }
1672
1842
  }
1843
+ if (repo.governance_events && repo.governance_events.length > 0) {
1844
+ repoLines.push('', '#### Governance Events', '');
1845
+ for (const evt of repo.governance_events) {
1846
+ repoLines.push(`- **${evt.type}** (\`${evt.role || '?'}\`, \`${evt.phase || '?'}\` phase) at \`${evt.timestamp || 'n/a'}\``);
1847
+ renderGovernanceEventDetailMarkdown(repoLines, evt);
1848
+ }
1849
+ }
1673
1850
  if (repo.timeout_events && repo.timeout_events.length > 0) {
1674
1851
  repoLines.push('', '#### Timeout Events', '');
1675
1852
  for (const evt of repo.timeout_events) {