agentxchain 2.69.0 → 2.70.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/lib/report.js +177 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentxchain",
3
- "version": "2.69.0",
3
+ "version": "2.70.0",
4
4
  "description": "CLI for AgentXchain — governed multi-agent software delivery",
5
5
  "type": "module",
6
6
  "bin": {
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) {