opencastle 0.22.0 → 0.23.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/dist/cli/convoy/engine.d.ts +1 -0
- package/dist/cli/convoy/engine.d.ts.map +1 -1
- package/dist/cli/convoy/engine.js +1 -0
- package/dist/cli/convoy/engine.js.map +1 -1
- package/dist/cli/convoy/export.d.ts +1 -0
- package/dist/cli/convoy/export.d.ts.map +1 -1
- package/dist/cli/convoy/export.js +34 -0
- package/dist/cli/convoy/export.js.map +1 -1
- package/dist/cli/convoy/pipeline.d.ts +35 -0
- package/dist/cli/convoy/pipeline.d.ts.map +1 -0
- package/dist/cli/convoy/pipeline.js +353 -0
- package/dist/cli/convoy/pipeline.js.map +1 -0
- package/dist/cli/convoy/pipeline.test.d.ts +2 -0
- package/dist/cli/convoy/pipeline.test.d.ts.map +1 -0
- package/dist/cli/convoy/pipeline.test.js +778 -0
- package/dist/cli/convoy/pipeline.test.js.map +1 -0
- package/dist/cli/convoy/store.d.ts +14 -2
- package/dist/cli/convoy/store.d.ts.map +1 -1
- package/dist/cli/convoy/store.js +84 -5
- package/dist/cli/convoy/store.js.map +1 -1
- package/dist/cli/convoy/store.test.js +216 -7
- package/dist/cli/convoy/store.test.js.map +1 -1
- package/dist/cli/convoy/types.d.ts +15 -0
- package/dist/cli/convoy/types.d.ts.map +1 -1
- package/dist/cli/dashboard.d.ts.map +1 -1
- package/dist/cli/dashboard.js +1 -0
- package/dist/cli/dashboard.js.map +1 -1
- package/dist/cli/run/schema.d.ts +5 -1
- package/dist/cli/run/schema.d.ts.map +1 -1
- package/dist/cli/run/schema.js +41 -8
- package/dist/cli/run/schema.js.map +1 -1
- package/dist/cli/run/schema.test.js +194 -5
- package/dist/cli/run/schema.test.js.map +1 -1
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +141 -2
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/types.d.ts +3 -1
- package/dist/cli/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cli/convoy/engine.ts +2 -0
- package/src/cli/convoy/export.ts +41 -0
- package/src/cli/convoy/pipeline.test.ts +939 -0
- package/src/cli/convoy/pipeline.ts +430 -0
- package/src/cli/convoy/store.test.ts +239 -7
- package/src/cli/convoy/store.ts +110 -7
- package/src/cli/convoy/types.ts +17 -0
- package/src/cli/dashboard.ts +1 -0
- package/src/cli/run/schema.test.ts +244 -5
- package/src/cli/run/schema.ts +49 -8
- package/src/cli/run.ts +140 -2
- package/src/cli/types.ts +3 -1
- package/src/dashboard/dist/_astro/{index.DyyaCW8L.css → index.Cq68OHaZ.css} +1 -1
- package/src/dashboard/dist/index.html +214 -2
- package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
- package/src/dashboard/src/pages/index.astro +230 -1
- package/src/dashboard/src/styles/dashboard.css +116 -0
- package/src/orchestrator/customizations/KNOWN-ISSUES.md +1 -1
|
@@ -27,8 +27,9 @@ const base = import.meta.env.BASE_URL;
|
|
|
27
27
|
<nav class="dash-sidebar" id="dash-sidebar">
|
|
28
28
|
<ul class="dash-sidebar__list">
|
|
29
29
|
<li><a class="dash-sidebar__link" href="#convoy-section" data-section="convoy-section">Convoy</a></li>
|
|
30
|
+
<li><a class="dash-sidebar__link" href="#convoy-pipeline-section" data-section="convoy-pipeline-section">Convoy Chain</a></li>
|
|
30
31
|
<li><a class="dash-sidebar__link dash-sidebar__link--active" href="#kpi-row" data-section="kpi-row">Overview</a></li>
|
|
31
|
-
<li><a class="dash-sidebar__link" href="#pipeline-section" data-section="pipeline-section">
|
|
32
|
+
<li><a class="dash-sidebar__link" href="#pipeline-section" data-section="pipeline-section">Task Flow</a></li>
|
|
32
33
|
<li><a class="dash-sidebar__link" href="#agent-section" data-section="agent-section">Agents</a></li>
|
|
33
34
|
<li><a class="dash-sidebar__link" href="#tier-section" data-section="tier-section">Tiers</a></li>
|
|
34
35
|
<li><a class="dash-sidebar__link" href="#delegation-section" data-section="delegation-section">Delegations</a></li>
|
|
@@ -73,6 +74,12 @@ const base = import.meta.env.BASE_URL;
|
|
|
73
74
|
<option value="">All convoys</option>
|
|
74
75
|
</select>
|
|
75
76
|
</div>
|
|
77
|
+
<div class="filter-group">
|
|
78
|
+
<label class="filter-label" for="filter-pipeline">Pipeline</label>
|
|
79
|
+
<select class="filter-select" id="filter-pipeline">
|
|
80
|
+
<option value="">All</option>
|
|
81
|
+
</select>
|
|
82
|
+
</div>
|
|
76
83
|
<button class="dash-btn dash-btn--ghost filter-reset" id="filter-reset" type="button">Reset</button>
|
|
77
84
|
</div>
|
|
78
85
|
|
|
@@ -86,6 +93,16 @@ const base = import.meta.env.BASE_URL;
|
|
|
86
93
|
</div>
|
|
87
94
|
</section>
|
|
88
95
|
|
|
96
|
+
<!-- Convoy Pipeline (Chaining) Section -->
|
|
97
|
+
<section class="chart-card" id="convoy-pipeline-section" data-nav-section style="display:none">
|
|
98
|
+
<div class="chart-card__header">
|
|
99
|
+
<h2 class="chart-card__title">Convoy Pipeline</h2>
|
|
100
|
+
<p class="chart-card__desc" id="convoy-pipeline-desc">Pipeline convoy chain progress</p>
|
|
101
|
+
</div>
|
|
102
|
+
<div class="chart-card__body" id="convoy-pipeline-body">
|
|
103
|
+
</div>
|
|
104
|
+
</section>
|
|
105
|
+
|
|
89
106
|
<!-- KPI Row -->
|
|
90
107
|
<section class="kpi-row" id="kpi-row" data-nav-section>
|
|
91
108
|
<div class="kpi-card" id="kpi-sessions">
|
|
@@ -1178,6 +1195,7 @@ const base = import.meta.env.BASE_URL;
|
|
|
1178
1195
|
let rawPanels = [];
|
|
1179
1196
|
let rawReviews = [];
|
|
1180
1197
|
let rawConvoys = [];
|
|
1198
|
+
let rawPipelines = [];
|
|
1181
1199
|
|
|
1182
1200
|
function applyFilters() {
|
|
1183
1201
|
const dateFrom = document.getElementById('filter-date-from').value;
|
|
@@ -1185,6 +1203,7 @@ const base = import.meta.env.BASE_URL;
|
|
|
1185
1203
|
const agentFilter = document.getElementById('filter-agent').value;
|
|
1186
1204
|
const outcomeFilter = document.getElementById('filter-outcome').value;
|
|
1187
1205
|
const convoyFilter = document.getElementById('filter-convoy').value;
|
|
1206
|
+
const pipelineFilter = document.getElementById('filter-pipeline')?.value || '';
|
|
1188
1207
|
|
|
1189
1208
|
function matchDate(ts) {
|
|
1190
1209
|
const date = ts.slice(0, 10);
|
|
@@ -1214,6 +1233,18 @@ const base = import.meta.env.BASE_URL;
|
|
|
1214
1233
|
return true;
|
|
1215
1234
|
});
|
|
1216
1235
|
|
|
1236
|
+
// Pipeline filter: restrict events to convoy_ids within the selected pipeline
|
|
1237
|
+
if (pipelineFilter) {
|
|
1238
|
+
const activePipeline = rawPipelines.find((p) => p.id === pipelineFilter);
|
|
1239
|
+
const pipelineConvoyIds = new Set((activePipeline && activePipeline.convoy_ids) || []);
|
|
1240
|
+
if (pipelineConvoyIds.size > 0) {
|
|
1241
|
+
sessions = sessions.filter((s) => !s.convoy_id || pipelineConvoyIds.has(s.convoy_id));
|
|
1242
|
+
delegations = delegations.filter((d) => !d.convoy_id || pipelineConvoyIds.has(d.convoy_id));
|
|
1243
|
+
panels = panels.filter((p2) => !p2.convoy_id || pipelineConvoyIds.has(p2.convoy_id));
|
|
1244
|
+
reviews = reviews.filter((r) => !r.convoy_id || pipelineConvoyIds.has(r.convoy_id));
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1217
1248
|
if (convoyFilter) {
|
|
1218
1249
|
sessions = sessions.filter((s) => s.convoy_id === convoyFilter);
|
|
1219
1250
|
delegations = delegations.filter((d) => d.convoy_id === convoyFilter);
|
|
@@ -1230,6 +1261,17 @@ const base = import.meta.env.BASE_URL;
|
|
|
1230
1261
|
}
|
|
1231
1262
|
}
|
|
1232
1263
|
|
|
1264
|
+
// Show/hide convoy pipeline section based on pipeline filter
|
|
1265
|
+
const pipelineSectionEl = document.getElementById('convoy-pipeline-section');
|
|
1266
|
+
if (pipelineSectionEl) {
|
|
1267
|
+
if (pipelineFilter) {
|
|
1268
|
+
const activePipeline = rawPipelines.find((p) => p.id === pipelineFilter);
|
|
1269
|
+
renderConvoyPipeline(activePipeline, rawConvoys);
|
|
1270
|
+
} else {
|
|
1271
|
+
pipelineSectionEl.style.display = 'none';
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1233
1275
|
renderAll(sessions, delegations, panels, reviews);
|
|
1234
1276
|
}
|
|
1235
1277
|
|
|
@@ -1416,9 +1458,186 @@ const base = import.meta.env.BASE_URL;
|
|
|
1416
1458
|
bodyEl.innerHTML = html;
|
|
1417
1459
|
}
|
|
1418
1460
|
|
|
1461
|
+
// ── Pipeline Filter Population ───────────────────────────
|
|
1462
|
+
|
|
1463
|
+
function populatePipelineFilter(pipelines) {
|
|
1464
|
+
const select = document.getElementById('filter-pipeline');
|
|
1465
|
+
if (!select) return;
|
|
1466
|
+
while (select.options.length > 1) select.remove(1);
|
|
1467
|
+
const sorted = pipelines.slice().sort((a, b) =>
|
|
1468
|
+
(b.created_at || '').localeCompare(a.created_at || '')
|
|
1469
|
+
);
|
|
1470
|
+
sorted.forEach((p) => {
|
|
1471
|
+
const opt = document.createElement('option');
|
|
1472
|
+
opt.value = p.id;
|
|
1473
|
+
opt.textContent = (p.name || p.id) + ' (' + (p.status || 'unknown') + ')';
|
|
1474
|
+
select.appendChild(opt);
|
|
1475
|
+
});
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
// ── Convoy Pipeline (Chaining) Render ────────────────────
|
|
1479
|
+
|
|
1480
|
+
function renderConvoyPipeline(pipeline, convoys) {
|
|
1481
|
+
const sectionEl = document.getElementById('convoy-pipeline-section');
|
|
1482
|
+
const descEl = document.getElementById('convoy-pipeline-desc');
|
|
1483
|
+
const bodyEl = document.getElementById('convoy-pipeline-body');
|
|
1484
|
+
if (!sectionEl || !bodyEl) return;
|
|
1485
|
+
|
|
1486
|
+
if (!pipeline) {
|
|
1487
|
+
sectionEl.style.display = 'none';
|
|
1488
|
+
return;
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
sectionEl.style.display = '';
|
|
1492
|
+
if (descEl) {
|
|
1493
|
+
descEl.textContent =
|
|
1494
|
+
(pipeline.name || pipeline.id) + ' \u2014 ' + (pipeline.branch || 'no branch');
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
const convoyIds = pipeline.convoy_ids || [];
|
|
1498
|
+
const pipelineConvoys = convoyIds
|
|
1499
|
+
.map((id) => convoys.find((c) => c.id === id))
|
|
1500
|
+
.filter(Boolean);
|
|
1501
|
+
|
|
1502
|
+
const total = pipelineConvoys.length;
|
|
1503
|
+
const done = pipelineConvoys.filter((c) => c.status === 'done').length;
|
|
1504
|
+
const failed = pipelineConvoys.filter(
|
|
1505
|
+
(c) => c.status === 'failed' || c.status === 'gate-failed'
|
|
1506
|
+
).length;
|
|
1507
|
+
const totalTasks = pipelineConvoys.reduce((sum, c) => {
|
|
1508
|
+
const s = c.summary || {};
|
|
1509
|
+
return sum + (s.total || (c.tasks ? c.tasks.length : 0));
|
|
1510
|
+
}, 0);
|
|
1511
|
+
const doneTasks = pipelineConvoys.reduce((sum, c) => {
|
|
1512
|
+
const s = c.summary || {};
|
|
1513
|
+
return sum + (s.done || 0);
|
|
1514
|
+
}, 0);
|
|
1515
|
+
const totalTokens = pipelineConvoys.reduce((sum, c) => sum + (c.total_tokens || 0), 0);
|
|
1516
|
+
|
|
1517
|
+
const pct =
|
|
1518
|
+
totalTasks > 0
|
|
1519
|
+
? Math.round((doneTasks / totalTasks) * 100)
|
|
1520
|
+
: total > 0
|
|
1521
|
+
? Math.round((done / total) * 100)
|
|
1522
|
+
: 0;
|
|
1523
|
+
|
|
1524
|
+
const statusClass =
|
|
1525
|
+
pipeline.status === 'done'
|
|
1526
|
+
? 'success'
|
|
1527
|
+
: pipeline.status === 'failed' || pipeline.status === 'gate-failed'
|
|
1528
|
+
? 'failed'
|
|
1529
|
+
: pipeline.status === 'running'
|
|
1530
|
+
? 'partial'
|
|
1531
|
+
: '';
|
|
1532
|
+
|
|
1533
|
+
let html = '<div class="convoy-overview">';
|
|
1534
|
+
html +=
|
|
1535
|
+
'<div class="convoy-stat"><span class="convoy-stat__label">Status</span>' +
|
|
1536
|
+
'<span class="outcome-badge outcome-badge--' + statusClass + '">' +
|
|
1537
|
+
escapeHtml(pipeline.status || 'unknown') + '</span></div>';
|
|
1538
|
+
html +=
|
|
1539
|
+
'<div class="convoy-stat"><span class="convoy-stat__label">Branch</span>' +
|
|
1540
|
+
'<span class="convoy-stat__value">' + escapeHtml(pipeline.branch || '\u2014') + '</span></div>';
|
|
1541
|
+
html +=
|
|
1542
|
+
'<div class="convoy-stat"><span class="convoy-stat__label">Convoys</span>' +
|
|
1543
|
+
'<span class="convoy-stat__value">' + done + '/' + total + '</span></div>';
|
|
1544
|
+
if (totalTasks > 0) {
|
|
1545
|
+
html +=
|
|
1546
|
+
'<div class="convoy-stat"><span class="convoy-stat__label">Tasks</span>' +
|
|
1547
|
+
'<span class="convoy-stat__value">' + doneTasks + '/' + totalTasks + '</span></div>';
|
|
1548
|
+
}
|
|
1549
|
+
if (totalTokens > 0) {
|
|
1550
|
+
html +=
|
|
1551
|
+
'<div class="convoy-stat"><span class="convoy-stat__label">Tokens</span>' +
|
|
1552
|
+
'<span class="convoy-stat__value">' + formatTokens(totalTokens) + '</span></div>';
|
|
1553
|
+
}
|
|
1554
|
+
html +=
|
|
1555
|
+
'<div class="convoy-stat"><span class="convoy-stat__label">Started</span>' +
|
|
1556
|
+
'<span class="convoy-stat__value">' +
|
|
1557
|
+
(pipeline.started_at ? formatTime(pipeline.started_at) : '\u2014') + '</span></div>';
|
|
1558
|
+
if (pipeline.finished_at) {
|
|
1559
|
+
html +=
|
|
1560
|
+
'<div class="convoy-stat"><span class="convoy-stat__label">Finished</span>' +
|
|
1561
|
+
'<span class="convoy-stat__value">' + formatTime(pipeline.finished_at) + '</span></div>';
|
|
1562
|
+
}
|
|
1563
|
+
html += '</div>';
|
|
1564
|
+
|
|
1565
|
+
// Convoy chain visualization
|
|
1566
|
+
html += '<div class="convoy-chain">';
|
|
1567
|
+
pipelineConvoys.forEach((convoy, i) => {
|
|
1568
|
+
const cs = convoy.summary || {};
|
|
1569
|
+
const cDone = cs.done || 0;
|
|
1570
|
+
const cTotal = cs.total || (convoy.tasks ? convoy.tasks.length : 0);
|
|
1571
|
+
const cTokens = convoy.total_tokens || 0;
|
|
1572
|
+
const isActive =
|
|
1573
|
+
(pipeline.current_convoy_id && pipeline.current_convoy_id === convoy.id) ||
|
|
1574
|
+
convoy.status === 'running';
|
|
1575
|
+
const nodeStatusClass =
|
|
1576
|
+
convoy.status === 'done'
|
|
1577
|
+
? 'done'
|
|
1578
|
+
: convoy.status === 'failed' || convoy.status === 'gate-failed'
|
|
1579
|
+
? 'failed'
|
|
1580
|
+
: isActive
|
|
1581
|
+
? 'active'
|
|
1582
|
+
: 'pending';
|
|
1583
|
+
const badgeClass =
|
|
1584
|
+
convoy.status === 'done'
|
|
1585
|
+
? 'success'
|
|
1586
|
+
: convoy.status === 'failed' || convoy.status === 'gate-failed'
|
|
1587
|
+
? 'failed'
|
|
1588
|
+
: convoy.status === 'running'
|
|
1589
|
+
? 'partial'
|
|
1590
|
+
: '';
|
|
1591
|
+
|
|
1592
|
+
if (i > 0) {
|
|
1593
|
+
html += '<div class="convoy-chain__connector">\u2192</div>';
|
|
1594
|
+
}
|
|
1595
|
+
html +=
|
|
1596
|
+
'<div class="convoy-chain__node convoy-chain__node--' + nodeStatusClass +
|
|
1597
|
+
'" data-convoy-id="' + escapeHtml(convoy.id) + '" title="Click to filter to this convoy">';
|
|
1598
|
+
html += '<div class="convoy-chain__node-name">' + escapeHtml(convoy.name || convoy.id) + '</div>';
|
|
1599
|
+
html +=
|
|
1600
|
+
'<span class="outcome-badge outcome-badge--' + badgeClass + '">' +
|
|
1601
|
+
escapeHtml(convoy.status) + '</span>';
|
|
1602
|
+
if (cTotal > 0) {
|
|
1603
|
+
html += '<div class="convoy-chain__node-meta">' + cDone + '/' + cTotal + ' tasks</div>';
|
|
1604
|
+
}
|
|
1605
|
+
if (cTokens > 0) {
|
|
1606
|
+
html += '<div class="convoy-chain__node-meta">' + formatTokens(cTokens) + ' tokens</div>';
|
|
1607
|
+
}
|
|
1608
|
+
html += '</div>';
|
|
1609
|
+
});
|
|
1610
|
+
html += '</div>';
|
|
1611
|
+
|
|
1612
|
+
// Progress bar
|
|
1613
|
+
html += '<div class="convoy-progress">';
|
|
1614
|
+
html +=
|
|
1615
|
+
'<div class="convoy-progress__bar">' +
|
|
1616
|
+
'<div class="convoy-progress__fill" style="width:' + pct + '%"></div></div>';
|
|
1617
|
+
html +=
|
|
1618
|
+
'<span class="convoy-progress__label">' + pct + '% complete' +
|
|
1619
|
+
(failed > 0 ? ' \u00B7 ' + failed + ' failed' : '') + '</span>';
|
|
1620
|
+
html += '</div>';
|
|
1621
|
+
|
|
1622
|
+
bodyEl.innerHTML = html;
|
|
1623
|
+
|
|
1624
|
+
// Click handlers for convoy drill-down
|
|
1625
|
+
bodyEl.querySelectorAll('.convoy-chain__node').forEach((node) => {
|
|
1626
|
+
node.addEventListener('click', () => {
|
|
1627
|
+
const convoyId = node.dataset.convoyId;
|
|
1628
|
+
const sel = document.getElementById('filter-convoy');
|
|
1629
|
+
if (sel && convoyId) {
|
|
1630
|
+
sel.value = convoyId;
|
|
1631
|
+
applyFilters();
|
|
1632
|
+
}
|
|
1633
|
+
});
|
|
1634
|
+
});
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1419
1637
|
async function main() {
|
|
1420
1638
|
const events = await loadNdjson(base + 'data/events.ndjson');
|
|
1421
1639
|
const convoys = await loadNdjson(base + 'data/convoys.ndjson');
|
|
1640
|
+
const pipelines = await loadNdjson(base + 'data/pipelines.ndjson');
|
|
1422
1641
|
|
|
1423
1642
|
const sessions = events.filter((e) => e.type === 'session');
|
|
1424
1643
|
const delegations = events.filter((e) => e.type === 'delegation');
|
|
@@ -1430,9 +1649,11 @@ const base = import.meta.env.BASE_URL;
|
|
|
1430
1649
|
rawPanels = panels;
|
|
1431
1650
|
rawReviews = reviews;
|
|
1432
1651
|
rawConvoys = convoys;
|
|
1652
|
+
rawPipelines = pipelines;
|
|
1433
1653
|
|
|
1434
1654
|
populateAgentFilter(sessions, delegations, reviews);
|
|
1435
1655
|
populateConvoyFilter(convoys);
|
|
1656
|
+
populatePipelineFilter(pipelines);
|
|
1436
1657
|
|
|
1437
1658
|
// ── Read URL params ───────────────────────────────────
|
|
1438
1659
|
const urlParams = new URLSearchParams(window.location.search);
|
|
@@ -1461,12 +1682,14 @@ const base = import.meta.env.BASE_URL;
|
|
|
1461
1682
|
document.getElementById('filter-agent')?.addEventListener('change', applyFilters);
|
|
1462
1683
|
document.getElementById('filter-outcome')?.addEventListener('change', applyFilters);
|
|
1463
1684
|
document.getElementById('filter-convoy')?.addEventListener('change', applyFilters);
|
|
1685
|
+
document.getElementById('filter-pipeline')?.addEventListener('change', applyFilters);
|
|
1464
1686
|
document.getElementById('filter-reset')?.addEventListener('click', () => {
|
|
1465
1687
|
document.getElementById('filter-date-from').value = '';
|
|
1466
1688
|
document.getElementById('filter-date-to').value = '';
|
|
1467
1689
|
document.getElementById('filter-agent').value = '';
|
|
1468
1690
|
document.getElementById('filter-outcome').value = '';
|
|
1469
1691
|
document.getElementById('filter-convoy').value = '';
|
|
1692
|
+
document.getElementById('filter-pipeline').value = '';
|
|
1470
1693
|
applyFilters();
|
|
1471
1694
|
});
|
|
1472
1695
|
|
|
@@ -1477,15 +1700,21 @@ const base = import.meta.env.BASE_URL;
|
|
|
1477
1700
|
refreshInterval = setInterval(async () => {
|
|
1478
1701
|
const freshEvents = await loadNdjson(base + 'data/events.ndjson');
|
|
1479
1702
|
const freshConvoys = await loadNdjson(base + 'data/convoys.ndjson');
|
|
1703
|
+
const freshPipelines = await loadNdjson(base + 'data/pipelines.ndjson');
|
|
1480
1704
|
rawSessions = freshEvents.filter((e) => e.type === 'session');
|
|
1481
1705
|
rawDelegations = freshEvents.filter((e) => e.type === 'delegation');
|
|
1482
1706
|
rawPanels = freshEvents.filter((e) => e.type === 'panel');
|
|
1483
1707
|
rawReviews = freshEvents.filter((e) => e.type === 'review');
|
|
1484
1708
|
rawConvoys = freshConvoys;
|
|
1709
|
+
rawPipelines = freshPipelines;
|
|
1485
1710
|
const currentValue = document.getElementById('filter-convoy')?.value;
|
|
1711
|
+
const currentPipelineValue = document.getElementById('filter-pipeline')?.value;
|
|
1486
1712
|
populateConvoyFilter(freshConvoys);
|
|
1713
|
+
populatePipelineFilter(freshPipelines);
|
|
1487
1714
|
const sel = document.getElementById('filter-convoy');
|
|
1488
1715
|
if (sel && currentValue) sel.value = currentValue;
|
|
1716
|
+
const pSel = document.getElementById('filter-pipeline');
|
|
1717
|
+
if (pSel && currentPipelineValue) pSel.value = currentPipelineValue;
|
|
1489
1718
|
applyFilters();
|
|
1490
1719
|
}, 5000);
|
|
1491
1720
|
}
|
|
@@ -1593,3 +1593,119 @@ body {
|
|
|
1593
1593
|
.convoy-tasks {
|
|
1594
1594
|
margin-top: 8px;
|
|
1595
1595
|
}
|
|
1596
|
+
|
|
1597
|
+
/* ---------- Convoy Chain / Pipeline Section ---------- */
|
|
1598
|
+
.convoy-chain {
|
|
1599
|
+
display: flex;
|
|
1600
|
+
align-items: stretch;
|
|
1601
|
+
gap: 0;
|
|
1602
|
+
overflow-x: auto;
|
|
1603
|
+
padding: 1rem 0 1.5rem;
|
|
1604
|
+
scrollbar-width: thin;
|
|
1605
|
+
scrollbar-color: var(--border-color) transparent;
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
.convoy-chain::-webkit-scrollbar {
|
|
1609
|
+
height: 4px;
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
.convoy-chain::-webkit-scrollbar-track {
|
|
1613
|
+
background: transparent;
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
.convoy-chain::-webkit-scrollbar-thumb {
|
|
1617
|
+
background: var(--border-color);
|
|
1618
|
+
border-radius: 2px;
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
.convoy-chain__connector {
|
|
1622
|
+
display: flex;
|
|
1623
|
+
align-items: center;
|
|
1624
|
+
padding: 0 0.5rem;
|
|
1625
|
+
color: var(--text-tertiary);
|
|
1626
|
+
font-size: 1.1rem;
|
|
1627
|
+
flex-shrink: 0;
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
.convoy-chain__node {
|
|
1631
|
+
display: flex;
|
|
1632
|
+
flex-direction: column;
|
|
1633
|
+
align-items: center;
|
|
1634
|
+
gap: 6px;
|
|
1635
|
+
padding: 12px 16px;
|
|
1636
|
+
background: var(--bg-tertiary);
|
|
1637
|
+
border: 1px solid var(--border-color);
|
|
1638
|
+
border-radius: 10px;
|
|
1639
|
+
min-width: 140px;
|
|
1640
|
+
cursor: pointer;
|
|
1641
|
+
transition:
|
|
1642
|
+
background var(--transition-fast),
|
|
1643
|
+
border-color var(--transition-fast),
|
|
1644
|
+
transform var(--transition-fast),
|
|
1645
|
+
box-shadow var(--transition-fast);
|
|
1646
|
+
flex-shrink: 0;
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
.convoy-chain__node:hover {
|
|
1650
|
+
background: var(--bg-card-hover);
|
|
1651
|
+
transform: translateY(-2px);
|
|
1652
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
.convoy-chain__node-name {
|
|
1656
|
+
font-size: 0.8rem;
|
|
1657
|
+
font-weight: 600;
|
|
1658
|
+
color: var(--text-primary);
|
|
1659
|
+
text-align: center;
|
|
1660
|
+
word-break: break-word;
|
|
1661
|
+
max-width: 120px;
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
.convoy-chain__node-meta {
|
|
1665
|
+
font-size: 0.72rem;
|
|
1666
|
+
color: var(--text-tertiary);
|
|
1667
|
+
text-align: center;
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
.convoy-chain__node--active {
|
|
1671
|
+
border-color: var(--accent-purple);
|
|
1672
|
+
box-shadow: 0 0 0 1px var(--accent-purple), 0 0 12px rgba(167, 139, 250, 0.2);
|
|
1673
|
+
animation: convoy-pulse 2s ease-in-out infinite;
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
.convoy-chain__node--done {
|
|
1677
|
+
border-color: rgba(34, 197, 94, 0.3);
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
.convoy-chain__node--failed {
|
|
1681
|
+
border-color: rgba(239, 68, 68, 0.3);
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
.convoy-chain__node--pending {
|
|
1685
|
+
opacity: 0.6;
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
@keyframes convoy-pulse {
|
|
1689
|
+
0%, 100% {
|
|
1690
|
+
box-shadow: 0 0 0 1px var(--accent-purple), 0 0 8px rgba(167, 139, 250, 0.15);
|
|
1691
|
+
}
|
|
1692
|
+
50% {
|
|
1693
|
+
box-shadow: 0 0 0 1px var(--accent-purple), 0 0 18px rgba(167, 139, 250, 0.35);
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
@media (max-width: 768px) {
|
|
1698
|
+
.convoy-chain {
|
|
1699
|
+
flex-wrap: wrap;
|
|
1700
|
+
gap: 8px;
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
.convoy-chain__connector {
|
|
1704
|
+
display: none;
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
.convoy-chain__node {
|
|
1708
|
+
flex: 1 1 calc(50% - 4px);
|
|
1709
|
+
min-width: 120px;
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
@@ -14,7 +14,7 @@ Tracked issues, limitations, and accepted risks discovered during agent sessions
|
|
|
14
14
|
|
|
15
15
|
| Issue ID | Status | Severity | Summary | Evidence | Root Cause | Solution Options |
|
|
16
16
|
|----------|--------|----------|---------|----------|------------|------------------|
|
|
17
|
-
| KI-001 | Open | Medium |
|
|
17
|
+
| KI-001 | Open | Medium | Convoy engine run()/resume() don't catch unexpected errors from runConvoy() — convoy DB records can get stuck in 'running' status | `src/cli/convoy/engine.ts` lines 452-510 (run) and 520-570 (resume): if `runConvoy()` throws, the convoy record is never updated to 'failed' | The try/finally block exports and closes the store but doesn't catch to update convoy status | Add a catch block before finally that calls `store.updateConvoyStatus(convoyId, 'failed', ...)` before rethrowing |
|
|
18
18
|
|
|
19
19
|
### Status Values
|
|
20
20
|
|