agentxchain 2.103.0 → 2.105.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/README.md +13 -7
- package/bin/agentxchain.js +16 -8
- package/dashboard/app.js +111 -7
- package/dashboard/components/blocked.js +95 -11
- package/dashboard/components/blockers.js +85 -86
- package/dashboard/components/coordinator-timeouts.js +13 -0
- package/dashboard/components/cross-repo.js +17 -12
- package/dashboard/components/gate.js +31 -11
- package/dashboard/components/initiative.js +173 -78
- package/dashboard/components/ledger.js +28 -0
- package/dashboard/components/live-status.js +39 -0
- package/dashboard/components/run-history.js +76 -1
- package/dashboard/components/timeline.js +5 -1
- package/dashboard/index.html +21 -0
- package/dashboard/live-observer.js +91 -0
- package/package.json +1 -1
- package/scripts/release-bump.sh +26 -3
- package/scripts/release-preflight.sh +82 -38
- package/src/commands/accept-turn.js +3 -3
- package/src/commands/decisions.js +98 -29
- package/src/commands/diff.js +27 -4
- package/src/commands/doctor.js +48 -16
- package/src/commands/generate.js +126 -1
- package/src/commands/history.js +21 -3
- package/src/commands/init.js +15 -97
- package/src/commands/multi.js +223 -54
- package/src/commands/phase.js +11 -13
- package/src/commands/reject-turn.js +1 -1
- package/src/commands/restart.js +28 -11
- package/src/commands/resume.js +6 -6
- package/src/commands/role.js +51 -14
- package/src/commands/run.js +5 -11
- package/src/commands/status.js +145 -13
- package/src/commands/step.js +36 -29
- package/src/lib/admission-control.js +14 -12
- package/src/lib/blocked-state.js +150 -0
- package/src/lib/conflict-actions.js +17 -0
- package/src/lib/context-section-parser.js +2 -0
- package/src/lib/continuity-status.js +1 -1
- package/src/lib/coordinator-blocker-presentation.js +127 -0
- package/src/lib/coordinator-event-narrative.js +43 -0
- package/src/lib/coordinator-gate-approval.js +98 -0
- package/src/lib/coordinator-gate-evaluation-presentation.js +57 -0
- package/src/lib/coordinator-next-actions.js +128 -0
- package/src/lib/coordinator-pending-gate-presentation.js +79 -0
- package/src/lib/coordinator-presentation-detail.js +11 -0
- package/src/lib/coordinator-repo-snapshots.js +53 -0
- package/src/lib/coordinator-repo-status-presentation.js +134 -0
- package/src/lib/dashboard/actions.js +105 -29
- package/src/lib/dashboard/bridge-server.js +7 -0
- package/src/lib/dashboard/coordinator-blockers.js +17 -0
- package/src/lib/dashboard/coordinator-repo-status.js +50 -0
- package/src/lib/dashboard/coordinator-timeout-status.js +34 -11
- package/src/lib/dashboard/state-reader.js +36 -1
- package/src/lib/dispatch-bundle.js +23 -0
- package/src/lib/export-diff.js +70 -38
- package/src/lib/export-verifier.js +3 -0
- package/src/lib/history-diff-summary.js +249 -0
- package/src/lib/manual-qa-fallback.js +18 -0
- package/src/lib/normalized-config.js +27 -22
- package/src/lib/planning-artifacts.js +131 -0
- package/src/lib/recent-event-summary.js +132 -0
- package/src/lib/repo-decisions.js +69 -28
- package/src/lib/report.js +353 -145
- package/src/lib/run-diff.js +4 -0
- package/src/lib/runtime-capabilities.js +222 -0
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ Legacy IDE-window coordination is still shipped as a compatibility mode for team
|
|
|
17
17
|
- [Templates](https://agentxchain.dev/docs/templates/)
|
|
18
18
|
- [Export schema reference](https://agentxchain.dev/docs/export-schema/)
|
|
19
19
|
- [Adapter reference](https://agentxchain.dev/docs/adapters/)
|
|
20
|
-
- [Protocol
|
|
20
|
+
- [Protocol v7](https://agentxchain.dev/docs/protocol/)
|
|
21
21
|
- [Protocol reference](https://agentxchain.dev/docs/protocol-reference/)
|
|
22
22
|
- [Build your own runner](https://agentxchain.dev/docs/build-your-own-runner/)
|
|
23
23
|
- [Why governed multi-agent delivery matters](https://agentxchain.dev/why/)
|
|
@@ -173,16 +173,17 @@ agentxchain step
|
|
|
173
173
|
| `verify turn` | Replay a staged turn's declared machine-evidence commands to confirm reproducibility before acceptance |
|
|
174
174
|
| `replay turn` | Replay an accepted turn's machine-evidence commands from history for audit and drift detection |
|
|
175
175
|
| `verify protocol` | Run the shipped protocol conformance suite against a target implementation |
|
|
176
|
-
| `dashboard` | Open the local governance dashboard in your browser for repo
|
|
176
|
+
| `dashboard` | Open the live local governance dashboard in your browser for the current repo/workspace or multi-repo coordinator initiative, including pending gate approvals |
|
|
177
177
|
| `run [--auto-approve] [--max-turns N] [--dry-run]` | Drive a governed run from start to completion — dispatches turns, handles gates, manages rejection/retry |
|
|
178
178
|
|
|
179
179
|
### Governed proof and inspection
|
|
180
180
|
|
|
181
181
|
| Command | What it does |
|
|
182
182
|
|---|---|
|
|
183
|
-
| `audit [--format json]` | Live governance audit report with cost summary, decision history, and artifact inventory |
|
|
183
|
+
| `audit [--format json]` | Live governance audit report for the current repo/workspace with cost summary, decision history, and artifact inventory |
|
|
184
184
|
| `diff <left> <right>` | Compare two governed runs side by side (phase, decisions, artifacts, timing) |
|
|
185
|
-
| `report` | Generate a governance report
|
|
185
|
+
| `report` | Generate a governance report from a verified export artifact (`--input <path>` or stdin) |
|
|
186
|
+
| `replay export <export-file>` | Open an existing verified export artifact in the read-only dashboard for offline post-mortem inspection |
|
|
186
187
|
| `events [--type <type>] [--limit N]` | Inspect the lifecycle event stream (turns, phases, gates, governance events) |
|
|
187
188
|
| `history [--limit N] [--role <role>]` | Query accepted-turn history from append-only JSONL |
|
|
188
189
|
| `role list\|show` | List all configured roles or inspect a single role's charter, runtime, and phase assignment |
|
|
@@ -192,6 +193,8 @@ agentxchain step
|
|
|
192
193
|
| `doctor [--json]` | Governed project health check: config, roles, runtimes, state, schedules, plugins, workflow-kit, connector handoff |
|
|
193
194
|
| `connector check [--json]` | Live health probes for all configured connectors (api_proxy, remote_agent, MCP stdio/streamable_http) |
|
|
194
195
|
|
|
196
|
+
Partial coordinator artifacts are first-class here too: `audit` and `report` keep repo rows plus `repo_ok_count` / `repo_error_count` export-health totals when a child export fails, and they do not fabricate child drill-down for the failed repo.
|
|
197
|
+
|
|
195
198
|
### Governed automation, plugins, and continuity
|
|
196
199
|
|
|
197
200
|
| Command | What it does |
|
|
@@ -202,10 +205,12 @@ agentxchain step
|
|
|
202
205
|
| `schedule list\|run-due\|daemon\|status` | Run repo-local lights-out scheduling: inspect schedules, execute due runs, poll in a local daemon loop, or check daemon heartbeat |
|
|
203
206
|
| `plugin install\|list\|remove` | Install, inspect, or remove governed hook plugins under `.agentxchain/plugins/` |
|
|
204
207
|
| `plugin list-available` | List bundled built-in plugins installable by short name |
|
|
205
|
-
| `export [--output <path>]` | Export
|
|
208
|
+
| `export [--output <path>]` | Export the portable raw governed/coordinator artifact for continuity or offline review |
|
|
206
209
|
| `restore --input <path>` | Restore run state from a prior export on a same-repo, same-commit checkout |
|
|
207
210
|
| `restart` | Rebuild lost session context from `.agentxchain/session.json` |
|
|
208
211
|
|
|
212
|
+
`dashboard` and `audit` read the live current repo/workspace. `report --input` and `replay export` read an existing verified export artifact instead: `report` renders a derived summary, while `replay export` opens the read-only dashboard. Partial coordinator artifacts remain first-class here too: `report` and `replay export` keep repo rows plus `repo_ok_count` / `repo_error_count` export-health totals when a child export fails, and they do not fabricate child drill-down for the failed repo.
|
|
213
|
+
|
|
209
214
|
### Shared utilities
|
|
210
215
|
|
|
211
216
|
| Command | What it does |
|
|
@@ -224,7 +229,7 @@ agentxchain step
|
|
|
224
229
|
| `supervise` | Run `watch` plus optional macOS auto-nudge |
|
|
225
230
|
| `claim` / `release` | Human override of legacy lock ownership |
|
|
226
231
|
| `rebind` | Rebuild Cursor bindings |
|
|
227
|
-
| `generate` | Regenerate VS Code agent files |
|
|
232
|
+
| `generate` | Regenerate VS Code agent files; use `generate planning` to restore scaffold-owned governed planning docs |
|
|
228
233
|
| `branch` | Manage Cursor branch override for launches |
|
|
229
234
|
| `doctor` | Check local environment and setup |
|
|
230
235
|
| `stop` | Stop watch daemon and local sessions |
|
|
@@ -273,6 +278,7 @@ The first-party governed workflow kit includes `.planning/SYSTEM_SPEC.md` alongs
|
|
|
273
278
|
- `local_cli`: implemented
|
|
274
279
|
- `mcp`: implemented for stdio and streamable HTTP tool-contract dispatch
|
|
275
280
|
- `api_proxy`: implemented for synchronous `review_only` and `proposed` write-authority turns; stages a provider-backed result during `step`
|
|
281
|
+
- `remote_agent`: implemented for governed HTTP request/response dispatch against long-running remote agents
|
|
276
282
|
|
|
277
283
|
## Legacy IDE Mode
|
|
278
284
|
|
|
@@ -309,7 +315,7 @@ Requires:
|
|
|
309
315
|
- [Quickstart](https://agentxchain.dev/docs/quickstart/)
|
|
310
316
|
- [CLI reference](https://agentxchain.dev/docs/cli/)
|
|
311
317
|
- [Adapter reference](https://agentxchain.dev/docs/adapters/)
|
|
312
|
-
- [Protocol
|
|
318
|
+
- [Protocol v7](https://agentxchain.dev/docs/protocol/)
|
|
313
319
|
- [GitHub](https://github.com/shivamtiwari93/agentXchain.dev)
|
|
314
320
|
- [Legacy Protocol v3 spec](https://github.com/shivamtiwari93/agentXchain.dev/blob/main/PROTOCOL-v3.md)
|
|
315
321
|
|
package/bin/agentxchain.js
CHANGED
|
@@ -55,7 +55,7 @@ import { configCommand } from '../src/commands/config.js';
|
|
|
55
55
|
import { updateCommand } from '../src/commands/update.js';
|
|
56
56
|
import { watchCommand } from '../src/commands/watch.js';
|
|
57
57
|
import { claimCommand, releaseCommand } from '../src/commands/claim.js';
|
|
58
|
-
import { generateCommand } from '../src/commands/generate.js';
|
|
58
|
+
import { generateCommand, generatePlanningCommand } from '../src/commands/generate.js';
|
|
59
59
|
import { doctorCommand } from '../src/commands/doctor.js';
|
|
60
60
|
import { superviseCommand } from '../src/commands/supervise.js';
|
|
61
61
|
import { validateCommand } from '../src/commands/validate.js';
|
|
@@ -153,14 +153,14 @@ program
|
|
|
153
153
|
|
|
154
154
|
program
|
|
155
155
|
.command('export')
|
|
156
|
-
.description('
|
|
156
|
+
.description('Write a portable governed/coordinator export artifact from the current repo/workspace')
|
|
157
157
|
.option('--format <format>', 'Export format (json)', 'json')
|
|
158
158
|
.option('--output <path>', 'Write the export artifact to a file instead of stdout')
|
|
159
159
|
.action(exportCommand);
|
|
160
160
|
|
|
161
161
|
program
|
|
162
162
|
.command('audit')
|
|
163
|
-
.description('Render a governance audit
|
|
163
|
+
.description('Render a governance audit from the live current repo/workspace')
|
|
164
164
|
.option('--format <format>', 'Output format: text, json, markdown, or html', 'text')
|
|
165
165
|
.action(auditCommand);
|
|
166
166
|
|
|
@@ -178,7 +178,7 @@ program
|
|
|
178
178
|
|
|
179
179
|
program
|
|
180
180
|
.command('report')
|
|
181
|
-
.description('Render a
|
|
181
|
+
.description('Render a governance summary from an existing verified export artifact')
|
|
182
182
|
.option('--input <path>', 'Export artifact path, or "-" for stdin', '-')
|
|
183
183
|
.option('--format <format>', 'Output format: text, json, markdown, or html', 'text')
|
|
184
184
|
.action(reportCommand);
|
|
@@ -223,11 +223,19 @@ program
|
|
|
223
223
|
.option('--unset', 'Remove override and follow the active git branch automatically')
|
|
224
224
|
.action(branchCommand);
|
|
225
225
|
|
|
226
|
-
program
|
|
226
|
+
const generateCmd = program
|
|
227
227
|
.command('generate')
|
|
228
|
-
.description('Regenerate VS Code agent files
|
|
228
|
+
.description('Regenerate VS Code agent files, or governed planning artifacts via subcommands')
|
|
229
229
|
.action(generateCommand);
|
|
230
230
|
|
|
231
|
+
generateCmd
|
|
232
|
+
.command('planning')
|
|
233
|
+
.description('Generate or restore scaffold-owned governed planning artifacts')
|
|
234
|
+
.option('--dry-run', 'Show which planning artifacts would be written without changing files')
|
|
235
|
+
.option('--force', 'Overwrite existing scaffold-owned planning artifacts')
|
|
236
|
+
.option('-j, --json', 'Output as JSON')
|
|
237
|
+
.action(generatePlanningCommand);
|
|
238
|
+
|
|
231
239
|
program
|
|
232
240
|
.command('watch')
|
|
233
241
|
.description('Watch lock.json and coordinate agent turns (the referee)')
|
|
@@ -441,7 +449,7 @@ replayCmd
|
|
|
441
449
|
|
|
442
450
|
replayCmd
|
|
443
451
|
.command('export <export-file>')
|
|
444
|
-
.description('
|
|
452
|
+
.description('Open an existing export artifact in the read-only dashboard')
|
|
445
453
|
.option('-j, --json', 'Output session info as JSON')
|
|
446
454
|
.option('--port <port>', 'Dashboard port', '3847')
|
|
447
455
|
.option('--no-open', 'Do not auto-open browser')
|
|
@@ -522,7 +530,7 @@ program
|
|
|
522
530
|
|
|
523
531
|
program
|
|
524
532
|
.command('dashboard')
|
|
525
|
-
.description('Open the
|
|
533
|
+
.description('Open the live governance dashboard for the current repo/workspace')
|
|
526
534
|
.option('--port <port>', 'Server port', '3847')
|
|
527
535
|
.option('--daemon', 'Run the dashboard in background mode')
|
|
528
536
|
.option('--no-open', 'Do not auto-open the browser')
|
package/dashboard/app.js
CHANGED
|
@@ -18,15 +18,20 @@ import { render as renderArtifacts } from './components/artifacts.js';
|
|
|
18
18
|
import { render as renderRunHistory } from './components/run-history.js';
|
|
19
19
|
import { render as renderTimeouts } from './components/timeouts.js';
|
|
20
20
|
import { render as renderCoordinatorTimeouts } from './components/coordinator-timeouts.js';
|
|
21
|
+
import {
|
|
22
|
+
buildLiveMeta,
|
|
23
|
+
createLiveEventFromMessage,
|
|
24
|
+
shouldRefreshViewForLiveMessage,
|
|
25
|
+
} from './live-observer.js';
|
|
21
26
|
|
|
22
27
|
const VIEWS = {
|
|
23
28
|
timeline: { fetch: ['state', 'continuity', 'history', 'audit', 'annotations', 'connectors', 'coordinatorAudit', 'coordinatorAnnotations'], render: renderTimeline },
|
|
24
29
|
delegations: { fetch: ['state', 'history'], render: renderDelegations },
|
|
25
|
-
ledger: { fetch: ['state', 'ledger', 'coordinatorState', 'coordinatorLedger'], render: renderLedger },
|
|
30
|
+
ledger: { fetch: ['state', 'ledger', 'coordinatorState', 'coordinatorLedger', 'repoDecisionsSummary'], render: renderLedger },
|
|
26
31
|
hooks: { fetch: ['audit', 'annotations', 'coordinatorAudit', 'coordinatorAnnotations'], render: renderHooks },
|
|
27
|
-
blocked: { fetch: ['state', 'audit', 'coordinatorState', 'coordinatorAudit'], render: renderBlocked },
|
|
32
|
+
blocked: { fetch: ['state', 'audit', 'coordinatorState', 'coordinatorAudit', 'coordinatorBlockers', 'coordinatorRepoStatusRows'], render: renderBlocked },
|
|
28
33
|
gate: { fetch: ['state', 'history', 'coordinatorState', 'coordinatorHistory', 'coordinatorBarriers'], render: renderGate },
|
|
29
|
-
initiative: { fetch: ['coordinatorState', 'coordinatorBarriers', 'barrierLedger', 'coordinatorBlockers'], render: renderInitiative },
|
|
34
|
+
initiative: { fetch: ['coordinatorState', 'coordinatorBarriers', 'barrierLedger', 'coordinatorBlockers', 'coordinatorRepoStatusRows'], render: renderInitiative },
|
|
30
35
|
'cross-repo': { fetch: ['coordinatorState', 'coordinatorHistory'], render: renderCrossRepo },
|
|
31
36
|
blockers: { fetch: ['coordinatorBlockers'], render: renderBlockers },
|
|
32
37
|
artifacts: { fetch: ['workflowKitArtifacts'], render: renderArtifacts },
|
|
@@ -40,6 +45,7 @@ const API_MAP = {
|
|
|
40
45
|
continuity: '/api/continuity',
|
|
41
46
|
history: '/api/history',
|
|
42
47
|
ledger: '/api/ledger',
|
|
48
|
+
repoDecisionsSummary: '/api/repo-decisions-summary',
|
|
43
49
|
coordinatorLedger: '/api/coordinator/ledger',
|
|
44
50
|
audit: '/api/hooks/audit',
|
|
45
51
|
annotations: '/api/hooks/annotations',
|
|
@@ -50,6 +56,7 @@ const API_MAP = {
|
|
|
50
56
|
coordinatorAudit: '/api/coordinator/hooks/audit',
|
|
51
57
|
coordinatorAnnotations: '/api/coordinator/hooks/annotations',
|
|
52
58
|
coordinatorBlockers: '/api/coordinator/blockers',
|
|
59
|
+
coordinatorRepoStatusRows: '/api/coordinator/repo-status',
|
|
53
60
|
workflowKitArtifacts: '/api/workflow-kit-artifacts',
|
|
54
61
|
connectors: '/api/connectors',
|
|
55
62
|
runHistory: '/api/run-history',
|
|
@@ -76,6 +83,12 @@ let activeViewName = null;
|
|
|
76
83
|
let activeViewData = null;
|
|
77
84
|
let dashboardSession = null;
|
|
78
85
|
let actionInFlight = false;
|
|
86
|
+
const liveObserverState = {
|
|
87
|
+
connected: false,
|
|
88
|
+
lastRefreshAt: null,
|
|
89
|
+
lastRunEvent: null,
|
|
90
|
+
lastCoordinatorEvent: null,
|
|
91
|
+
};
|
|
79
92
|
|
|
80
93
|
function escapeHtml(str) {
|
|
81
94
|
if (str == null) return '';
|
|
@@ -132,19 +145,40 @@ async function pickInitialView() {
|
|
|
132
145
|
}
|
|
133
146
|
|
|
134
147
|
function buildRenderData(viewName, data) {
|
|
148
|
+
const liveMeta = viewName === 'timeline'
|
|
149
|
+
? buildLiveMeta({
|
|
150
|
+
connected: liveObserverState.connected,
|
|
151
|
+
lastRefreshAt: liveObserverState.lastRefreshAt,
|
|
152
|
+
lastEvent: liveObserverState.lastRunEvent,
|
|
153
|
+
scope: 'run',
|
|
154
|
+
})
|
|
155
|
+
: viewName === 'cross-repo'
|
|
156
|
+
? buildLiveMeta({
|
|
157
|
+
connected: liveObserverState.connected,
|
|
158
|
+
lastRefreshAt: liveObserverState.lastRefreshAt,
|
|
159
|
+
lastEvent: liveObserverState.lastCoordinatorEvent,
|
|
160
|
+
scope: 'coordinator',
|
|
161
|
+
})
|
|
162
|
+
: null;
|
|
163
|
+
|
|
135
164
|
if (viewName === 'ledger') {
|
|
136
165
|
return {
|
|
137
166
|
...data,
|
|
138
167
|
filter: viewState.ledger,
|
|
168
|
+
liveMeta,
|
|
139
169
|
};
|
|
140
170
|
}
|
|
141
171
|
if (viewName === 'hooks') {
|
|
142
172
|
return {
|
|
143
173
|
...data,
|
|
144
174
|
filter: viewState.hooks,
|
|
175
|
+
liveMeta,
|
|
145
176
|
};
|
|
146
177
|
}
|
|
147
|
-
return
|
|
178
|
+
return {
|
|
179
|
+
...data,
|
|
180
|
+
liveMeta,
|
|
181
|
+
};
|
|
148
182
|
}
|
|
149
183
|
|
|
150
184
|
function renderView(viewName, data) {
|
|
@@ -172,6 +206,51 @@ function setActionBanner(message, tone = 'info') {
|
|
|
172
206
|
banner.className = `action-banner visible ${tone === 'error' ? 'error' : tone === 'success' ? 'success' : ''}`.trim();
|
|
173
207
|
}
|
|
174
208
|
|
|
209
|
+
function formatActionErrorMessage(payload, status) {
|
|
210
|
+
if (!payload || typeof payload !== 'object') {
|
|
211
|
+
return `Dashboard action failed with HTTP ${status}.`;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const parts = [];
|
|
215
|
+
if (typeof payload.error === 'string' && payload.error.trim()) {
|
|
216
|
+
parts.push(payload.error.trim());
|
|
217
|
+
} else {
|
|
218
|
+
parts.push(`Dashboard action failed with HTTP ${status}.`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const detail = payload.recovery_summary?.detail;
|
|
222
|
+
if (typeof detail === 'string' && detail.trim()) {
|
|
223
|
+
parts.push(detail.trim());
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const nextAction = payload.next_actions?.[0]?.command || payload.next_action || null;
|
|
227
|
+
if (typeof nextAction === 'string' && nextAction.trim()) {
|
|
228
|
+
parts.push(`Next: ${nextAction.trim()}`);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return parts.join(' ');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function formatActionSuccessMessage(payload) {
|
|
235
|
+
if (!payload || typeof payload !== 'object') {
|
|
236
|
+
return 'Gate approved.';
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const parts = [];
|
|
240
|
+
if (typeof payload.message === 'string' && payload.message.trim()) {
|
|
241
|
+
parts.push(payload.message.trim());
|
|
242
|
+
} else {
|
|
243
|
+
parts.push('Gate approved.');
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const nextAction = payload.next_actions?.[0]?.command || payload.next_action || null;
|
|
247
|
+
if (typeof nextAction === 'string' && nextAction.trim()) {
|
|
248
|
+
parts.push(`Next: ${nextAction.trim()}`);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return parts.join(' ');
|
|
252
|
+
}
|
|
253
|
+
|
|
175
254
|
async function loadView(viewName, { refresh = true } = {}) {
|
|
176
255
|
const view = VIEWS[viewName];
|
|
177
256
|
if (!view) {
|
|
@@ -183,11 +262,17 @@ async function loadView(viewName, { refresh = true } = {}) {
|
|
|
183
262
|
activeViewName = viewName;
|
|
184
263
|
if (shouldRefetch) {
|
|
185
264
|
activeViewData = await fetchData(view.fetch);
|
|
265
|
+
liveObserverState.lastRefreshAt = new Date().toISOString();
|
|
186
266
|
}
|
|
187
267
|
|
|
188
268
|
renderView(viewName, activeViewData);
|
|
189
269
|
}
|
|
190
270
|
|
|
271
|
+
function rerenderActiveView() {
|
|
272
|
+
if (!activeViewName) return;
|
|
273
|
+
renderView(activeViewName, activeViewData);
|
|
274
|
+
}
|
|
275
|
+
|
|
191
276
|
// ── WebSocket connection ──────────────────────────────────────────────────
|
|
192
277
|
|
|
193
278
|
let ws = null;
|
|
@@ -204,13 +289,30 @@ function connect() {
|
|
|
204
289
|
statusDot.classList.remove('disconnected');
|
|
205
290
|
statusLabel.textContent = 'Connected';
|
|
206
291
|
reconnectDelay = 1000;
|
|
292
|
+
liveObserverState.connected = true;
|
|
207
293
|
loadView(currentView());
|
|
208
294
|
};
|
|
209
295
|
|
|
210
296
|
ws.onmessage = (event) => {
|
|
211
297
|
try {
|
|
212
298
|
const msg = JSON.parse(event.data);
|
|
213
|
-
if (msg.type === '
|
|
299
|
+
if (msg.type === 'event') {
|
|
300
|
+
liveObserverState.lastRunEvent = createLiveEventFromMessage(msg);
|
|
301
|
+
rerenderActiveView();
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (msg.type === 'coordinator_event') {
|
|
306
|
+
liveObserverState.lastCoordinatorEvent = createLiveEventFromMessage(msg);
|
|
307
|
+
if (shouldRefreshViewForLiveMessage(currentView(), msg.type)) {
|
|
308
|
+
loadView(currentView());
|
|
309
|
+
} else {
|
|
310
|
+
rerenderActiveView();
|
|
311
|
+
}
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (shouldRefreshViewForLiveMessage(currentView(), msg.type)) {
|
|
214
316
|
loadView(currentView());
|
|
215
317
|
}
|
|
216
318
|
} catch {}
|
|
@@ -219,6 +321,8 @@ function connect() {
|
|
|
219
321
|
ws.onclose = () => {
|
|
220
322
|
statusDot.classList.add('disconnected');
|
|
221
323
|
statusLabel.textContent = 'Disconnected';
|
|
324
|
+
liveObserverState.connected = false;
|
|
325
|
+
rerenderActiveView();
|
|
222
326
|
setTimeout(() => {
|
|
223
327
|
reconnectDelay = Math.min(reconnectDelay * 2, 30000);
|
|
224
328
|
connect();
|
|
@@ -344,11 +448,11 @@ document.addEventListener('click', async (event) => {
|
|
|
344
448
|
}));
|
|
345
449
|
|
|
346
450
|
if (!res.ok || payload.ok === false) {
|
|
347
|
-
setActionBanner(payload
|
|
451
|
+
setActionBanner(formatActionErrorMessage(payload, res.status), 'error');
|
|
348
452
|
return;
|
|
349
453
|
}
|
|
350
454
|
|
|
351
|
-
setActionBanner(payload
|
|
455
|
+
setActionBanner(formatActionSuccessMessage(payload), 'success');
|
|
352
456
|
await loadView(currentView());
|
|
353
457
|
} catch (error) {
|
|
354
458
|
setActionBanner(error?.message || 'Dashboard action failed.', 'error');
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { getCoordinatorPendingGateDetails } from '../../src/lib/coordinator-pending-gate-presentation.js';
|
|
2
|
+
import { buildCoordinatorRepoStatusRows } from '../../src/lib/coordinator-repo-status-presentation.js';
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* Blocked State view — renders current blocked state with recovery info.
|
|
3
6
|
*
|
|
@@ -23,6 +26,18 @@ function getHookName(entry) {
|
|
|
23
26
|
return entry?.hook_name || entry?.hook || entry?.name || '';
|
|
24
27
|
}
|
|
25
28
|
|
|
29
|
+
function renderDetailRows(details) {
|
|
30
|
+
if (!Array.isArray(details) || details.length === 0) {
|
|
31
|
+
return '';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let html = '';
|
|
35
|
+
for (const detail of details) {
|
|
36
|
+
html += `<dt>${esc(detail.label)}</dt><dd${detail.mono ? ' class="mono"' : ''}>${esc(detail.value)}</dd>`;
|
|
37
|
+
}
|
|
38
|
+
return html;
|
|
39
|
+
}
|
|
40
|
+
|
|
26
41
|
function selectRelevantAuditEntries(state, audit) {
|
|
27
42
|
if (!Array.isArray(audit) || audit.length === 0) {
|
|
28
43
|
return [];
|
|
@@ -55,7 +70,36 @@ function selectRelevantAuditEntries(state, audit) {
|
|
|
55
70
|
return audit.slice(-3);
|
|
56
71
|
}
|
|
57
72
|
|
|
58
|
-
|
|
73
|
+
function getCoordinatorRepoRows(coordinatorState, coordinatorRepoStatusRows) {
|
|
74
|
+
if (Array.isArray(coordinatorRepoStatusRows) && coordinatorRepoStatusRows.length > 0) {
|
|
75
|
+
return coordinatorRepoStatusRows;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return buildCoordinatorRepoStatusRows({
|
|
79
|
+
config: null,
|
|
80
|
+
coordinatorRepoRuns: coordinatorState?.repo_runs || {},
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function formatCoordinatorRepoCardMeta(row) {
|
|
85
|
+
const parts = [];
|
|
86
|
+
if (row?.run_id) {
|
|
87
|
+
parts.push(`run ${row.run_id}`);
|
|
88
|
+
}
|
|
89
|
+
for (const detail of Array.isArray(row?.details) ? row.details : []) {
|
|
90
|
+
parts.push(`${detail.label}: ${detail.value}`);
|
|
91
|
+
}
|
|
92
|
+
return parts.join(' | ') || '-';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function render({
|
|
96
|
+
state,
|
|
97
|
+
audit = [],
|
|
98
|
+
coordinatorState = null,
|
|
99
|
+
coordinatorAudit = [],
|
|
100
|
+
coordinatorBlockers = null,
|
|
101
|
+
coordinatorRepoStatusRows = null,
|
|
102
|
+
}) {
|
|
59
103
|
const activeState = state?.status === 'blocked' ? state : coordinatorState;
|
|
60
104
|
const activeAudit = activeState === state ? audit : coordinatorAudit;
|
|
61
105
|
const isCoordinator = activeState === coordinatorState;
|
|
@@ -75,6 +119,26 @@ export function render({ state, audit = [], coordinatorState = null, coordinator
|
|
|
75
119
|
const typedReason = recovery.typed_reason || null;
|
|
76
120
|
const turnRetained = typeof recovery.turn_retained === 'boolean' ? recovery.turn_retained : null;
|
|
77
121
|
const blockedAt = blocked.blocked_at || null;
|
|
122
|
+
const runtimeGuidance = Array.isArray(activeState.runtime_guidance) ? activeState.runtime_guidance : [];
|
|
123
|
+
const coordinatorNextActions = coordinatorBlockers?.ok === false
|
|
124
|
+
? []
|
|
125
|
+
: Array.isArray(coordinatorBlockers?.next_actions)
|
|
126
|
+
? coordinatorBlockers.next_actions
|
|
127
|
+
: [];
|
|
128
|
+
const nextActions = isCoordinator
|
|
129
|
+
? coordinatorNextActions
|
|
130
|
+
: Array.isArray(activeState.next_actions)
|
|
131
|
+
? activeState.next_actions
|
|
132
|
+
: [];
|
|
133
|
+
const coordinatorPendingGateDetails = isCoordinator
|
|
134
|
+
? getCoordinatorPendingGateDetails({
|
|
135
|
+
pendingGate: activeState.pending_gate,
|
|
136
|
+
active: coordinatorBlockers?.active,
|
|
137
|
+
})
|
|
138
|
+
: [];
|
|
139
|
+
const coordinatorRepoRows = isCoordinator
|
|
140
|
+
? getCoordinatorRepoRows(coordinatorState, coordinatorRepoStatusRows)
|
|
141
|
+
: [];
|
|
78
142
|
const relevantAudit = selectRelevantAuditEntries(activeState, activeAudit);
|
|
79
143
|
|
|
80
144
|
let html = `<div class="blocked-view">
|
|
@@ -104,22 +168,42 @@ export function render({ state, audit = [], coordinatorState = null, coordinator
|
|
|
104
168
|
</div>`;
|
|
105
169
|
}
|
|
106
170
|
|
|
107
|
-
if (
|
|
171
|
+
if (runtimeGuidance.length > 0) {
|
|
172
|
+
html += `<div class="section"><h3>Runtime Guidance</h3><div class="annotation-list">`;
|
|
173
|
+
for (const entry of runtimeGuidance) {
|
|
174
|
+
html += `<div class="annotation-card">
|
|
175
|
+
<span class="mono">${esc(entry.code || '-')}</span>
|
|
176
|
+
<span class="mono">${esc(entry.command || '-')}</span>
|
|
177
|
+
<span>${esc(entry.reason || '-')}</span>
|
|
178
|
+
</div>`;
|
|
179
|
+
}
|
|
180
|
+
html += `</div></div>`;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (nextActions.length > 0) {
|
|
184
|
+
html += `<div class="section"><h3>Next Actions</h3><div class="annotation-list">`;
|
|
185
|
+
for (const action of nextActions) {
|
|
186
|
+
html += `<div class="annotation-card">
|
|
187
|
+
<span class="mono">${esc(action.command || '-')}</span>
|
|
188
|
+
<span>${esc(action.reason || '-')}</span>
|
|
189
|
+
</div>`;
|
|
190
|
+
}
|
|
191
|
+
html += `</div></div>`;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (isCoordinator && coordinatorPendingGateDetails.length > 0) {
|
|
108
195
|
html += `<div class="section"><h3>Pending Gate</h3>
|
|
109
|
-
<dl class="detail-list">
|
|
110
|
-
<dt>Gate</dt><dd class="mono">${esc(activeState.pending_gate.gate || '-')}</dd>
|
|
111
|
-
<dt>Type</dt><dd>${esc(activeState.pending_gate.gate_type || '-')}</dd>
|
|
112
|
-
</dl>
|
|
113
|
-
<pre class="recovery-command mono" data-copy="agentxchain multi approve-gate">agentxchain multi approve-gate</pre>
|
|
196
|
+
<dl class="detail-list">${renderDetailRows(coordinatorPendingGateDetails)}</dl>
|
|
114
197
|
</div>`;
|
|
115
198
|
}
|
|
116
199
|
|
|
117
|
-
if (isCoordinator &&
|
|
200
|
+
if (isCoordinator && coordinatorRepoRows.length > 0) {
|
|
118
201
|
html += `<div class="section"><h3>Repo Status</h3><div class="annotation-list">`;
|
|
119
|
-
for (const
|
|
202
|
+
for (const row of coordinatorRepoRows) {
|
|
120
203
|
html += `<div class="annotation-card">
|
|
121
|
-
<span class="mono">${esc(
|
|
122
|
-
<span>${esc(`${
|
|
204
|
+
<span class="mono">${esc(row.repo_id || '-')}</span>
|
|
205
|
+
<span>${esc(`${row.status || 'unknown'}${row.phase ? ` [${row.phase}]` : ''}`)}</span>
|
|
206
|
+
<span>${esc(formatCoordinatorRepoCardMeta(row))}</span>
|
|
123
207
|
</div>`;
|
|
124
208
|
}
|
|
125
209
|
html += `</div></div>`;
|