agentxchain 2.71.0 → 2.73.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 CHANGED
@@ -20,8 +20,8 @@ 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 },
24
- hooks: { fetch: ['audit', 'annotations'], render: renderHooks },
23
+ ledger: { fetch: ['state', 'ledger', 'coordinatorState', 'coordinatorLedger'], render: renderLedger },
24
+ hooks: { fetch: ['audit', 'annotations', 'coordinatorAudit', 'coordinatorAnnotations'], render: renderHooks },
25
25
  blocked: { fetch: ['state', 'audit', 'coordinatorState', 'coordinatorAudit'], render: renderBlocked },
26
26
  gate: { fetch: ['state', 'history', 'coordinatorState', 'coordinatorHistory', 'coordinatorBarriers'], render: renderGate },
27
27
  initiative: { fetch: ['coordinatorState', 'coordinatorBarriers', 'barrierLedger', 'coordinatorBlockers'], render: renderInitiative },
@@ -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',
@@ -45,6 +46,7 @@ const API_MAP = {
45
46
  coordinatorBarriers: '/api/coordinator/barriers',
46
47
  barrierLedger: '/api/coordinator/barrier-ledger',
47
48
  coordinatorAudit: '/api/coordinator/hooks/audit',
49
+ coordinatorAnnotations: '/api/coordinator/hooks/annotations',
48
50
  coordinatorBlockers: '/api/coordinator/blockers',
49
51
  workflowKitArtifacts: '/api/workflow-kit-artifacts',
50
52
  connectors: '/api/connectors',
@@ -2,6 +2,7 @@
2
2
  * Hook Audit view — renders hook-audit.jsonl entries.
3
3
  *
4
4
  * Pure render function: takes data, returns HTML string. Testable in Node.js.
5
+ * Supports both repo-local and coordinator hook audit/annotation data.
5
6
  */
6
7
 
7
8
  function esc(str) {
@@ -90,26 +91,82 @@ function collectHookNames(audit) {
90
91
  return Array.from(unique).sort();
91
92
  }
92
93
 
93
- export function render({ audit, annotations, filter = {} }) {
94
+ function renderAuditTable(entries, filter) {
95
+ const filtered = filterAudit(entries, filter);
96
+ let html = `<p class="section-subtitle">${filtered.length} of ${entries.length} hook execution${entries.length !== 1 ? 's' : ''}</p>
97
+ <table class="data-table">
98
+ <thead><tr><th>Time</th><th>Phase</th><th>Hook</th><th>Verdict</th><th>Action</th><th>Duration</th></tr></thead>
99
+ <tbody>`;
100
+
101
+ for (const entry of filtered) {
102
+ const duration = entry.duration_ms != null ? `${entry.duration_ms}ms` : '-';
103
+ const action = entry.orchestrator_action || entry.action || 'continued';
104
+ html += `<tr>
105
+ <td class="mono">${esc(entry.timestamp || '-')}</td>
106
+ <td class="mono">${esc(getHookPhase(entry))}</td>
107
+ <td>${esc(getHookName(entry))}</td>
108
+ <td>${verdictBadge(entry.verdict)}</td>
109
+ <td class="mono">${esc(action)}</td>
110
+ <td class="mono">${esc(duration)}</td>
111
+ </tr>`;
112
+ }
113
+
114
+ html += `</tbody></table>`;
115
+ return html;
116
+ }
117
+
118
+ function renderAnnotationList(entries) {
119
+ let html = `<p class="section-subtitle">${entries.length} annotation${entries.length !== 1 ? 's' : ''}</p>
120
+ <div class="annotation-list">`;
121
+
122
+ for (const entry of entries) {
123
+ const annotationText = formatAnnotations(entry);
124
+ html += `<div class="annotation-card">
125
+ <span class="mono">${esc(entry.turn_id || '-')}</span>
126
+ <span class="mono">${esc(getHookName(entry))}</span>
127
+ <span>${esc(annotationText || JSON.stringify(entry))}</span>
128
+ </div>`;
129
+ }
130
+
131
+ html += `</div>`;
132
+ return html;
133
+ }
134
+
135
+ export function render({
136
+ audit,
137
+ annotations,
138
+ coordinatorAudit = null,
139
+ coordinatorAnnotations = null,
140
+ filter = {},
141
+ }) {
94
142
  const hasAudit = Array.isArray(audit) && audit.length > 0;
95
143
  const hasAnnotations = Array.isArray(annotations) && annotations.length > 0;
144
+ const hasCoordinatorAudit = Array.isArray(coordinatorAudit) && coordinatorAudit.length > 0;
145
+ const hasCoordinatorAnnotations = Array.isArray(coordinatorAnnotations) && coordinatorAnnotations.length > 0;
146
+ const hasAnyData = hasAudit || hasAnnotations || hasCoordinatorAudit || hasCoordinatorAnnotations;
147
+ const hasCoordinatorData = hasCoordinatorAudit || hasCoordinatorAnnotations;
96
148
 
97
- if (!hasAudit && !hasAnnotations) {
149
+ if (!hasAnyData) {
98
150
  return `<div class="placeholder"><h2>Hook Audit</h2><p>No hook activity recorded.</p></div>`;
99
151
  }
100
152
 
153
+ // Build combined audit for shared filter bar
154
+ const combinedAudit = [
155
+ ...(Array.isArray(audit) ? audit : []),
156
+ ...(Array.isArray(coordinatorAudit) ? coordinatorAudit : []),
157
+ ];
158
+ const phases = collectHookPhases(combinedAudit);
159
+ const hookNames = collectHookNames(combinedAudit);
160
+ const selectedPhase = filter.phase || 'all';
161
+ const selectedVerdict = filter.verdict || 'all';
162
+ const selectedHookName = filter.hookName || 'all';
163
+
101
164
  let html = `<div class="hooks-view">`;
102
165
 
103
- if (hasAudit) {
104
- const filtered = filterAudit(audit, filter);
105
- const phases = collectHookPhases(audit);
106
- const hookNames = collectHookNames(audit);
107
- const selectedPhase = filter.phase || 'all';
108
- const selectedVerdict = filter.verdict || 'all';
109
- const selectedHookName = filter.hookName || 'all';
110
-
111
- html += `<div class="section"><h3>Hook Audit Log</h3>
112
- <p class="section-subtitle">${filtered.length} of ${audit.length} hook execution${audit.length !== 1 ? 's' : ''}</p>
166
+ // Shared filter bar (covers both repo-local and coordinator data)
167
+ if (hasAudit || hasCoordinatorAudit) {
168
+ html += `<div class="section"><h3>Hook Audit</h3>
169
+ <p class="section-subtitle">${hasCoordinatorData ? 'Repo-local and coordinator hook audit surfaces' : 'Hook audit surface'}</p>
113
170
  <div class="filter-bar">
114
171
  <label class="filter-control">
115
172
  <span>Phase</span>
@@ -135,41 +192,37 @@ export function render({ audit, annotations, filter = {} }) {
135
192
  </select>
136
193
  </label>
137
194
  </div>
138
- <table class="data-table">
139
- <thead><tr><th>Time</th><th>Phase</th><th>Hook</th><th>Verdict</th><th>Action</th><th>Duration</th></tr></thead>
140
- <tbody>`;
141
-
142
- for (const entry of filtered) {
143
- const duration = entry.duration_ms != null ? `${entry.duration_ms}ms` : '-';
144
- const action = entry.orchestrator_action || entry.action || 'continued';
145
- html += `<tr>
146
- <td class="mono">${esc(entry.timestamp || '-')}</td>
147
- <td class="mono">${esc(getHookPhase(entry))}</td>
148
- <td>${esc(getHookName(entry))}</td>
149
- <td>${verdictBadge(entry.verdict)}</td>
150
- <td class="mono">${esc(action)}</td>
151
- <td class="mono">${esc(duration)}</td>
152
- </tr>`;
153
- }
195
+ </div>`;
196
+ }
154
197
 
155
- html += `</tbody></table></div>`;
198
+ // Repo-local audit section
199
+ if (hasAudit) {
200
+ const sectionTitle = hasCoordinatorData ? 'Repo Hook Audit Log' : 'Hook Audit Log';
201
+ html += `<div class="section"><h3>${sectionTitle}</h3>`;
202
+ html += renderAuditTable(audit, filter);
203
+ html += `</div>`;
204
+ }
205
+
206
+ // Coordinator audit section
207
+ if (hasCoordinatorAudit) {
208
+ html += `<div class="section"><h3>Coordinator Hook Audit Log</h3>`;
209
+ html += renderAuditTable(coordinatorAudit, filter);
210
+ html += `</div>`;
156
211
  }
157
212
 
213
+ // Repo-local annotations section
158
214
  if (hasAnnotations) {
159
- html += `<div class="section"><h3>Hook Annotations</h3>
160
- <p class="section-subtitle">${annotations.length} annotation${annotations.length !== 1 ? 's' : ''}</p>
161
- <div class="annotation-list">`;
162
-
163
- for (const entry of annotations) {
164
- const annotationText = formatAnnotations(entry);
165
- html += `<div class="annotation-card">
166
- <span class="mono">${esc(entry.turn_id || '-')}</span>
167
- <span class="mono">${esc(getHookName(entry))}</span>
168
- <span>${esc(annotationText || JSON.stringify(entry))}</span>
169
- </div>`;
170
- }
215
+ const sectionTitle = hasCoordinatorData ? 'Repo Hook Annotations' : 'Hook Annotations';
216
+ html += `<div class="section"><h3>${sectionTitle}</h3>`;
217
+ html += renderAnnotationList(annotations);
218
+ html += `</div>`;
219
+ }
171
220
 
172
- html += `</div></div>`;
221
+ // Coordinator annotations section
222
+ if (hasCoordinatorAnnotations) {
223
+ html += `<div class="section"><h3>Coordinator Hook Annotations</h3>`;
224
+ html += renderAnnotationList(coordinatorAnnotations);
225
+ html += `</div>`;
173
226
  }
174
227
 
175
228
  html += `</div>`;
@@ -79,12 +79,7 @@ function hasObjections(entry) {
79
79
  return Array.isArray(entry.objections) && entry.objections.length > 0;
80
80
  }
81
81
 
82
- export function render({ ledger, filter = {} }) {
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
- let html = `<div class="ledger-view">
97
- <div class="section"><h3>Decision Ledger</h3>
98
- <p class="section-subtitle">${filtered.length} of ${ledger.length} decision${ledger.length !== 1 ? 's' : ''} shown</p>
99
- <div class="filter-bar">
100
- <label class="filter-control">
101
- <span>Agent</span>
102
- <select data-view-control="ledger-agent">
103
- <option value="all"${selectedAgent === 'all' ? ' selected' : ''}>All roles</option>
104
- ${agents.map((agent) => `<option value="${esc(agent)}"${selectedAgent === agent ? ' selected' : ''}>${esc(agent)}</option>`).join('')}
105
- </select>
106
- </label>
107
- <label class="filter-control">
108
- <span>Phase</span>
109
- <select data-view-control="ledger-phase">
110
- <option value="all"${selectedPhase === 'all' ? ' selected' : ''}>All phases</option>
111
- ${phases.map((p) => `<option value="${esc(p)}"${selectedPhase === p ? ' selected' : ''}>${esc(p)}</option>`).join('')}
112
- </select>
113
- </label>
114
- <label class="filter-control">
115
- <span>Search</span>
116
- <input
117
- type="search"
118
- data-view-control="ledger-query"
119
- value="${esc(query)}"
120
- placeholder="Filter by role or decision"
121
- autocomplete="off"
122
- >
123
- </label>
124
- <label class="filter-control">
125
- <span>From</span>
126
- <input
127
- type="date"
128
- data-view-control="ledger-date-from"
129
- value="${esc(dateFrom)}"
130
- autocomplete="off"
131
- >
132
- </label>
133
- <label class="filter-control">
134
- <span>To</span>
135
- <input
136
- type="date"
137
- data-view-control="ledger-date-to"
138
- value="${esc(dateTo)}"
139
- autocomplete="off"
140
- >
141
- </label>
142
- </div>
143
- <table class="data-table">
144
- <thead><tr><th>Turn</th><th>Agent</th><th>Decision</th><th>Timestamp</th></tr></thead>
145
- <tbody>`;
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></div></div>`;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentxchain",
3
- "version": "2.71.0",
3
+ "version": "2.73.0",
4
4
  "description": "CLI for AgentXchain — governed multi-agent software delivery",
5
5
  "type": "module",
6
6
  "bin": {