agentxchain 2.45.0 → 2.46.2
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 +6 -0
- package/dashboard/components/coordinator-timeouts.js +220 -0
- package/dashboard/components/timeouts.js +201 -0
- package/dashboard/index.html +2 -0
- package/package.json +1 -1
- package/scripts/publish-from-tag.sh +33 -4
- package/src/commands/accept-turn.js +30 -0
- package/src/commands/init.js +8 -1
- package/src/commands/migrate.js +1 -0
- package/src/commands/status.js +49 -0
- package/src/lib/approval-policy.js +139 -0
- package/src/lib/blocked-state.js +35 -0
- package/src/lib/dashboard/bridge-server.js +14 -0
- package/src/lib/dashboard/coordinator-timeout-status.js +139 -0
- package/src/lib/dashboard/timeout-status.js +201 -0
- package/src/lib/governed-state.js +530 -25
- package/src/lib/governed-templates.js +2 -0
- package/src/lib/normalized-config.js +132 -0
- package/src/lib/policy-evaluator.js +330 -0
- package/src/lib/reference-conformance-adapter.js +1 -0
- package/src/lib/repo-observer.js +132 -1
- package/src/lib/report.js +323 -6
- package/src/lib/schema.js +47 -0
- package/src/lib/timeout-evaluator.js +234 -0
- package/src/templates/governed/enterprise-app.json +20 -0
package/src/lib/report.js
CHANGED
|
@@ -78,6 +78,10 @@ function extractHistoryTimeline(artifact) {
|
|
|
78
78
|
phase: e.phase || null,
|
|
79
79
|
phase_transition: e.phase_transition_request || null,
|
|
80
80
|
files_changed_count: Array.isArray(e.files_changed) ? e.files_changed.length : 0,
|
|
81
|
+
concurrent_with: Array.isArray(e.concurrent_with) && e.concurrent_with.length > 0 ? e.concurrent_with : undefined,
|
|
82
|
+
sibling_attributed_files: Array.isArray(e.observed_artifact?.attributed_to_concurrent_siblings) && e.observed_artifact.attributed_to_concurrent_siblings.length > 0
|
|
83
|
+
? e.observed_artifact.attributed_to_concurrent_siblings
|
|
84
|
+
: undefined,
|
|
81
85
|
decisions: Array.isArray(e.decisions) ? e.decisions.map((d) => d?.id || d).filter(Boolean) : [],
|
|
82
86
|
objections: Array.isArray(e.objections) ? e.objections.map((o) => o?.id || o).filter(Boolean) : [],
|
|
83
87
|
cost_usd: typeof e.cost?.total_usd === 'number' ? e.cost.total_usd : null,
|
|
@@ -99,6 +103,61 @@ function extractDecisionDigest(artifact) {
|
|
|
99
103
|
}));
|
|
100
104
|
}
|
|
101
105
|
|
|
106
|
+
function extractApprovalPolicyDigest(artifact) {
|
|
107
|
+
const data = extractFileData(artifact, '.agentxchain/decision-ledger.jsonl');
|
|
108
|
+
if (!Array.isArray(data) || data.length === 0) return [];
|
|
109
|
+
return data
|
|
110
|
+
.filter((d) => d?.type === 'approval_policy')
|
|
111
|
+
.map((d) => ({
|
|
112
|
+
gate_type: d.gate_type || null,
|
|
113
|
+
action: d.action || null,
|
|
114
|
+
matched_rule: d.matched_rule || null,
|
|
115
|
+
from_phase: d.from_phase || null,
|
|
116
|
+
to_phase: d.to_phase || null,
|
|
117
|
+
reason: d.reason || '',
|
|
118
|
+
gate_id: d.gate_id || null,
|
|
119
|
+
timestamp: d.timestamp || null,
|
|
120
|
+
}));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function extractGateFailureDigest(artifact) {
|
|
124
|
+
const data = extractFileData(artifact, '.agentxchain/decision-ledger.jsonl');
|
|
125
|
+
if (!Array.isArray(data) || data.length === 0) return [];
|
|
126
|
+
return data
|
|
127
|
+
.filter((d) => d?.type === 'gate_failure')
|
|
128
|
+
.map((d) => ({
|
|
129
|
+
gate_type: d.gate_type || null,
|
|
130
|
+
gate_id: d.gate_id || null,
|
|
131
|
+
phase: d.phase || null,
|
|
132
|
+
from_phase: d.from_phase || null,
|
|
133
|
+
to_phase: d.to_phase || null,
|
|
134
|
+
requested_by_turn: d.requested_by_turn || null,
|
|
135
|
+
failed_at: d.failed_at || null,
|
|
136
|
+
queued_request: d.queued_request === true,
|
|
137
|
+
reasons: Array.isArray(d.reasons) ? d.reasons : [],
|
|
138
|
+
missing_files: Array.isArray(d.missing_files) ? d.missing_files : [],
|
|
139
|
+
missing_verification: d.missing_verification === true,
|
|
140
|
+
}));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function extractTimeoutEventDigest(artifact, relPath = '.agentxchain/decision-ledger.jsonl') {
|
|
144
|
+
const data = extractFileData(artifact, relPath);
|
|
145
|
+
if (!Array.isArray(data) || data.length === 0) return [];
|
|
146
|
+
return data
|
|
147
|
+
.filter((d) => typeof d?.type === 'string' && d.type.startsWith('timeout'))
|
|
148
|
+
.map((d) => ({
|
|
149
|
+
type: d.type,
|
|
150
|
+
scope: d.scope || null,
|
|
151
|
+
phase: d.phase || null,
|
|
152
|
+
turn_id: d.turn_id || null,
|
|
153
|
+
limit_minutes: typeof d.limit_minutes === 'number' ? d.limit_minutes : null,
|
|
154
|
+
elapsed_minutes: typeof d.elapsed_minutes === 'number' ? d.elapsed_minutes : null,
|
|
155
|
+
exceeded_by_minutes: typeof d.exceeded_by_minutes === 'number' ? d.exceeded_by_minutes : null,
|
|
156
|
+
action: d.action || null,
|
|
157
|
+
timestamp: d.timestamp || null,
|
|
158
|
+
}));
|
|
159
|
+
}
|
|
160
|
+
|
|
102
161
|
function extractHookSummary(artifact) {
|
|
103
162
|
const data = extractFileData(artifact, '.agentxchain/hook-audit.jsonl');
|
|
104
163
|
if (!Array.isArray(data) || data.length === 0) return null;
|
|
@@ -408,6 +467,23 @@ function extractCoordinatorDecisionDigest(artifact) {
|
|
|
408
467
|
}));
|
|
409
468
|
}
|
|
410
469
|
|
|
470
|
+
function extractCoordinatorApprovalPolicyDigest(artifact) {
|
|
471
|
+
const data = extractFileData(artifact, '.agentxchain/multirepo/decision-ledger.jsonl');
|
|
472
|
+
if (!Array.isArray(data) || data.length === 0) return [];
|
|
473
|
+
return data
|
|
474
|
+
.filter((d) => d?.type === 'approval_policy')
|
|
475
|
+
.map((d) => ({
|
|
476
|
+
gate_type: d.gate_type || null,
|
|
477
|
+
action: d.action || null,
|
|
478
|
+
matched_rule: d.matched_rule || null,
|
|
479
|
+
from_phase: d.from_phase || null,
|
|
480
|
+
to_phase: d.to_phase || null,
|
|
481
|
+
reason: d.reason || '',
|
|
482
|
+
gate_id: d.gate_id || null,
|
|
483
|
+
timestamp: d.timestamp || null,
|
|
484
|
+
}));
|
|
485
|
+
}
|
|
486
|
+
|
|
411
487
|
function extractBarrierSummary(artifact) {
|
|
412
488
|
const data = extractFileData(artifact, '.agentxchain/multirepo/barriers.json');
|
|
413
489
|
if (!data || typeof data !== 'object' || Array.isArray(data)) return [];
|
|
@@ -538,6 +614,9 @@ function buildRunSubject(artifact) {
|
|
|
538
614
|
|
|
539
615
|
const turns = extractHistoryTimeline(artifact);
|
|
540
616
|
const decisions = extractDecisionDigest(artifact);
|
|
617
|
+
const approvalPolicyEvents = extractApprovalPolicyDigest(artifact);
|
|
618
|
+
const gateFailures = extractGateFailureDigest(artifact);
|
|
619
|
+
const timeoutEvents = extractTimeoutEventDigest(artifact);
|
|
541
620
|
const hookSummary = extractHookSummary(artifact);
|
|
542
621
|
const timing = computeTiming(artifact, turns);
|
|
543
622
|
const gateSummary = extractGateSummary(artifact);
|
|
@@ -571,6 +650,9 @@ function buildRunSubject(artifact) {
|
|
|
571
650
|
duration_seconds: timing.duration_seconds,
|
|
572
651
|
turns,
|
|
573
652
|
decisions,
|
|
653
|
+
approval_policy_events: approvalPolicyEvents,
|
|
654
|
+
gate_failures: gateFailures,
|
|
655
|
+
timeout_events: timeoutEvents,
|
|
574
656
|
hook_summary: hookSummary,
|
|
575
657
|
gate_summary: gateSummary,
|
|
576
658
|
intake_links: intakeLinks,
|
|
@@ -643,6 +725,9 @@ function buildCoordinatorSubject(artifact) {
|
|
|
643
725
|
const childExport = repoEntry.export;
|
|
644
726
|
base.turns = extractHistoryTimeline(childExport);
|
|
645
727
|
base.decisions = extractDecisionDigest(childExport);
|
|
728
|
+
base.approval_policy_events = extractApprovalPolicyDigest(childExport);
|
|
729
|
+
base.gate_failures = extractGateFailureDigest(childExport);
|
|
730
|
+
base.timeout_events = extractTimeoutEventDigest(childExport);
|
|
646
731
|
base.hook_summary = extractHookSummary(childExport);
|
|
647
732
|
base.gate_summary = extractGateSummary(childExport);
|
|
648
733
|
base.recovery_summary = extractRecoverySummary(childExport);
|
|
@@ -656,6 +741,8 @@ function buildCoordinatorSubject(artifact) {
|
|
|
656
741
|
const barrierSummary = extractBarrierSummary(artifact);
|
|
657
742
|
const barrierLedgerTimeline = extractBarrierLedgerTimeline(artifact);
|
|
658
743
|
const decisionDigest = extractCoordinatorDecisionDigest(artifact);
|
|
744
|
+
const coordinatorApprovalPolicyEvents = extractCoordinatorApprovalPolicyDigest(artifact);
|
|
745
|
+
const coordinatorTimeoutEvents = extractTimeoutEventDigest(artifact, '.agentxchain/multirepo/decision-ledger.jsonl');
|
|
659
746
|
const timing = computeCoordinatorTiming(artifact, coordinatorTimeline);
|
|
660
747
|
const blockedReason = normalizeCoordinatorBlockedReason(coordinatorState.blocked_reason);
|
|
661
748
|
const pendingGate = normalizePendingGate(coordinatorState.pending_gate);
|
|
@@ -698,6 +785,8 @@ function buildCoordinatorSubject(artifact) {
|
|
|
698
785
|
barrier_summary: barrierSummary,
|
|
699
786
|
barrier_ledger_timeline: barrierLedgerTimeline,
|
|
700
787
|
decision_digest: decisionDigest,
|
|
788
|
+
approval_policy_events: coordinatorApprovalPolicyEvents,
|
|
789
|
+
timeout_events: coordinatorTimeoutEvents,
|
|
701
790
|
recovery_report: extractRecoveryReportSummary(artifact),
|
|
702
791
|
repos,
|
|
703
792
|
artifacts: {
|
|
@@ -828,7 +917,8 @@ export function formatGovernanceReportText(report) {
|
|
|
828
917
|
const t = run.turns[i];
|
|
829
918
|
const cost = t.cost_usd != null ? formatUsd(t.cost_usd) : 'n/a';
|
|
830
919
|
const phase = t.phase_transition ? `${t.phase || '?'} -> ${t.phase_transition}` : (t.phase || '?');
|
|
831
|
-
|
|
920
|
+
const siblingNote = Array.isArray(t.sibling_attributed_files) ? ` (${t.sibling_attributed_files.length} sibling-attributed)` : '';
|
|
921
|
+
lines.push(` ${i + 1}. [${t.role}] ${t.summary || '(no summary)'} | phase: ${phase} | files: ${t.files_changed_count}${siblingNote} | cost: ${cost} | ${t.accepted_at || 'n/a'}`);
|
|
832
922
|
}
|
|
833
923
|
}
|
|
834
924
|
|
|
@@ -846,6 +936,46 @@ export function formatGovernanceReportText(report) {
|
|
|
846
936
|
}
|
|
847
937
|
}
|
|
848
938
|
|
|
939
|
+
if (run.gate_failures && run.gate_failures.length > 0) {
|
|
940
|
+
lines.push('', 'Gate Failures:');
|
|
941
|
+
for (const failure of run.gate_failures) {
|
|
942
|
+
const request = failure.gate_type === 'run_completion'
|
|
943
|
+
? 'run completion'
|
|
944
|
+
: `${failure.from_phase || failure.phase || '?'} -> ${failure.to_phase || '?'}`;
|
|
945
|
+
const source = failure.queued_request ? 'queued drain' : 'direct';
|
|
946
|
+
lines.push(` - ${failure.gate_id || 'unknown'} | ${failure.gate_type || 'unknown'} | request: ${request} | source: ${source} | at: ${failure.failed_at || 'n/a'}`);
|
|
947
|
+
for (const reason of failure.reasons || []) {
|
|
948
|
+
lines.push(` reason: ${reason}`);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
if (run.approval_policy_events && run.approval_policy_events.length > 0) {
|
|
954
|
+
lines.push('', 'Approval Policy:');
|
|
955
|
+
for (const evt of run.approval_policy_events) {
|
|
956
|
+
const transition = evt.gate_type === 'run_completion'
|
|
957
|
+
? 'run completion'
|
|
958
|
+
: `${evt.from_phase || '?'} -> ${evt.to_phase || '?'}`;
|
|
959
|
+
const rule = evt.matched_rule ? ` | rule: ${typeof evt.matched_rule === 'object' ? JSON.stringify(evt.matched_rule) : evt.matched_rule}` : '';
|
|
960
|
+
lines.push(` - ${evt.action || 'unknown'} | ${evt.gate_type || 'unknown'} | ${transition}${rule} | at: ${evt.timestamp || 'n/a'}`);
|
|
961
|
+
if (evt.reason) lines.push(` reason: ${evt.reason}`);
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
if (run.timeout_events && run.timeout_events.length > 0) {
|
|
966
|
+
lines.push('', 'Timeout Events:');
|
|
967
|
+
for (const evt of run.timeout_events) {
|
|
968
|
+
const label = evt.type === 'timeout_warning' ? 'warning'
|
|
969
|
+
: evt.type === 'timeout_skip' ? 'skip'
|
|
970
|
+
: evt.type === 'timeout_skip_failed' ? 'skip failed'
|
|
971
|
+
: 'escalation';
|
|
972
|
+
const elapsed = evt.elapsed_minutes != null ? `${evt.elapsed_minutes}m` : '?';
|
|
973
|
+
const limit = evt.limit_minutes != null ? `${evt.limit_minutes}m` : '?';
|
|
974
|
+
const exceeded = evt.exceeded_by_minutes != null ? `+${evt.exceeded_by_minutes}m` : '';
|
|
975
|
+
lines.push(` - ${label} | ${evt.scope || '?'} scope | ${elapsed}/${limit}${exceeded ? ` (${exceeded})` : ''} | action: ${evt.action || 'n/a'} | phase: ${evt.phase || 'n/a'} | at: ${evt.timestamp || 'n/a'}`);
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
|
|
849
979
|
if (run.intake_links && run.intake_links.length > 0) {
|
|
850
980
|
lines.push('', 'Intake Linkage:');
|
|
851
981
|
for (const intake of run.intake_links) {
|
|
@@ -896,7 +1026,19 @@ export function formatGovernanceReportText(report) {
|
|
|
896
1026
|
return lines.join('\n');
|
|
897
1027
|
}
|
|
898
1028
|
|
|
899
|
-
const {
|
|
1029
|
+
const {
|
|
1030
|
+
coordinator,
|
|
1031
|
+
run,
|
|
1032
|
+
artifacts,
|
|
1033
|
+
repos,
|
|
1034
|
+
coordinator_timeline,
|
|
1035
|
+
barrier_summary,
|
|
1036
|
+
barrier_ledger_timeline,
|
|
1037
|
+
decision_digest,
|
|
1038
|
+
approval_policy_events,
|
|
1039
|
+
timeout_events,
|
|
1040
|
+
recovery_report,
|
|
1041
|
+
} = report.subject;
|
|
900
1042
|
const lines = [
|
|
901
1043
|
'AgentXchain Governance Report',
|
|
902
1044
|
`Input: ${report.input}`,
|
|
@@ -979,6 +1121,32 @@ export function formatGovernanceReportText(report) {
|
|
|
979
1121
|
}
|
|
980
1122
|
}
|
|
981
1123
|
|
|
1124
|
+
if (approval_policy_events && approval_policy_events.length > 0) {
|
|
1125
|
+
lines.push('', 'Approval Policy:');
|
|
1126
|
+
for (const evt of approval_policy_events) {
|
|
1127
|
+
const transition = evt.gate_type === 'run_completion'
|
|
1128
|
+
? 'run completion'
|
|
1129
|
+
: `${evt.from_phase || '?'} -> ${evt.to_phase || '?'}`;
|
|
1130
|
+
const rule = evt.matched_rule ? ` | rule: ${typeof evt.matched_rule === 'object' ? JSON.stringify(evt.matched_rule) : evt.matched_rule}` : '';
|
|
1131
|
+
lines.push(` - ${evt.action || 'unknown'} | ${evt.gate_type || 'unknown'} | ${transition}${rule} | at: ${evt.timestamp || 'n/a'}`);
|
|
1132
|
+
if (evt.reason) lines.push(` reason: ${evt.reason}`);
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
if (timeout_events && timeout_events.length > 0) {
|
|
1137
|
+
lines.push('', 'Timeout Events:');
|
|
1138
|
+
for (const evt of timeout_events) {
|
|
1139
|
+
const label = evt.type === 'timeout_warning' ? 'warning'
|
|
1140
|
+
: evt.type === 'timeout_skip' ? 'skip'
|
|
1141
|
+
: evt.type === 'timeout_skip_failed' ? 'skip failed'
|
|
1142
|
+
: 'escalation';
|
|
1143
|
+
const elapsed = evt.elapsed_minutes != null ? `${evt.elapsed_minutes}m` : '?';
|
|
1144
|
+
const limit = evt.limit_minutes != null ? `${evt.limit_minutes}m` : '?';
|
|
1145
|
+
const exceeded = evt.exceeded_by_minutes != null ? `+${evt.exceeded_by_minutes}m` : '';
|
|
1146
|
+
lines.push(` - ${label} | ${evt.scope || '?'} scope | ${elapsed}/${limit}${exceeded ? ` (${exceeded})` : ''} | action: ${evt.action || 'n/a'} | phase: ${evt.phase || 'n/a'} | at: ${evt.timestamp || 'n/a'}`);
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
|
|
982
1150
|
if (recovery_report) {
|
|
983
1151
|
lines.push('', 'Recovery Report:');
|
|
984
1152
|
lines.push(` Trigger: ${recovery_report.trigger || 'n/a'}`);
|
|
@@ -1003,7 +1171,8 @@ export function formatGovernanceReportText(report) {
|
|
|
1003
1171
|
const t = repo.turns[i];
|
|
1004
1172
|
const cost = t.cost_usd != null ? formatUsd(t.cost_usd) : 'n/a';
|
|
1005
1173
|
const phase = t.phase_transition ? `${t.phase || '?'} -> ${t.phase_transition}` : (t.phase || '?');
|
|
1006
|
-
|
|
1174
|
+
const siblingNote = Array.isArray(t.sibling_attributed_files) ? ` (${t.sibling_attributed_files.length} sibling-attributed)` : '';
|
|
1175
|
+
repoLines.push(` ${i + 1}. [${t.role}] ${t.summary || '(no summary)'} | phase: ${phase} | files: ${t.files_changed_count}${siblingNote} | cost: ${cost} | ${t.accepted_at || 'n/a'}`);
|
|
1007
1176
|
}
|
|
1008
1177
|
}
|
|
1009
1178
|
if (repo.decisions && repo.decisions.length > 0) {
|
|
@@ -1018,6 +1187,39 @@ export function formatGovernanceReportText(report) {
|
|
|
1018
1187
|
repoLines.push(` - ${gate.gate_id}: ${gate.status}`);
|
|
1019
1188
|
}
|
|
1020
1189
|
}
|
|
1190
|
+
if (repo.gate_failures && repo.gate_failures.length > 0) {
|
|
1191
|
+
repoLines.push(' Gate Failures:');
|
|
1192
|
+
for (const failure of repo.gate_failures) {
|
|
1193
|
+
const request = failure.gate_type === 'run_completion'
|
|
1194
|
+
? 'run completion'
|
|
1195
|
+
: `${failure.from_phase || failure.phase || '?'} -> ${failure.to_phase || '?'}`;
|
|
1196
|
+
repoLines.push(` - ${failure.gate_id || 'unknown'}: ${failure.gate_type || 'unknown'} | ${request} | ${failure.queued_request ? 'queued drain' : 'direct'} | ${failure.failed_at || 'n/a'}`);
|
|
1197
|
+
for (const reason of failure.reasons || []) {
|
|
1198
|
+
repoLines.push(` reason: ${reason}`);
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
if (repo.approval_policy_events && repo.approval_policy_events.length > 0) {
|
|
1203
|
+
repoLines.push(' Approval Policy:');
|
|
1204
|
+
for (const evt of repo.approval_policy_events) {
|
|
1205
|
+
const transition = evt.gate_type === 'run_completion'
|
|
1206
|
+
? 'run completion'
|
|
1207
|
+
: `${evt.from_phase || '?'} -> ${evt.to_phase || '?'}`;
|
|
1208
|
+
repoLines.push(` - ${evt.action || 'unknown'}: ${evt.gate_type || 'unknown'} | ${transition} | ${evt.timestamp || 'n/a'}`);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
if (repo.timeout_events && repo.timeout_events.length > 0) {
|
|
1212
|
+
repoLines.push(' Timeout Events:');
|
|
1213
|
+
for (const evt of repo.timeout_events) {
|
|
1214
|
+
const label = evt.type === 'timeout_warning' ? 'warning'
|
|
1215
|
+
: evt.type === 'timeout_skip' ? 'skip'
|
|
1216
|
+
: evt.type === 'timeout_skip_failed' ? 'skip failed'
|
|
1217
|
+
: 'escalation';
|
|
1218
|
+
const elapsed = evt.elapsed_minutes != null ? `${evt.elapsed_minutes}m` : '?';
|
|
1219
|
+
const limit = evt.limit_minutes != null ? `${evt.limit_minutes}m` : '?';
|
|
1220
|
+
repoLines.push(` - ${label}: ${evt.scope || '?'} scope | ${elapsed}/${limit} | action: ${evt.action || 'n/a'} | ${evt.timestamp || 'n/a'}`);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1021
1223
|
if (repo.hook_summary) {
|
|
1022
1224
|
repoLines.push(` Hook Activity: ${repo.hook_summary.total} total, ${repo.hook_summary.blocked} blocked`);
|
|
1023
1225
|
}
|
|
@@ -1117,7 +1319,8 @@ export function formatGovernanceReportMarkdown(report) {
|
|
|
1117
1319
|
const cost = t.cost_usd != null ? formatUsd(t.cost_usd) : 'n/a';
|
|
1118
1320
|
const phase = t.phase_transition ? `${t.phase || '?'} → ${t.phase_transition}` : (t.phase || '?');
|
|
1119
1321
|
const summary = (t.summary || '(no summary)').replace(/\|/g, '\\|');
|
|
1120
|
-
|
|
1322
|
+
const siblingNote = Array.isArray(t.sibling_attributed_files) ? ` (${t.sibling_attributed_files.length} sibling)` : '';
|
|
1323
|
+
lines.push(`| ${i + 1} | ${t.role} | ${phase} | ${summary} | ${t.files_changed_count}${siblingNote} | ${cost} | ${t.accepted_at || 'n/a'} |`);
|
|
1121
1324
|
}
|
|
1122
1325
|
}
|
|
1123
1326
|
|
|
@@ -1135,6 +1338,45 @@ export function formatGovernanceReportMarkdown(report) {
|
|
|
1135
1338
|
}
|
|
1136
1339
|
}
|
|
1137
1340
|
|
|
1341
|
+
if (run.gate_failures && run.gate_failures.length > 0) {
|
|
1342
|
+
lines.push('', '## Gate Failures', '');
|
|
1343
|
+
for (const failure of run.gate_failures) {
|
|
1344
|
+
const request = failure.gate_type === 'run_completion'
|
|
1345
|
+
? 'run completion'
|
|
1346
|
+
: `${failure.from_phase || failure.phase || '?'} → ${failure.to_phase || '?'}`;
|
|
1347
|
+
lines.push(`- \`${failure.gate_id || 'unknown'}\` (${failure.gate_type || 'unknown'}) at \`${failure.failed_at || 'n/a'}\` via ${failure.queued_request ? 'queued drain' : 'direct'} request: ${request}`);
|
|
1348
|
+
for (const reason of failure.reasons || []) {
|
|
1349
|
+
lines.push(` - ${reason}`);
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
if (run.approval_policy_events && run.approval_policy_events.length > 0) {
|
|
1355
|
+
lines.push('', '## Approval Policy', '');
|
|
1356
|
+
for (const evt of run.approval_policy_events) {
|
|
1357
|
+
const transition = evt.gate_type === 'run_completion'
|
|
1358
|
+
? 'run completion'
|
|
1359
|
+
: `${evt.from_phase || '?'} → ${evt.to_phase || '?'}`;
|
|
1360
|
+
const rule = evt.matched_rule ? ` — rule: \`${typeof evt.matched_rule === 'object' ? JSON.stringify(evt.matched_rule) : evt.matched_rule}\`` : '';
|
|
1361
|
+
lines.push(`- **${evt.action || 'unknown'}** (${evt.gate_type || 'unknown'}) ${transition}${rule} at \`${evt.timestamp || 'n/a'}\``);
|
|
1362
|
+
if (evt.reason) lines.push(` - ${evt.reason}`);
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
if (run.timeout_events && run.timeout_events.length > 0) {
|
|
1367
|
+
lines.push('', '## Timeout Events', '');
|
|
1368
|
+
for (const evt of run.timeout_events) {
|
|
1369
|
+
const label = evt.type === 'timeout_warning' ? 'Warning'
|
|
1370
|
+
: evt.type === 'timeout_skip' ? 'Skip'
|
|
1371
|
+
: evt.type === 'timeout_skip_failed' ? 'Skip Failed'
|
|
1372
|
+
: 'Escalation';
|
|
1373
|
+
const elapsed = evt.elapsed_minutes != null ? `${evt.elapsed_minutes}m` : '?';
|
|
1374
|
+
const limit = evt.limit_minutes != null ? `${evt.limit_minutes}m` : '?';
|
|
1375
|
+
const exceeded = evt.exceeded_by_minutes != null ? ` (+${evt.exceeded_by_minutes}m)` : '';
|
|
1376
|
+
lines.push(`- **${label}** (\`${evt.scope || '?'}\` scope) — ${elapsed}/${limit}${exceeded}, action: \`${evt.action || 'n/a'}\`, phase: \`${evt.phase || 'n/a'}\` at \`${evt.timestamp || 'n/a'}\``);
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1138
1380
|
if (run.intake_links && run.intake_links.length > 0) {
|
|
1139
1381
|
lines.push('', '## Intake Linkage', '');
|
|
1140
1382
|
for (const intake of run.intake_links) {
|
|
@@ -1188,7 +1430,19 @@ export function formatGovernanceReportMarkdown(report) {
|
|
|
1188
1430
|
return lines.join('\n');
|
|
1189
1431
|
}
|
|
1190
1432
|
|
|
1191
|
-
const {
|
|
1433
|
+
const {
|
|
1434
|
+
coordinator,
|
|
1435
|
+
run,
|
|
1436
|
+
artifacts,
|
|
1437
|
+
repos,
|
|
1438
|
+
coordinator_timeline,
|
|
1439
|
+
barrier_summary,
|
|
1440
|
+
barrier_ledger_timeline,
|
|
1441
|
+
decision_digest,
|
|
1442
|
+
approval_policy_events,
|
|
1443
|
+
timeout_events,
|
|
1444
|
+
recovery_report: coordRecoveryReport,
|
|
1445
|
+
} = report.subject;
|
|
1192
1446
|
const mdLines = [
|
|
1193
1447
|
'# AgentXchain Governance Report',
|
|
1194
1448
|
'',
|
|
@@ -1272,6 +1526,32 @@ export function formatGovernanceReportMarkdown(report) {
|
|
|
1272
1526
|
}
|
|
1273
1527
|
}
|
|
1274
1528
|
|
|
1529
|
+
if (approval_policy_events && approval_policy_events.length > 0) {
|
|
1530
|
+
mdLines.push('', '## Approval Policy', '');
|
|
1531
|
+
for (const evt of approval_policy_events) {
|
|
1532
|
+
const transition = evt.gate_type === 'run_completion'
|
|
1533
|
+
? 'run completion'
|
|
1534
|
+
: `${evt.from_phase || '?'} → ${evt.to_phase || '?'}`;
|
|
1535
|
+
const rule = evt.matched_rule ? ` — rule: \`${typeof evt.matched_rule === 'object' ? JSON.stringify(evt.matched_rule) : evt.matched_rule}\`` : '';
|
|
1536
|
+
mdLines.push(`- **${evt.action || 'unknown'}** (${evt.gate_type || 'unknown'}) ${transition}${rule} at \`${evt.timestamp || 'n/a'}\``);
|
|
1537
|
+
if (evt.reason) mdLines.push(` - ${evt.reason}`);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
if (timeout_events && timeout_events.length > 0) {
|
|
1542
|
+
mdLines.push('', '## Timeout Events', '');
|
|
1543
|
+
for (const evt of timeout_events) {
|
|
1544
|
+
const label = evt.type === 'timeout_warning' ? 'Warning'
|
|
1545
|
+
: evt.type === 'timeout_skip' ? 'Skip'
|
|
1546
|
+
: evt.type === 'timeout_skip_failed' ? 'Skip Failed'
|
|
1547
|
+
: 'Escalation';
|
|
1548
|
+
const elapsed = evt.elapsed_minutes != null ? `${evt.elapsed_minutes}m` : '?';
|
|
1549
|
+
const limit = evt.limit_minutes != null ? `${evt.limit_minutes}m` : '?';
|
|
1550
|
+
const exceeded = evt.exceeded_by_minutes != null ? ` (+${evt.exceeded_by_minutes}m)` : '';
|
|
1551
|
+
mdLines.push(`- **${label}** (\`${evt.scope || '?'}\` scope) — ${elapsed}/${limit}${exceeded}, action: \`${evt.action || 'n/a'}\`, phase: \`${evt.phase || 'n/a'}\` at \`${evt.timestamp || 'n/a'}\``);
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1275
1555
|
if (coordRecoveryReport) {
|
|
1276
1556
|
mdLines.push('', '## Recovery Report', '');
|
|
1277
1557
|
mdLines.push(`- **Trigger:** ${coordRecoveryReport.trigger || 'n/a'}`);
|
|
@@ -1297,7 +1577,8 @@ export function formatGovernanceReportMarkdown(report) {
|
|
|
1297
1577
|
const cost = t.cost_usd != null ? formatUsd(t.cost_usd) : 'n/a';
|
|
1298
1578
|
const phase = t.phase_transition ? `${t.phase || '?'} → ${t.phase_transition}` : (t.phase || '?');
|
|
1299
1579
|
const summary = (t.summary || '(no summary)').replace(/\|/g, '\\|');
|
|
1300
|
-
|
|
1580
|
+
const siblingNote = Array.isArray(t.sibling_attributed_files) ? ` (${t.sibling_attributed_files.length} sibling)` : '';
|
|
1581
|
+
repoLines.push(`| ${i + 1} | ${t.role} | ${phase} | ${summary} | ${t.files_changed_count}${siblingNote} | ${cost} | ${t.accepted_at || 'n/a'} |`);
|
|
1301
1582
|
}
|
|
1302
1583
|
}
|
|
1303
1584
|
if (repo.decisions && repo.decisions.length > 0) {
|
|
@@ -1312,6 +1593,42 @@ export function formatGovernanceReportMarkdown(report) {
|
|
|
1312
1593
|
repoLines.push(`- \`${gate.gate_id}\`: \`${gate.status}\``);
|
|
1313
1594
|
}
|
|
1314
1595
|
}
|
|
1596
|
+
if (repo.gate_failures && repo.gate_failures.length > 0) {
|
|
1597
|
+
repoLines.push('', '#### Gate Failures', '');
|
|
1598
|
+
for (const failure of repo.gate_failures) {
|
|
1599
|
+
const request = failure.gate_type === 'run_completion'
|
|
1600
|
+
? 'run completion'
|
|
1601
|
+
: `${failure.from_phase || failure.phase || '?'} → ${failure.to_phase || '?'}`;
|
|
1602
|
+
repoLines.push(`- \`${failure.gate_id || 'unknown'}\` (${failure.gate_type || 'unknown'}) at \`${failure.failed_at || 'n/a'}\` via ${failure.queued_request ? 'queued drain' : 'direct'} request: ${request}`);
|
|
1603
|
+
for (const reason of failure.reasons || []) {
|
|
1604
|
+
repoLines.push(` - ${reason}`);
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
if (repo.approval_policy_events && repo.approval_policy_events.length > 0) {
|
|
1609
|
+
repoLines.push('', '#### Approval Policy', '');
|
|
1610
|
+
for (const evt of repo.approval_policy_events) {
|
|
1611
|
+
const transition = evt.gate_type === 'run_completion'
|
|
1612
|
+
? 'run completion'
|
|
1613
|
+
: `${evt.from_phase || '?'} → ${evt.to_phase || '?'}`;
|
|
1614
|
+
const rule = evt.matched_rule ? ` — rule: \`${typeof evt.matched_rule === 'object' ? JSON.stringify(evt.matched_rule) : evt.matched_rule}\`` : '';
|
|
1615
|
+
repoLines.push(`- **${evt.action || 'unknown'}** (${evt.gate_type || 'unknown'}) ${transition}${rule} at \`${evt.timestamp || 'n/a'}\``);
|
|
1616
|
+
if (evt.reason) repoLines.push(` - ${evt.reason}`);
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
if (repo.timeout_events && repo.timeout_events.length > 0) {
|
|
1620
|
+
repoLines.push('', '#### Timeout Events', '');
|
|
1621
|
+
for (const evt of repo.timeout_events) {
|
|
1622
|
+
const label = evt.type === 'timeout_warning' ? 'Warning'
|
|
1623
|
+
: evt.type === 'timeout_skip' ? 'Skip'
|
|
1624
|
+
: evt.type === 'timeout_skip_failed' ? 'Skip Failed'
|
|
1625
|
+
: 'Escalation';
|
|
1626
|
+
const elapsed = evt.elapsed_minutes != null ? `${evt.elapsed_minutes}m` : '?';
|
|
1627
|
+
const limit = evt.limit_minutes != null ? `${evt.limit_minutes}m` : '?';
|
|
1628
|
+
const exceeded = evt.exceeded_by_minutes != null ? ` (+${evt.exceeded_by_minutes}m)` : '';
|
|
1629
|
+
repoLines.push(`- **${label}** (\`${evt.scope || '?'}\` scope) — ${elapsed}/${limit}${exceeded}, action: \`${evt.action || 'n/a'}\`, phase: \`${evt.phase || 'n/a'}\` at \`${evt.timestamp || 'n/a'}\``);
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1315
1632
|
if (repo.hook_summary) {
|
|
1316
1633
|
repoLines.push('', '#### Hook Activity', '', `- Total: ${repo.hook_summary.total}`, `- Blocked: ${repo.hook_summary.blocked}`);
|
|
1317
1634
|
const eventList = Object.entries(repo.hook_summary.events).sort(([a], [b]) => a.localeCompare(b, 'en')).map(([e, c]) => `${e}(${c})`).join(', ');
|
package/src/lib/schema.js
CHANGED
|
@@ -74,6 +74,12 @@ export function validateGovernedStateSchema(data) {
|
|
|
74
74
|
errors.push(`status must be one of: ${VALID_RUN_STATUSES.join(', ')}`);
|
|
75
75
|
}
|
|
76
76
|
if (typeof data.phase !== 'string' || !data.phase.trim()) errors.push('phase must be a non-empty string');
|
|
77
|
+
if ('created_at' in data && data.created_at !== null && (typeof data.created_at !== 'string' || !data.created_at.trim())) {
|
|
78
|
+
errors.push('created_at must be a non-empty string or null');
|
|
79
|
+
}
|
|
80
|
+
if ('phase_entered_at' in data && data.phase_entered_at !== null && (typeof data.phase_entered_at !== 'string' || !data.phase_entered_at.trim())) {
|
|
81
|
+
errors.push('phase_entered_at must be a non-empty string or null');
|
|
82
|
+
}
|
|
77
83
|
|
|
78
84
|
if (isV1_1) {
|
|
79
85
|
if (hasLegacyCurrentTurn) {
|
|
@@ -96,6 +102,47 @@ export function validateGovernedStateSchema(data) {
|
|
|
96
102
|
if ('phase_gate_status' in data && data.phase_gate_status !== null && typeof data.phase_gate_status !== 'object') {
|
|
97
103
|
errors.push('phase_gate_status must be an object');
|
|
98
104
|
}
|
|
105
|
+
if ('last_gate_failure' in data && data.last_gate_failure !== null) {
|
|
106
|
+
if (typeof data.last_gate_failure !== 'object' || Array.isArray(data.last_gate_failure)) {
|
|
107
|
+
errors.push('last_gate_failure must be an object or null');
|
|
108
|
+
} else {
|
|
109
|
+
const failure = data.last_gate_failure;
|
|
110
|
+
const validGateTypes = ['phase_transition', 'run_completion'];
|
|
111
|
+
if (typeof failure.gate_type !== 'string' || !validGateTypes.includes(failure.gate_type)) {
|
|
112
|
+
errors.push(`last_gate_failure.gate_type must be one of: ${validGateTypes.join(', ')}`);
|
|
113
|
+
}
|
|
114
|
+
if (failure.gate_id !== null && failure.gate_id !== undefined && typeof failure.gate_id !== 'string') {
|
|
115
|
+
errors.push('last_gate_failure.gate_id must be a string or null');
|
|
116
|
+
}
|
|
117
|
+
if (typeof failure.phase !== 'string' || !failure.phase.trim()) {
|
|
118
|
+
errors.push('last_gate_failure.phase must be a non-empty string');
|
|
119
|
+
}
|
|
120
|
+
if (failure.from_phase !== null && failure.from_phase !== undefined && typeof failure.from_phase !== 'string') {
|
|
121
|
+
errors.push('last_gate_failure.from_phase must be a string or null');
|
|
122
|
+
}
|
|
123
|
+
if (failure.to_phase !== null && failure.to_phase !== undefined && typeof failure.to_phase !== 'string') {
|
|
124
|
+
errors.push('last_gate_failure.to_phase must be a string or null');
|
|
125
|
+
}
|
|
126
|
+
if (failure.requested_by_turn !== null && failure.requested_by_turn !== undefined && typeof failure.requested_by_turn !== 'string') {
|
|
127
|
+
errors.push('last_gate_failure.requested_by_turn must be a string or null');
|
|
128
|
+
}
|
|
129
|
+
if (typeof failure.failed_at !== 'string' || !failure.failed_at.trim()) {
|
|
130
|
+
errors.push('last_gate_failure.failed_at must be a non-empty string');
|
|
131
|
+
}
|
|
132
|
+
if (typeof failure.queued_request !== 'boolean') {
|
|
133
|
+
errors.push('last_gate_failure.queued_request must be a boolean');
|
|
134
|
+
}
|
|
135
|
+
if (!Array.isArray(failure.reasons) || failure.reasons.some((reason) => typeof reason !== 'string')) {
|
|
136
|
+
errors.push('last_gate_failure.reasons must be an array of strings');
|
|
137
|
+
}
|
|
138
|
+
if (!Array.isArray(failure.missing_files) || failure.missing_files.some((file) => typeof file !== 'string')) {
|
|
139
|
+
errors.push('last_gate_failure.missing_files must be an array of strings');
|
|
140
|
+
}
|
|
141
|
+
if (typeof failure.missing_verification !== 'boolean') {
|
|
142
|
+
errors.push('last_gate_failure.missing_verification must be a boolean');
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
99
146
|
if ('budget_status' in data && data.budget_status !== null && typeof data.budget_status !== 'object') {
|
|
100
147
|
errors.push('budget_status must be an object');
|
|
101
148
|
}
|