@yemi33/minions 0.1.2176 → 0.1.2178
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/js/refresh.js +8 -0
- package/dashboard/js/render-dispatch.js +92 -0
- package/dashboard/js/render-plans.js +82 -13
- package/dashboard/js/settings.js +100 -11
- package/dashboard/js/utils.js +8 -5
- package/dashboard/layout.html +6 -0
- package/dashboard/slim/body.html +12 -9
- package/dashboard/slim/js/command-send.js +5 -1
- package/dashboard/slim/js/modals-tiles.js +89 -7
- package/dashboard/slim/js/projects.js +36 -28
- package/dashboard/slim/js/status.js +52 -4
- package/dashboard/slim/styles.css +165 -20
- package/dashboard/styles.css +39 -0
- package/dashboard.js +250 -6
- package/docs/README.md +8 -1
- package/docs/auto-discovery.md +40 -0
- package/docs/cross-repo-plans.md +292 -0
- package/docs/deprecated.json +4 -4
- package/docs/pr-auto-fix-dispatch.md +64 -0
- package/docs/pr-review-fix-loop.md +1 -1
- package/docs/watches.md +1 -0
- package/engine/ado.js +1 -10
- package/engine/dispatch.js +53 -0
- package/engine/lifecycle.js +44 -190
- package/engine/meeting.js +30 -0
- package/engine/playbook.js +15 -0
- package/engine/queries.js +26 -1
- package/engine/runtimes/copilot.js +19 -0
- package/engine/shared.js +190 -1
- package/engine.js +531 -112
- package/package.json +1 -1
- package/playbooks/plan-to-prd.md +25 -2
- package/playbooks/plan.md +4 -2
package/dashboard/js/refresh.js
CHANGED
|
@@ -143,6 +143,7 @@ const RENDER_VERSIONS = {
|
|
|
143
143
|
version: 1,
|
|
144
144
|
adoThrottle: 1,
|
|
145
145
|
ghThrottle: 1,
|
|
146
|
+
pausedBanner: 1,
|
|
146
147
|
dispatch: 2,
|
|
147
148
|
engineLog: 2,
|
|
148
149
|
metrics: 1,
|
|
@@ -713,6 +714,13 @@ function _processStatusUpdate(data, opts) {
|
|
|
713
714
|
_safeRender('adoThrottle', function() { renderAdoThrottleAlert(data.adoThrottle); });
|
|
714
715
|
_changed('ghThrottle', data.ghThrottle);
|
|
715
716
|
_safeRender('ghThrottle', function() { renderGhThrottleAlert(data.ghThrottle); });
|
|
717
|
+
// P-g7a2b4c5 — sticky kill-switch banner. Cache key is the pair of paused
|
|
718
|
+
// flags so the banner only re-renders when either flag actually flips (the
|
|
719
|
+
// full data.engine slice changes every tick because heartbeat/lastTickAt
|
|
720
|
+
// advance).
|
|
721
|
+
const _pausedCacheKey = (data.engine && (data.engine.pollingPaused ? 'p' : '-') + (data.engine.autoFixPaused ? 'a' : '-')) || '--';
|
|
722
|
+
_changed('pausedBanner', _pausedCacheKey);
|
|
723
|
+
_safeRender('pausedBanner', function() { renderPausedBanner(data.engine); });
|
|
716
724
|
// Dispatch now comes from /api/dispatch — a dedicated fresh-JSON
|
|
717
725
|
// endpoint that re-runs getDispatchQueue() server-side on every
|
|
718
726
|
// request (issue #2949). Completion-report sidecars are now loaded
|
|
@@ -245,6 +245,98 @@ function renderGhThrottleAlert(ghThrottle) {
|
|
|
245
245
|
el.style.display = 'flex';
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
+
// P-g7a2b4c5 — Sticky cross-page kill-switch banner. Renders whenever either
|
|
249
|
+
// engine.pollingPaused or engine.autoFixPaused is true, with per-row Resume
|
|
250
|
+
// buttons that POST to the convenience endpoints from P-f3c9d0e7. Built with
|
|
251
|
+
// the DOM API (no innerHTML) so the eslint-plugin-no-unsanitized gate passes
|
|
252
|
+
// cleanly — banner content is system-controlled but follows the project
|
|
253
|
+
// convention of avoiding innerHTML in new code anyway.
|
|
254
|
+
function renderPausedBanner(engine) {
|
|
255
|
+
const el = document.getElementById('paused-banner');
|
|
256
|
+
if (!el) return;
|
|
257
|
+
const pollingPaused = !!(engine && engine.pollingPaused);
|
|
258
|
+
const autoFixPaused = !!(engine && engine.autoFixPaused);
|
|
259
|
+
if (!pollingPaused && !autoFixPaused) {
|
|
260
|
+
el.hidden = true;
|
|
261
|
+
while (el.firstChild) el.removeChild(el.firstChild);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Wipe + rebuild — list-of-pauses model, one row per active pause.
|
|
266
|
+
while (el.firstChild) el.removeChild(el.firstChild);
|
|
267
|
+
el.hidden = false;
|
|
268
|
+
|
|
269
|
+
const title = document.createElement('div');
|
|
270
|
+
title.className = 'paused-banner-title';
|
|
271
|
+
title.textContent = '⚠️ Some engine functions are paused:';
|
|
272
|
+
el.appendChild(title);
|
|
273
|
+
|
|
274
|
+
const list = document.createElement('ul');
|
|
275
|
+
list.className = 'paused-banner-list';
|
|
276
|
+
el.appendChild(list);
|
|
277
|
+
|
|
278
|
+
const rows = [];
|
|
279
|
+
if (pollingPaused) {
|
|
280
|
+
rows.push({
|
|
281
|
+
key: 'polling',
|
|
282
|
+
label: 'Polling',
|
|
283
|
+
detail: 'no new PR state arriving',
|
|
284
|
+
endpoint: '/api/engine/polling/resume',
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
if (autoFixPaused) {
|
|
288
|
+
rows.push({
|
|
289
|
+
key: 'autofix',
|
|
290
|
+
label: 'PR auto-fix',
|
|
291
|
+
detail: 'no new PR-triggered dispatches',
|
|
292
|
+
endpoint: '/api/engine/auto-fix/resume',
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
rows.forEach(function (row) {
|
|
297
|
+
const li = document.createElement('li');
|
|
298
|
+
li.className = 'paused-banner-row';
|
|
299
|
+
li.setAttribute('data-paused-row', row.key);
|
|
300
|
+
|
|
301
|
+
const labelSpan = document.createElement('span');
|
|
302
|
+
labelSpan.className = 'paused-banner-row-label';
|
|
303
|
+
labelSpan.textContent = '• ' + row.label;
|
|
304
|
+
li.appendChild(labelSpan);
|
|
305
|
+
|
|
306
|
+
const detailSpan = document.createElement('span');
|
|
307
|
+
detailSpan.className = 'paused-banner-row-detail';
|
|
308
|
+
detailSpan.textContent = '— ' + row.detail;
|
|
309
|
+
li.appendChild(detailSpan);
|
|
310
|
+
|
|
311
|
+
const btn = document.createElement('button');
|
|
312
|
+
btn.type = 'button';
|
|
313
|
+
btn.className = 'paused-banner-row-resume';
|
|
314
|
+
btn.textContent = 'Resume';
|
|
315
|
+
btn.setAttribute('data-paused-resume', row.key);
|
|
316
|
+
btn.addEventListener('click', function () {
|
|
317
|
+
if (btn.disabled) return;
|
|
318
|
+
btn.disabled = true;
|
|
319
|
+
btn.classList.add('clicked');
|
|
320
|
+
fetch(row.endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json' } })
|
|
321
|
+
.then(function (resp) {
|
|
322
|
+
if (!resp.ok) throw new Error('HTTP ' + resp.status);
|
|
323
|
+
if (typeof showToast === 'function') showToast('cmd-toast', row.label + ' resumed', true);
|
|
324
|
+
if (typeof refreshNow === 'function') refreshNow();
|
|
325
|
+
})
|
|
326
|
+
.catch(function (err) {
|
|
327
|
+
btn.disabled = false;
|
|
328
|
+
btn.classList.remove('clicked');
|
|
329
|
+
if (typeof showToast === 'function') {
|
|
330
|
+
showToast('cmd-toast', 'Resume failed: ' + (err && err.message ? err.message : 'unknown'), false);
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
li.appendChild(btn);
|
|
335
|
+
|
|
336
|
+
list.appendChild(li);
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
248
340
|
function renderDispatch(dispatch, opts) {
|
|
249
341
|
opts = opts || {};
|
|
250
342
|
if (!dispatch) return;
|
|
@@ -8,7 +8,10 @@ function _plansNext() { _plansPage++; refresh(); }
|
|
|
8
8
|
|
|
9
9
|
function openCreatePlanModal() {
|
|
10
10
|
const projOpts = (typeof cmdProjects !== 'undefined' ? cmdProjects : []).map(p =>
|
|
11
|
-
'<
|
|
11
|
+
'<label style="display:flex;align-items:center;gap:6px;padding:4px 8px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm);cursor:pointer;font-size:var(--text-md);color:var(--text)">' +
|
|
12
|
+
'<input type="checkbox" class="plan-new-project-cb" value="' + escapeHtml(p) + '" onchange="_updatePlanProjectHint()">' +
|
|
13
|
+
escapeHtml(p) +
|
|
14
|
+
'</label>'
|
|
12
15
|
).join('');
|
|
13
16
|
const inputStyle = 'display:block;width:100%;margin-top:4px;padding:6px 8px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--text);font-size:var(--text-md);font-family:inherit';
|
|
14
17
|
|
|
@@ -17,7 +20,11 @@ function openCreatePlanModal() {
|
|
|
17
20
|
document.getElementById('modal-body').innerHTML =
|
|
18
21
|
'<div style="display:flex;flex-direction:column;gap:10px">' +
|
|
19
22
|
'<label style="color:var(--text);font-size:var(--text-md)">Title <input id="plan-new-title" style="' + inputStyle + '" placeholder="e.g. Add user authentication with JWT"></label>' +
|
|
20
|
-
'<
|
|
23
|
+
'<div style="color:var(--text);font-size:var(--text-md)">Projects' +
|
|
24
|
+
'<div id="plan-new-project-list" style="display:flex;flex-wrap:wrap;gap:6px;margin-top:4px">' + projOpts + '</div>' +
|
|
25
|
+
'<span style="display:block;font-size:var(--text-sm);color:var(--muted);margin-top:2px">Tick one project to scope every item to that repo. Leave all unticked for plans where the agent should route each item per the PRD.</span>' +
|
|
26
|
+
'<p id="plan-new-project-hint" style="display:none;font-size:var(--text-sm);color:var(--muted);margin:4px 0 0 0;padding:6px 8px;background:var(--bg);border:1px dashed var(--border);border-radius:var(--radius-sm)">Cross-repo plan — each PRD item will route to the project you assign in the PRD.</p>' +
|
|
27
|
+
'</div>' +
|
|
21
28
|
'<label style="color:var(--text);font-size:var(--text-md)">Plan Content <textarea id="plan-new-content" rows="12" style="' + inputStyle + ';resize:vertical;font-family:monospace;font-size:var(--text-md)" placeholder="Write your plan in markdown...\n\nDescribe what needs to be built, the approach, requirements, and any constraints.\n\nThe squad will convert this into a PRD with structured work items."></textarea></label>' +
|
|
22
29
|
'<div style="font-size:var(--text-base);color:var(--muted)">After creating, click Execute on the plan card to have an agent convert it into a PRD with work items.</div>' +
|
|
23
30
|
'<div style="display:flex;justify-content:flex-end;gap:8px;margin-top:4px">' +
|
|
@@ -29,13 +36,34 @@ function openCreatePlanModal() {
|
|
|
29
36
|
setTimeout(() => document.getElementById('plan-new-title')?.focus(), 100);
|
|
30
37
|
}
|
|
31
38
|
|
|
39
|
+
// Toggle the cross-repo hint under the project picker. Visible only when
|
|
40
|
+
// ≥2 boxes are checked — a single check is still a regular single-project
|
|
41
|
+
// plan, so the hint would be misleading.
|
|
42
|
+
function _updatePlanProjectHint() {
|
|
43
|
+
const boxes = document.querySelectorAll('.plan-new-project-cb');
|
|
44
|
+
let checkedCount = 0;
|
|
45
|
+
for (const cb of boxes) { if (cb.checked) checkedCount++; }
|
|
46
|
+
const hint = document.getElementById('plan-new-project-hint');
|
|
47
|
+
if (hint) hint.style.display = checkedCount >= 2 ? 'block' : 'none';
|
|
48
|
+
}
|
|
49
|
+
|
|
32
50
|
async function _submitCreatePlan(e) {
|
|
33
51
|
var btn = (e || window.event)?.target; if (btn) { btn.disabled = true; btn.textContent = 'Creating...'; }
|
|
34
52
|
const title = document.getElementById('plan-new-title')?.value?.trim();
|
|
35
53
|
const content = document.getElementById('plan-new-content')?.value?.trim();
|
|
36
54
|
if (!title) { if (btn) { btn.disabled = false; btn.textContent = 'Create Plan'; } alert('Title is required'); return; }
|
|
37
55
|
if (!content) { if (btn) { btn.disabled = false; btn.textContent = 'Create Plan'; } alert('Plan content is required'); return; }
|
|
38
|
-
|
|
56
|
+
// Collect every checked .plan-new-project-cb into a name array. Server
|
|
57
|
+
// accepts string OR array (P-2e9b54d1) but we preserve today's contract
|
|
58
|
+
// for the 0/1-selected cases: 0 → '' (omit-equivalent), 1 → string,
|
|
59
|
+
// ≥2 → array. Order follows DOM order of the checkboxes.
|
|
60
|
+
const boxes = document.querySelectorAll('.plan-new-project-cb');
|
|
61
|
+
const selected = [];
|
|
62
|
+
for (const cb of boxes) { if (cb.checked) selected.push(cb.value); }
|
|
63
|
+
let project;
|
|
64
|
+
if (selected.length === 0) project = '';
|
|
65
|
+
else if (selected.length === 1) project = selected[0];
|
|
66
|
+
else if (selected.length > 1) project = selected;
|
|
39
67
|
|
|
40
68
|
try {
|
|
41
69
|
const res = await fetch('/api/plans/create', {
|
|
@@ -313,8 +341,8 @@ function renderPlans(plans) {
|
|
|
313
341
|
const showPause = effectiveStatus === 'dispatched' && prdFile && !isArchived;
|
|
314
342
|
// Resume pill not needed — paused state is handled by the actions block above
|
|
315
343
|
const showResume = false;
|
|
316
|
-
const
|
|
317
|
-
const hasVerifyWi =
|
|
344
|
+
const verifyWis = allWi.filter(w => w.itemType === 'verify' && w.sourcePlan === prdFile);
|
|
345
|
+
const hasVerifyWi = verifyWis.length > 0;
|
|
318
346
|
const showVerify = effectiveStatus === 'completed' && prdFile && !isArchived && !hasVerifyWi;
|
|
319
347
|
const pauseBtn = showPause ? '<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:2px 8px;color:var(--yellow)" ' +
|
|
320
348
|
'onclick="event.stopPropagation();planPause(\'' + escapeHtml(prdFile) + '\',this)">Pause</button>' : '';
|
|
@@ -338,17 +366,48 @@ function renderPlans(plans) {
|
|
|
338
366
|
const versionBadge = p.version ? ' <span style="font-size:var(--text-xs);font-weight:700;padding:1px 5px;border-radius:3px;background:rgba(56,139,253,0.15);color:var(--blue);vertical-align:middle">v' + p.version + '</span>' : '';
|
|
339
367
|
const statusColors = { 'completed': 'var(--green)', 'dispatched': 'var(--blue)', 'converting': 'var(--yellow)', 'paused': 'var(--muted)', 'awaiting-approval': 'var(--yellow)', 'approved': 'var(--green)', 'rejected': 'var(--red)', 'has-failures': 'var(--red)', 'revision-requested': 'var(--purple,#a855f7)', 'active': 'var(--muted)' };
|
|
340
368
|
const cardClass = effectiveStatus === 'dispatched' || effectiveStatus === 'converting' ? 'working' : effectiveStatus === 'awaiting-approval' || effectiveStatus === 'paused' ? 'awaiting' : effectiveStatus;
|
|
369
|
+
// P-e8d49105 — cross-repo plans surface every touched project as its
|
|
370
|
+
// own badge. Single-project plans (and old PRDs without _projects)
|
|
371
|
+
// fall back to the legacy `p.project` plain-text span so existing
|
|
372
|
+
// visuals stay identical.
|
|
373
|
+
const projectsRollup = Array.isArray(p._projects) ? p._projects : [];
|
|
374
|
+
const projectMeta = projectsRollup.length >= 2
|
|
375
|
+
? projectsRollup.map(function(pn) { return '<span class="prd-project-badge">' + escapeHtml(pn) + '</span>'; }).join(' ')
|
|
376
|
+
: (p.project ? '<span>' + escapeHtml(p.project) + '</span>' : (projectsRollup.length === 1 ? '<span>' + escapeHtml(projectsRollup[0]) + '</span>' : ''));
|
|
377
|
+
// P-66b1faec — when a plan touches >= 2 projects, surface a tiny pill
|
|
378
|
+
// per project in the meta line: `<name> <complete>/<total>` plus ✓ when
|
|
379
|
+
// all items in that project are done, ⏳ otherwise. Single-project plans
|
|
380
|
+
// (or PRD records that omit the rollup, e.g. MD drafts) render nothing
|
|
381
|
+
// extra so the existing meta line is visually unchanged.
|
|
382
|
+
const perProjectProgress = (p && p._perProjectProgress && typeof p._perProjectProgress === 'object')
|
|
383
|
+
? p._perProjectProgress : {};
|
|
384
|
+
const perProjectKeys = Object.keys(perProjectProgress);
|
|
385
|
+
const perProjectPills = perProjectKeys.length >= 2
|
|
386
|
+
? perProjectKeys.map(function(pn) {
|
|
387
|
+
const entry = perProjectProgress[pn] || { complete: 0, total: 0 };
|
|
388
|
+
const complete = Number(entry.complete) || 0;
|
|
389
|
+
const total = Number(entry.total) || 0;
|
|
390
|
+
const isComplete = total > 0 && complete === total;
|
|
391
|
+
const tail = isComplete ? ' ✓' : ' ⏳';
|
|
392
|
+
const color = isComplete ? 'var(--green)' : 'var(--muted)';
|
|
393
|
+
return '<span title="' + escapeHtml(pn) + ': ' + complete + '/' + total +
|
|
394
|
+
(isComplete ? ' complete' : ' in progress') + '" ' +
|
|
395
|
+
'style="font-size:var(--text-xs);font-weight:600;padding:1px 6px;border-radius:3px;background:rgba(110,118,129,0.15);color:' + color + '">' +
|
|
396
|
+
escapeHtml(pn) + ' ' + complete + '/' + total + tail + '</span>';
|
|
397
|
+
}).join(' ')
|
|
398
|
+
: '';
|
|
341
399
|
return '<div class="plan-card ' + cardClass + '" data-file="plans/' + escapeHtml(p.file) + '" style="cursor:pointer' + (isArchived ? ';opacity:0.7' : '') + '" onclick="if(shouldIgnoreSelectionClick(event))return;planView(\'' + escapeHtml(p.file) + '\')">' +
|
|
342
400
|
'<div class="plan-card-header">' +
|
|
343
401
|
'<div><div class="plan-card-title">' + escapeHtml(p.summary || p.file) + versionBadge + '</div>' +
|
|
344
402
|
'<div class="plan-card-meta">' +
|
|
345
403
|
'<span style="font-weight:600;color:' + (statusColors[effectiveStatus] || 'var(--muted)') + '">' + label + '</span>' +
|
|
346
|
-
|
|
404
|
+
projectMeta +
|
|
347
405
|
'<span>' + p.itemCount + ' items</span>' +
|
|
406
|
+
perProjectPills +
|
|
348
407
|
(p.updatedAt ? '<span title="Last updated: ' + p.updatedAt + '">Updated ' + timeAgo(p.updatedAt) + '</span>' : '') +
|
|
349
408
|
(p.completedAt ? '<span>' + p.completedAt.slice(0, 10) + '</span>' : '') +
|
|
350
409
|
(p.generatedBy ? '<span>by ' + escapeHtml(p.generatedBy) + '</span>' : '') +
|
|
351
|
-
executeBtn + pauseBtn + resumeBtn + verifyBtn + (hasVerifyWi ? _renderVerifyBadge(
|
|
410
|
+
executeBtn + pauseBtn + resumeBtn + verifyBtn + (verifyWis.length >= 2 ? verifyWis.map(v => _renderVerifyBadge(v, { projectLabel: v.project || '' })).join(' ') : (hasVerifyWi ? _renderVerifyBadge(verifyWis[0]) : '')) + archiveReadyBadge + archiveBtn + deleteBtn +
|
|
352
411
|
'</div>' +
|
|
353
412
|
'</div>' +
|
|
354
413
|
'</div>' +
|
|
@@ -620,12 +679,18 @@ function _renderPlanModal(normalizedFile, raw, lastMod) {
|
|
|
620
679
|
if (effectiveStatus === 'dispatched') {
|
|
621
680
|
modalActions += '<span style="' + bs + ';color:var(--blue)">In Progress</span> ';
|
|
622
681
|
}
|
|
623
|
-
// Verify / Verified badge
|
|
624
|
-
|
|
625
|
-
|
|
682
|
+
// Verify / Verified badge — cross-repo plans fan out one verify WI per
|
|
683
|
+
// project (engine/lifecycle.js); collect them all and render one badge
|
|
684
|
+
// per project so operators can see every repo's verify status.
|
|
685
|
+
const modalVerifyWis = (window._lastWorkItems || []).filter(w => w.itemType === 'verify' && w.sourcePlan === (prdFile || normalizedFile));
|
|
686
|
+
if (effectiveStatus === 'completed' && prdFile && !isArchived && modalVerifyWis.length === 0) {
|
|
626
687
|
modalActions += '<button class="pr-pager-btn" style="' + bs + ';color:var(--green)" onclick="triggerVerify(\'' + escapeHtml(prdFile) + '\',this)">Verify</button> ';
|
|
627
688
|
}
|
|
628
|
-
if (
|
|
689
|
+
if (modalVerifyWis.length >= 2) {
|
|
690
|
+
modalActions += modalVerifyWis.map(v => _renderVerifyBadge(v, { projectLabel: v.project || '' })).join(' ');
|
|
691
|
+
} else if (modalVerifyWis.length === 1) {
|
|
692
|
+
modalActions += _renderVerifyBadge(modalVerifyWis[0]);
|
|
693
|
+
}
|
|
629
694
|
// Archive + Delete (always, unless archived)
|
|
630
695
|
if (!isArchived) {
|
|
631
696
|
modalActions += '<button class="pr-pager-btn" style="' + bs + ';color:var(--muted)" onclick="planArchive(\'' + escapeHtml(prdFile || normalizedFile) + '\')">Archive</button> ';
|
|
@@ -918,10 +983,14 @@ async function planRegeneratePRD(source) {
|
|
|
918
983
|
} catch (e) { alert('Error: ' + e.message); }
|
|
919
984
|
}
|
|
920
985
|
|
|
921
|
-
function _renderVerifyBadge(verifyWi) {
|
|
986
|
+
function _renderVerifyBadge(verifyWi, opts) {
|
|
922
987
|
const statusColors = { pending: 'var(--muted)', dispatched: 'var(--blue)', done: 'var(--green)', failed: 'var(--red)' };
|
|
923
988
|
const color = statusColors[verifyWi.status] || 'var(--muted)';
|
|
924
|
-
const
|
|
989
|
+
const baseLabel = verifyWi.status === 'dispatched' ? 'Verifying...' : verifyWi.status === 'done' ? '\u2714 Verified' : verifyWi.status === 'failed' ? 'Verify failed' : 'Verify pending';
|
|
990
|
+
// Cross-repo plans fan out one verify WI per project (lifecycle.js); the
|
|
991
|
+
// call-site passes opts.projectLabel so each badge identifies its repo.
|
|
992
|
+
const projectLabel = (opts && opts.projectLabel) ? opts.projectLabel : '';
|
|
993
|
+
const label = projectLabel ? baseLabel + ' (' + escapeHtml(projectLabel) + ')' : baseLabel;
|
|
925
994
|
// E2E PR — check by prdItems, branch, or title. Issue #2949 — pullRequests
|
|
926
995
|
// moved off /api/status to /api/pull-requests (window._lastPullRequests).
|
|
927
996
|
const allPrs = window._lastPullRequests || [];
|
package/dashboard/js/settings.js
CHANGED
|
@@ -158,7 +158,11 @@ async function openSettings() {
|
|
|
158
158
|
|
|
159
159
|
const paneAutoFix =
|
|
160
160
|
'<h3>Auto-fix & Review Loop</h3>' +
|
|
161
|
-
'<div class="settings-pane-sub">
|
|
161
|
+
'<div class="settings-pane-sub">PR-triggered dispatch gates only. Each toggle below decides when an agent is auto-spawned in response to a PR signal (build failure, merge conflict, review verdict, human comment). All require the matching provider polling (Polling tab). The <em>Pause All Auto-fix</em> kill-switch below inerts every per-cause gate at once. Non-dispatch knobs (auto-merge, auto-vote, plan/decompose defaults) moved to the new <strong>PR Lifecycle</strong> and <strong>Workflow Defaults</strong> panes.</div>' +
|
|
162
|
+
'<div class="settings-stack" style="margin-bottom:16px">' +
|
|
163
|
+
settingsToggle('🛑 Pause ALL PR auto-fix dispatches', 'set-autoFixPaused', !!e.autoFixPaused, 'Halts every PR-triggered dispatch (review, re-review, build-failure fix, review-feedback fix, human-comment fix, merge-conflict fix) on the next tick. In-flight agents keep running. Reversible without restart. Per-cause flags below are inert while this is set.') +
|
|
164
|
+
'</div>' +
|
|
165
|
+
'<div class="settings-pane-sub" style="margin-bottom:8px">Per-cause dispatch gates — each one names the exact discoverFromPrs site it gates. When <em>Pause All Auto-fix</em> is ON these are inert; when it is OFF, each gate decides independently whether its site fires.</div>' +
|
|
162
166
|
'<div class="settings-stack" style="margin-bottom:16px">' +
|
|
163
167
|
settingsToggle('Auto-fix Builds', 'set-autoFixBuilds', e.autoFixBuilds !== false, 'Shared dispatch gate: auto-fix agent when a PR build fails; also requires that PR provider polling is enabled') +
|
|
164
168
|
settingsToggle('Auto-fix Conflicts', 'set-autoFixConflicts', e.autoFixConflicts !== false, 'Shared dispatch gate: auto-fix agent when a PR merge conflict is detected; also requires that PR provider polling is enabled') +
|
|
@@ -166,16 +170,42 @@ async function openSettings() {
|
|
|
166
170
|
settingsToggle('Auto-re-review PRs', 'set-autoReReviewPrs', e.autoReReviewPrs !== false, 'Shared dispatch gate: review agent after a fix push is awaiting re-review; also requires that PR provider polling is enabled') +
|
|
167
171
|
settingsToggle('Auto-fix Review Feedback', 'set-autoFixReviewFeedback', e.autoFixReviewFeedback !== false, 'Shared dispatch gate: fix agent for minions changes-requested verdicts; also requires that PR provider polling is enabled') +
|
|
168
172
|
settingsToggle('Auto-fix Human Comments', 'set-autoFixHumanComments', e.autoFixHumanComments !== false, 'Shared dispatch gate: fix agent for actionable human PR comments; also requires that PR provider polling is enabled') +
|
|
169
|
-
settingsToggle('
|
|
170
|
-
settingsToggle('Eval
|
|
171
|
-
settingsToggle('
|
|
172
|
-
settingsToggle('Auto-complete PRs', 'set-autoCompletePrs', !!e.autoCompletePrs, 'Auto-merge PRs when builds pass and review is approved (opt-in)') +
|
|
173
|
-
settingsToggle('Auto-approve Plans', 'set-autoApprovePlans', !!e.autoApprovePlans, 'PRDs are approved automatically without human review') +
|
|
174
|
-
settingsToggle('Auto-archive Plans', 'set-autoArchive', !!e.autoArchive, 'Automatically archive plans after verify completes (off = manual archive via dashboard)') +
|
|
175
|
-
settingsToggle('Auto-consolidate Memory', 'set-autoConsolidateMemory', !!e.autoConsolidateMemory, 'Periodically spawn the KB sweep (dedup + compress + normalize knowledge/) from the engine tick on a 4h cadence. Inbox→notes consolidation already runs every tick (gated by the Consolidation Threshold above); this toggle controls only the KB sweep that was previously dashboard-button-only.') +
|
|
173
|
+
settingsToggle('Eval Loop', 'set-evalLoop', e.evalLoop !== false, 'Gates the review→fix iteration loop only (first review, re-review, review-feedback fix). Does NOT gate build-failure, merge-conflict, or human-comment fixes. Use the emergency stop above to halt everything.') +
|
|
174
|
+
settingsToggle('Pre-dispatch Eval (cheap LLM gate)', 'set-enablePreDispatchEval', e.enablePreDispatchEval !== false, 'P-d2a9f6e5: cheap LLM gate that screens work items for clear/actionable/testable criteria BEFORE queueing the agent. Catches noop dispatches authored from impossible/ambiguous WIs. Fail-open on any validator error.') +
|
|
175
|
+
settingsToggle('Pre-dispatch Eval: skip PRD-sourced items', 'set-preDispatchEvalSkipPrdSourced', e.preDispatchEvalSkipPrdSourced !== false, 'W-mq9acoo800177bcb: short-circuit the validator for items materialized from an approved/active PRD — plan-to-prd already LLM-vets them. Reduces queue-time on an N-item approved PRD from ~N×25s to nearly instant. OFF = always re-validate even PRD-sourced items.') +
|
|
176
176
|
'</div>' +
|
|
177
177
|
'<div class="settings-grid-2">' +
|
|
178
178
|
settingsField('Eval Max Cost', 'set-evalMaxCost', e.evalMaxCost === null || e.evalMaxCost === undefined ? '' : e.evalMaxCost, '$', 'USD ceiling per work item across all eval iterations (blank = no limit)') +
|
|
179
|
+
settingsField('Pre-dispatch Eval Concurrency', 'set-preDispatchEvalConcurrency', e.preDispatchEvalConcurrency || 6, '', 'W-mq9acoo800177bcb: max parallel pre-dispatch validator calls per discovery tick. Default 6. Clamped to [1, 20]. Raises throughput when multiple PRDs queue at once; lower if the LLM provider throttles. 1 = sequential (pre-fix behavior).') +
|
|
180
|
+
'</div>' +
|
|
181
|
+
'<div style="margin-top:12px;padding:6px 8px;border:1px solid var(--border);border-radius:4px;background:rgba(130,160,210,0.06);font-size:var(--text-sm);color:var(--muted)">' +
|
|
182
|
+
'Moved to <strong>PR Lifecycle</strong>: Auto-complete PRs, Auto-apply review vote. ' +
|
|
183
|
+
'Moved to <strong>Workflow Defaults</strong>: Auto-archive Plans, Auto-approve Plans, Auto-decompose, Auto-consolidate Memory.' +
|
|
184
|
+
'</div>';
|
|
185
|
+
|
|
186
|
+
// P-g7a2b4c5 — new "PR Lifecycle" pane. Owns the two PR-lifecycle knobs
|
|
187
|
+
// that historically lived under Auto-fix but do NOT gate any dispatch:
|
|
188
|
+
// autoApplyReviewVote (lifecycle vote-posting), autoCompletePrs (auto-merge).
|
|
189
|
+
const paneLifecycle =
|
|
190
|
+
'<h3>PR Lifecycle</h3>' +
|
|
191
|
+
'<div class="settings-pane-sub">Post-merge / post-review lifecycle knobs. These are not dispatch gates — they control what Minions does with a PR once review or build state lands.</div>' +
|
|
192
|
+
'<div class="settings-stack">' +
|
|
193
|
+
settingsToggle('Auto-apply review vote to PR', 'set-autoApplyReviewVote', !!e.autoApplyReviewVote, 'When ON, Minions review verdicts (APPROVE / REQUEST_CHANGES) automatically flip the platform vote on ADO/GitHub. When OFF (default), verdicts are informational only and the human casts the final vote.') +
|
|
194
|
+
settingsToggle('Auto-complete PRs', 'set-autoCompletePrs', !!e.autoCompletePrs, 'Auto-merge PRs when builds pass and review is approved (opt-in). Independent of the per-cause auto-fix gates above.') +
|
|
195
|
+
'</div>';
|
|
196
|
+
|
|
197
|
+
// P-g7a2b4c5 — new "Workflow Defaults" pane. Owns the four workflow-level
|
|
198
|
+
// automation defaults that historically lived under Auto-fix: plan-flow
|
|
199
|
+
// (approve / decompose / archive) and memory consolidation. None of these
|
|
200
|
+
// gate a PR-triggered dispatch.
|
|
201
|
+
const paneWorkflow =
|
|
202
|
+
'<h3>Workflow Defaults</h3>' +
|
|
203
|
+
'<div class="settings-pane-sub">Workflow-level automation defaults. Independent of PR dispatch and PR lifecycle — these control plan flow and memory upkeep.</div>' +
|
|
204
|
+
'<div class="settings-stack">' +
|
|
205
|
+
settingsToggle('Auto-approve Plans', 'set-autoApprovePlans', !!e.autoApprovePlans, 'PRDs are approved automatically without human review.') +
|
|
206
|
+
settingsToggle('Auto-decompose', 'set-autoDecompose', e.autoDecompose !== false, 'Large implement items are auto-split into sub-tasks.') +
|
|
207
|
+
settingsToggle('Auto-archive Plans', 'set-autoArchive', !!e.autoArchive, 'Automatically archive plans after verify completes (off = manual archive via dashboard).') +
|
|
208
|
+
settingsToggle('Auto-consolidate Memory', 'set-autoConsolidateMemory', !!e.autoConsolidateMemory, 'Periodically spawn the KB sweep (dedup + compress + normalize knowledge/) from the engine tick on a 4h cadence. Inbox→notes consolidation already runs every tick (gated by the Consolidation Threshold under Advanced); this toggle controls only the KB sweep that was previously dashboard-button-only.') +
|
|
179
209
|
'</div>';
|
|
180
210
|
|
|
181
211
|
// W-mpmwxkrw000872ec — Appearance pane. Hosts dashboard-wide visual
|
|
@@ -268,11 +298,35 @@ async function openSettings() {
|
|
|
268
298
|
|
|
269
299
|
const panePolling =
|
|
270
300
|
'<h3>Polling</h3>' +
|
|
271
|
-
'<div class="settings-pane-sub">Cadence for fetching PR build status, votes, and comments from the platforms. Disabling a provider here turns the matching Auto-fix gates into no-ops.</div>' +
|
|
301
|
+
'<div class="settings-pane-sub">Cadence for fetching PR build status, votes, and comments from the platforms. Disabling a provider here turns the matching Auto-fix gates into no-ops. The <em>Pause All Polling</em> kill-switch below overrides both provider toggles and inerts every PR auto-dispatch gate (Auto-fix Builds / Conflicts / Review / etc.) until cleared.</div>' +
|
|
302
|
+
'<div class="settings-stack" style="margin-bottom:16px">' +
|
|
303
|
+
settingsToggle('🛑 Pause ALL polling', 'set-pollingPaused', !!e.pollingPaused, 'Halts every PR poll, reconciliation, rebase processing, and work-discovery scan on the next tick. Reversible without restart. Use when hitting API rate limits.') +
|
|
304
|
+
'</div>' +
|
|
305
|
+
'<div class="settings-pane-sub" style="margin-bottom:8px">Legacy bundle toggles — granular controls below override these when set.</div>' +
|
|
272
306
|
'<div class="settings-stack" style="margin-bottom:12px">' +
|
|
273
|
-
settingsToggle('ADO Polling', 'set-adoPollEnabled', e.adoPollEnabled !== false, '
|
|
274
|
-
settingsToggle('GitHub Polling', 'set-ghPollEnabled', e.ghPollEnabled !== false, '
|
|
307
|
+
settingsToggle('ADO Polling', 'set-adoPollEnabled', e.adoPollEnabled !== false, 'Legacy bundle macro — when OFF, silences all three ADO axes (status, comments, reconcile) and ADO PR dispatch gates are inert when this is off. Per-axis flags below take priority when explicitly set. Keep ON unless you want a one-knob ADO kill.') +
|
|
308
|
+
settingsToggle('GitHub Polling', 'set-ghPollEnabled', e.ghPollEnabled !== false, 'Legacy bundle macro — when OFF, silences all three GitHub axes (status, comments, reconcile) and GitHub PR dispatch gates are inert when this is off. Per-axis flags below take priority when explicitly set. Keep ON unless you want a one-knob GitHub kill.') +
|
|
275
309
|
'</div>' +
|
|
310
|
+
'<details class="settings-collapsible" style="margin-bottom:12px"><summary>Granular per-poller controls (P-c4d8e1a3)</summary>' +
|
|
311
|
+
'<div class="settings-pane-sub" style="margin-top:8px">Override individual ADO/GitHub poll axes + the pending-rebase processor. Explicit values here win; if unset, the legacy ADO/GitHub Polling macros above apply; otherwise the default is ON. <strong>Status</strong> + <strong>Comments</strong> still honor the <em>Pause All Polling</em> kill-switch above; <strong>Reconcile</strong> and <strong>Process Pending Rebases</strong> are recovery sweeps and ignore it (by design).</div>' +
|
|
312
|
+
'<div class="settings-stack" style="margin-top:8px">' +
|
|
313
|
+
settingsToggle('ADO PR Status Poll', 'set-adoPrStatusPollEnabled', e.adoPrStatusPollEnabled !== false, 'Granular: ADO PR build/merge/review status poll (section 2.6). Default ON. When OFF, ADO PR status will not refresh, but reconcile + comments still run (unless their own granular flag is OFF).') +
|
|
314
|
+
settingsToggle('ADO PR Comments Poll', 'set-adoPrCommentsPollEnabled', e.adoPrCommentsPollEnabled !== false, 'Granular: ADO PR human-comments poll (section 2.7). Default ON. When OFF, ADO PR comments will not surface — auto-fix-human-comments still composes against this flag for ADO.') +
|
|
315
|
+
settingsToggle('ADO PR Reconcile', 'set-adoPrReconcileEnabled', e.adoPrReconcileEnabled !== false, 'Granular: ADO PR reconciliation recovery sweep (section 2.7 tail). Default ON. Setting OFF stops engine from healing missed PR state transitions for ADO — only do this if reconcile is misbehaving.') +
|
|
316
|
+
settingsToggle('GitHub PR Status Poll', 'set-ghPrStatusPollEnabled', e.ghPrStatusPollEnabled !== false, 'Granular: GitHub PR build/merge/review status poll. Default ON. When OFF, GitHub PR status will not refresh, but reconcile + comments still run (unless their own granular flag is OFF).') +
|
|
317
|
+
settingsToggle('GitHub PR Comments Poll', 'set-ghPrCommentsPollEnabled', e.ghPrCommentsPollEnabled !== false, 'Granular: GitHub PR human-comments poll. Default ON. When OFF, GitHub PR comments will not surface — auto-fix-human-comments still composes against this flag for GitHub.') +
|
|
318
|
+
settingsToggle('GitHub PR Reconcile', 'set-ghPrReconcileEnabled', e.ghPrReconcileEnabled !== false, 'Granular: GitHub PR reconciliation recovery sweep. Default ON. Setting OFF stops engine from healing missed PR state transitions for GitHub — only do this if reconcile is misbehaving.') +
|
|
319
|
+
settingsToggle('Process Pending Rebases', 'set-processPendingRebasesEnabled', e.processPendingRebasesEnabled !== false, 'Granular: pending-rebase processor that runs after each status poll cycle. Default ON. Setting OFF freezes the rebase queue — useful if rebase-on-tick is causing platform churn or you want manual control. Has no legacy macro counterpart.') +
|
|
320
|
+
'</div></details>' +
|
|
321
|
+
'<details class="settings-collapsible" style="margin-bottom:12px"><summary>Granular work-discovery controls (P-d6f0a2b5)</summary>' +
|
|
322
|
+
'<div class="settings-pane-sub" style="margin-top:8px">Silence individual discovery phases inside the per-tick <code>engine.discoverWork()</code> sweep. Each flag complements (not replaces) the per-project <code>project.workSources.*.enabled</code> toggles — a <strong>false</strong> at either the global or per-project level skips the matching discovery call. There is no legacy macro to fall back to; defaults are ON. Use these for incident-response (e.g. <em>turn off PR discovery while triaging a runaway auto-fix loop</em>) or long migrations that must not auto-create work items.</div>' +
|
|
323
|
+
'<div class="settings-stack" style="margin-top:8px">' +
|
|
324
|
+
settingsToggle('PR Discovery', 'set-prDiscoveryEnabled', e.prDiscoveryEnabled !== false, 'Granular: discoverFromPrs per-project — gates the PR-driven fix / review / build-test work queue. Default ON. OFF stops the engine from queuing new fix/review/test work from PRs at all (per-project workSources.pullRequests.enabled still composes independently).') +
|
|
325
|
+
settingsToggle('Work Items Discovery', 'set-workItemsDiscoveryEnabled', e.workItemsDiscoveryEnabled !== false, 'Granular: discoverFromWorkItems per-project — gates the project-local work-items.json scan (includes items auto-filed from plans, design docs, build failures). Default ON. OFF freezes per-project work-item pickup; per-project workSources.workItems.enabled still composes independently.') +
|
|
326
|
+
settingsToggle('Central Work Discovery', 'set-centralWorkDiscoveryEnabled', e.centralWorkDiscoveryEnabled !== false, 'Granular: discoverCentralWorkItems — gates the top-level project-agnostic work-items.json scan. Default ON. OFF keeps centralWork iterable as [] so the downstream dispatch path stays safe.') +
|
|
327
|
+
settingsToggle('Scheduled Work Discovery', 'set-scheduledWorkDiscoveryEnabled', e.scheduledWorkDiscoveryEnabled !== false, 'Granular: discoverScheduledWork — gates the cron-style scheduled tasks + scheduled meetings block. Default ON. OFF stops cron-style scheduled tasks from firing.') +
|
|
328
|
+
settingsToggle('Plan Materialization', 'set-planMaterializationEnabled', e.planMaterializationEnabled !== false, 'Granular: reconcilePrdStatuses + materializePlansAsWorkItems pair — gates the PRD reconcile backward-scan and plan-to-work-item materialization. Default ON. OFF suppresses the pair atomically so a long migration does not race new PRD items into the queue.') +
|
|
329
|
+
'</div></details>' +
|
|
276
330
|
'<div class="settings-grid-2">' +
|
|
277
331
|
settingsField('PR Status Poll Frequency', 'set-prPollStatusEvery', e.prPollStatusEvery ?? 12, 'ticks', 'Poll PR build/review/merge status every N ticks for both ADO and GitHub (~12 min at default tick rate)') +
|
|
278
332
|
settingsField('PR Comments Poll Frequency', 'set-prPollCommentsEvery', e.prPollCommentsEvery ?? 12, 'ticks', 'Poll PR human comments every N ticks for both ADO and GitHub (~12 min at default tick rate)') +
|
|
@@ -344,6 +398,12 @@ async function openSettings() {
|
|
|
344
398
|
'</select>' +
|
|
345
399
|
'</div>' +
|
|
346
400
|
settingsField('Copilot fallback model', 'set-copilotFallbackModel', e.copilotFallbackModel || '', 'e.g. gpt-5.4', 'Copilot has no --fallback-model flag. On a MODEL_UNAVAILABLE (overloaded/503) retry, the engine OVERRIDES --model with this value (Copilot only).') +
|
|
401
|
+
'</div>' +
|
|
402
|
+
'<div class="settings-stack" style="margin-top:12px">' +
|
|
403
|
+
settingsField('Copilot: disable agent MCP servers', 'set-copilotAgentDisabledMcpServers',
|
|
404
|
+
Array.isArray(e.copilotAgentDisabledMcpServers) ? e.copilotAgentDisabledMcpServers.join(', ') : (e.copilotAgentDisabledMcpServers || ''),
|
|
405
|
+
'e.g. playwright, maestro, loop',
|
|
406
|
+
'Comma-separated MCP server names (from ~/.copilot/mcp-config.json) the engine disables for autonomous Copilot agent dispatches via --disable-mcp-server. Copilot loads your user MCP config on every spawn regardless of --add-dir/hermeticHarness; list the noisy local/UI servers (e.g. playwright, maestro) to keep them out of agents. Empty = inherit all.') +
|
|
347
407
|
'</div>';
|
|
348
408
|
|
|
349
409
|
const paneClaude =
|
|
@@ -446,9 +506,15 @@ async function openSettings() {
|
|
|
446
506
|
|
|
447
507
|
// Section registry — order is intentional (Runtime + Auto-fix surface first
|
|
448
508
|
// per Caleb's feedback). Each entry maps a rail-button id → pane HTML.
|
|
509
|
+
// P-g7a2b4c5 — added "PR Lifecycle" and "Workflow Defaults" panes for the
|
|
510
|
+
// non-dispatch toggles that historically lived under Auto-fix; placed
|
|
511
|
+
// adjacent to Auto-fix so operators following the legacy mental model find
|
|
512
|
+
// the moved flags quickly.
|
|
449
513
|
const sections = [
|
|
450
514
|
{ id: 'runtime', label: 'Runtime & Models', featured: true, html: paneRuntime },
|
|
451
515
|
{ id: 'autofix', label: 'Auto-fix & Review Loop', featured: true, html: paneAutoFix },
|
|
516
|
+
{ id: 'lifecycle', label: 'PR Lifecycle', html: paneLifecycle },
|
|
517
|
+
{ id: 'workflow', label: 'Workflow Defaults', html: paneWorkflow },
|
|
452
518
|
{ id: 'appearance', label: 'Appearance', html: paneAppearance },
|
|
453
519
|
{ id: 'projects', label: 'Projects', html: paneProjects },
|
|
454
520
|
{ id: 'polling', label: 'Polling', html: panePolling },
|
|
@@ -904,6 +970,7 @@ async function saveSettings() {
|
|
|
904
970
|
autoApplyReviewVote: document.getElementById('set-autoApplyReviewVote').checked,
|
|
905
971
|
autoFixBuilds: document.getElementById('set-autoFixBuilds').checked,
|
|
906
972
|
autoFixConflicts: document.getElementById('set-autoFixConflicts').checked,
|
|
973
|
+
autoFixPaused: document.getElementById('set-autoFixPaused').checked,
|
|
907
974
|
autoReviewPrs: document.getElementById('set-autoReviewPrs').checked,
|
|
908
975
|
autoReReviewPrs: document.getElementById('set-autoReReviewPrs').checked,
|
|
909
976
|
autoFixReviewFeedback: document.getElementById('set-autoFixReviewFeedback').checked,
|
|
@@ -914,9 +981,31 @@ async function saveSettings() {
|
|
|
914
981
|
orphanHolderScanTimeoutMs: document.getElementById('set-orphanHolderScanTimeoutMs')?.value,
|
|
915
982
|
adoPollEnabled: document.getElementById('set-adoPollEnabled').checked,
|
|
916
983
|
ghPollEnabled: document.getElementById('set-ghPollEnabled').checked,
|
|
984
|
+
pollingPaused: document.getElementById('set-pollingPaused').checked,
|
|
985
|
+
// P-c4d8e1a3 — granular per-poller flags (default true). All seven submit
|
|
986
|
+
// booleans; the server stores them on config.engine and resolvePollFlag
|
|
987
|
+
// resolves precedence at engine runtime.
|
|
988
|
+
adoPrStatusPollEnabled: document.getElementById('set-adoPrStatusPollEnabled').checked,
|
|
989
|
+
adoPrCommentsPollEnabled: document.getElementById('set-adoPrCommentsPollEnabled').checked,
|
|
990
|
+
adoPrReconcileEnabled: document.getElementById('set-adoPrReconcileEnabled').checked,
|
|
991
|
+
ghPrStatusPollEnabled: document.getElementById('set-ghPrStatusPollEnabled').checked,
|
|
992
|
+
ghPrCommentsPollEnabled: document.getElementById('set-ghPrCommentsPollEnabled').checked,
|
|
993
|
+
ghPrReconcileEnabled: document.getElementById('set-ghPrReconcileEnabled').checked,
|
|
994
|
+
processPendingRebasesEnabled: document.getElementById('set-processPendingRebasesEnabled').checked,
|
|
995
|
+
// P-d6f0a2b5 — granular work-discovery flags (default true). Each one
|
|
996
|
+
// gates a single phase inside engine.discoverWork via the inline
|
|
997
|
+
// `config.engine?.<flag> !== false` pattern. No legacy macro counterpart.
|
|
998
|
+
prDiscoveryEnabled: document.getElementById('set-prDiscoveryEnabled').checked,
|
|
999
|
+
workItemsDiscoveryEnabled: document.getElementById('set-workItemsDiscoveryEnabled').checked,
|
|
1000
|
+
centralWorkDiscoveryEnabled: document.getElementById('set-centralWorkDiscoveryEnabled').checked,
|
|
1001
|
+
scheduledWorkDiscoveryEnabled: document.getElementById('set-scheduledWorkDiscoveryEnabled').checked,
|
|
1002
|
+
planMaterializationEnabled: document.getElementById('set-planMaterializationEnabled').checked,
|
|
917
1003
|
prPollStatusEvery: document.getElementById('set-prPollStatusEvery').value,
|
|
918
1004
|
prPollCommentsEvery: document.getElementById('set-prPollCommentsEvery').value,
|
|
919
1005
|
evalMaxCost: document.getElementById('set-evalMaxCost').value || null,
|
|
1006
|
+
enablePreDispatchEval: document.getElementById('set-enablePreDispatchEval').checked,
|
|
1007
|
+
preDispatchEvalSkipPrdSourced: document.getElementById('set-preDispatchEvalSkipPrdSourced').checked,
|
|
1008
|
+
preDispatchEvalConcurrency: document.getElementById('set-preDispatchEvalConcurrency').value,
|
|
920
1009
|
agentBusyReassignMs: document.getElementById('set-agentBusyReassignMs').value,
|
|
921
1010
|
maxRetriesPerAgent: document.getElementById('set-maxRetriesPerAgent').value,
|
|
922
1011
|
ignoredCommentAuthors: document.getElementById('set-ignoredCommentAuthors').value,
|
package/dashboard/js/utils.js
CHANGED
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
// Signal the engine to tick immediately (pick up new work without waiting 60s)
|
|
4
4
|
function wakeEngine() { fetch('/api/engine/wakeup', { method: 'POST' }).catch(() => {}); }
|
|
5
5
|
|
|
6
|
-
// "Try new Slim UX" — flips the slim-ux feature flag ON then
|
|
7
|
-
// root route serves the slim cockpit (the takeover is checked at request
|
|
8
|
-
// so no engine restart is needed).
|
|
9
|
-
// the
|
|
6
|
+
// "Try new Slim UX" — flips the slim-ux feature flag ON then navigates to the
|
|
7
|
+
// root route so it serves the slim cockpit (the takeover is checked at request
|
|
8
|
+
// time, so no engine restart is needed). We navigate to '/' rather than
|
|
9
|
+
// location.reload() because the slim takeover is gated on pathname === '/';
|
|
10
|
+
// reloading from a non-home classic page (/work, /prs, …) preserves the path
|
|
11
|
+
// and silently re-serves the classic dashboard. The first-visit welcome popup
|
|
12
|
+
// is gated on the browser side in dashboard/slim/js/settings.js, not here.
|
|
10
13
|
async function trySlimUx(btn) {
|
|
11
14
|
if (btn) { btn.disabled = true; btn.textContent = 'Switching…'; }
|
|
12
15
|
try {
|
|
@@ -16,7 +19,7 @@ async function trySlimUx(btn) {
|
|
|
16
19
|
body: JSON.stringify({ id: 'slim-ux', enabled: true }),
|
|
17
20
|
});
|
|
18
21
|
if (!res.ok) throw new Error('HTTP ' + res.status);
|
|
19
|
-
location.
|
|
22
|
+
location.href = '/';
|
|
20
23
|
} catch (e) {
|
|
21
24
|
if (btn) { btn.disabled = false; btn.textContent = '✨ Try new Slim UX'; }
|
|
22
25
|
alert('Could not switch to Slim UX: ' + (e && e.message ? e.message : 'unknown error'));
|
package/dashboard/layout.html
CHANGED
|
@@ -35,6 +35,12 @@
|
|
|
35
35
|
</div>
|
|
36
36
|
</header>
|
|
37
37
|
<div class="engine-alert" id="engine-alert"></div>
|
|
38
|
+
<!-- P-g7a2b4c5 — sticky cross-page banner for the two operator kill-switches
|
|
39
|
+
(engine.pollingPaused, engine.autoFixPaused). Rendered by
|
|
40
|
+
renderPausedBanner() in dashboard/js/render-dispatch.js whenever either
|
|
41
|
+
flag is true; hidden otherwise. Lives outside .page-layout so it persists
|
|
42
|
+
across page nav. -->
|
|
43
|
+
<div class="paused-banner" id="paused-banner" hidden></div>
|
|
38
44
|
|
|
39
45
|
<!-- Command Center Drawer -->
|
|
40
46
|
<div id="cc-overlay" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.4);z-index:340" onclick="toggleCommandCenter()"></div>
|
package/dashboard/slim/body.html
CHANGED
|
@@ -21,7 +21,10 @@
|
|
|
21
21
|
<div class="panel panel-actions">
|
|
22
22
|
<div class="panel-header">
|
|
23
23
|
Actions
|
|
24
|
-
<span class="panel-
|
|
24
|
+
<span class="panel-head-meta">
|
|
25
|
+
<span class="cc-model-badge" id="cc-model-badge" hidden></span>
|
|
26
|
+
<span class="panel-sub">Command Center & quick triggers</span>
|
|
27
|
+
</span>
|
|
25
28
|
</div>
|
|
26
29
|
<div class="actions-chat">
|
|
27
30
|
<div class="chat-tabs" id="chat-tabs"></div>
|
|
@@ -39,7 +42,7 @@
|
|
|
39
42
|
rows="2"
|
|
40
43
|
placeholder="Ask anything or give a command... (Enter to send, Shift+Enter for newline)"
|
|
41
44
|
></textarea>
|
|
42
|
-
<button id="chat-stop" class="chat-stop" type="button" title="Stop the in-flight response"
|
|
45
|
+
<button id="chat-stop" class="chat-stop" type="button" title="Stop the in-flight response" aria-label="Stop agent"><svg class="chat-stop-icon" viewBox="0 0 24 24" width="20" height="20" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="9" fill="none" stroke="currentColor" stroke-width="2"></circle><rect x="8" y="8" width="8" height="8" rx="1" fill="currentColor"></rect></svg></button>
|
|
43
46
|
<button id="chat-send" class="chat-send" type="button">Send</button>
|
|
44
47
|
</div>
|
|
45
48
|
</div>
|
|
@@ -77,23 +80,23 @@
|
|
|
77
80
|
count now lives in the Team cards above, so that tile is removed. -->
|
|
78
81
|
<div class="cockpit-section">
|
|
79
82
|
<div class="cockpit-grid" id="cockpit-grid">
|
|
80
|
-
<div class="cockpit-tile" data-tile="engine">
|
|
83
|
+
<div class="cockpit-tile cockpit-tile--full" data-tile="engine">
|
|
81
84
|
<div class="cockpit-label"><span class="cockpit-dot"></span> Engine</div>
|
|
82
85
|
<div class="cockpit-value dim">—</div>
|
|
83
86
|
<div class="cockpit-detail">checking…</div>
|
|
84
87
|
</div>
|
|
85
|
-
<div class="cockpit-tile" data-tile="dispatches">
|
|
86
|
-
<div class="cockpit-label"><span class="cockpit-dot"></span> Active dispatches</div>
|
|
87
|
-
<div class="cockpit-value dim">0</div>
|
|
88
|
-
<div class="cockpit-detail">none in flight</div>
|
|
89
|
-
</div>
|
|
90
88
|
<div class="cockpit-tile" data-tile="queued">
|
|
91
89
|
<div class="cockpit-label"><span class="cockpit-dot"></span> Queued work</div>
|
|
92
90
|
<div class="cockpit-value dim">0</div>
|
|
93
91
|
<div class="cockpit-detail">queue empty</div>
|
|
94
92
|
</div>
|
|
93
|
+
<div class="cockpit-tile" data-tile="dispatches">
|
|
94
|
+
<div class="cockpit-label"><span class="cockpit-dot"></span> Active dispatches</div>
|
|
95
|
+
<div class="cockpit-value dim">0</div>
|
|
96
|
+
<div class="cockpit-detail">no agents working</div>
|
|
97
|
+
</div>
|
|
95
98
|
<div class="cockpit-tile" data-tile="prs">
|
|
96
|
-
<button id="slim-tile-linkpr-chip" class="linkpr-chip on-tile" type="button" title="Link a pull request"
|
|
99
|
+
<button id="slim-tile-linkpr-chip" class="linkpr-chip on-tile" type="button" title="Link a pull request" aria-label="Link a pull request">+</button>
|
|
97
100
|
<div class="cockpit-label"><span class="cockpit-dot"></span> Active PRs</div>
|
|
98
101
|
<div class="cockpit-value dim">0</div>
|
|
99
102
|
<div class="cockpit-detail">no open PRs</div>
|
|
@@ -102,7 +102,11 @@
|
|
|
102
102
|
var res = await fetch('/api/command-center/stream', {
|
|
103
103
|
method: 'POST',
|
|
104
104
|
headers: { 'Content-Type': 'application/json' },
|
|
105
|
-
|
|
105
|
+
// currentProject is the slim project picker's explicit selection (null
|
|
106
|
+
// when the indicator reads "Select project"). noProjectSelected lets the
|
|
107
|
+
// server inject a "no project — ask the user" CC preamble instead of
|
|
108
|
+
// silently defaulting; only the slim composer sends it (W-mqayzsj3).
|
|
109
|
+
body: JSON.stringify({ message: text, tabId: tabId, sessionId: sessionId, currentProject: currentProject || undefined, noProjectSelected: currentProject ? undefined : true }),
|
|
106
110
|
signal: signal,
|
|
107
111
|
});
|
|
108
112
|
if (!res.ok) {
|