@yemi33/minions 0.1.2424 → 0.1.2425
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/bin/install-internal-minions.js +1209 -0
- package/bin/minions.js +75 -9
- package/dashboard/docs/typography.md +27 -12
- package/dashboard/js/command-center.js +13 -11
- package/dashboard/js/live-stream.js +1 -1
- package/dashboard/js/memory-search.js +388 -43
- package/dashboard/js/modal-qa.js +5 -5
- package/dashboard/js/qa.js +39 -26
- package/dashboard/js/refresh.js +37 -18
- package/dashboard/js/render-agents.js +14 -18
- package/dashboard/js/render-dispatch.js +14 -74
- package/dashboard/js/render-inbox.js +3 -3
- package/dashboard/js/render-meetings.js +8 -8
- package/dashboard/js/render-other.js +45 -20
- package/dashboard/js/render-pipelines.js +6 -6
- package/dashboard/js/render-plans.js +14 -14
- package/dashboard/js/render-prd.js +46 -46
- package/dashboard/js/render-prs.js +9 -65
- package/dashboard/js/render-schedules.js +5 -5
- package/dashboard/js/render-skills.js +6 -6
- package/dashboard/js/render-utils.js +4 -4
- package/dashboard/js/render-watches.js +4 -16
- package/dashboard/js/render-work-items.js +95 -107
- package/dashboard/js/settings.js +175 -74
- package/dashboard/layout.html +3 -2
- package/dashboard/pages/inbox.html +1 -1
- package/dashboard/pages/work.html +1 -1
- package/dashboard/shared/model-display.js +9 -0
- package/dashboard/shared/watches-source.js +43 -0
- package/dashboard/shared/welcome-popup.js +259 -0
- package/dashboard/slim/body.html +6 -6
- package/dashboard/slim/js/chat.js +4 -2
- package/dashboard/slim/js/history.js +10 -3
- package/dashboard/slim/js/modals-tiles.js +85 -16
- package/dashboard/slim/js/status.js +12 -16
- package/dashboard/slim/styles.css +24 -4
- package/dashboard/styles.css +62 -27
- package/dashboard-build.js +8 -2
- package/dashboard.js +3854 -2514
- package/docs/README.md +6 -4
- package/docs/api-errors.md +144 -0
- package/docs/auto-discovery.md +83 -61
- package/docs/capture-demos.js +173 -30
- package/docs/command-center.md +5 -1
- package/docs/completion-reports.md +127 -11
- package/docs/constants.md +15 -3
- package/docs/constellation-style-telemetry.md +2 -2
- package/docs/copilot-cli-schema.md +32 -22
- package/docs/cross-repo-plans.md +30 -30
- package/docs/demo/memory-system.html +1 -1
- package/docs/deprecated.json +132 -18
- package/docs/diagnostics-memory.md +8 -4
- package/docs/human-vs-automated.md +2 -2
- package/docs/internal-install.md +190 -0
- package/docs/kb-pr3223-cascade-archiving.md +16 -0
- package/docs/kb-pr696-merge-conflict-docs.md +23 -0
- package/docs/kb-sweep.md +2 -2
- package/docs/keep-processes.md +8 -1
- package/docs/live-checkout-mode.md +1 -1
- package/docs/managed-spawn.md +21 -3
- package/docs/named-agents.md +3 -2
- package/docs/plan-lifecycle.md +32 -32
- package/docs/pr-screenshots/pr-899/worker-pool-worktrees-AFTER.png +0 -0
- package/docs/pr-screenshots/pr-899/worker-pool-worktrees-BEFORE.png +0 -0
- package/docs/pr-screenshots/pr-979/auto-fix-pane-AFTER.png +0 -0
- package/docs/pr-screenshots/pr-979/auto-fix-pane-BEFORE.png +0 -0
- package/docs/pr-screenshots/pr-985/pr-column-em-dash-AFTER.png +0 -0
- package/docs/pr-screenshots/pr-985/pr-column-em-dash-BEFORE.png +0 -0
- package/docs/qa-runbook-lifecycle.md +67 -51
- package/docs/qa-runbooks.md +2 -2
- package/docs/runtime-adapters.md +101 -50
- package/docs/security.md +28 -0
- package/docs/self-improvement.md +47 -13
- package/docs/skills.md +29 -0
- package/docs/slim-ux/concepts.md +2 -2
- package/docs/specs/agent-configurability.md +43 -41
- package/docs/specs/agent-rename.md +13 -13
- package/docs/team-memory.md +283 -14
- package/docs/tutorials/03-plan-driven-feature.md +3 -3
- package/docs/tutorials/04-runtimes-and-harness.md +18 -21
- package/docs/tutorials/08-operations-and-recovery.md +3 -2
- package/docs/tutorials/README.md +4 -3
- package/docs/visual-evidence-ci.md +103 -0
- package/docs/watches.md +35 -30
- package/docs/worktree-lifecycle.md +151 -13
- package/engine/acp-transport.js +1 -0
- package/engine/ado.js +80 -33
- package/engine/agent-api-validation.js +571 -0
- package/engine/agent-worker-pool.js +7 -6
- package/engine/api-contracts/agent-content.js +761 -0
- package/engine/api-contracts/cc-ops.js +219 -0
- package/engine/api-contracts/config-runtime.js +419 -0
- package/engine/api-contracts/core.js +138 -0
- package/engine/api-contracts/index.js +506 -0
- package/engine/api-contracts/orchestration.js +602 -0
- package/engine/api-contracts/pull-requests.js +323 -0
- package/engine/api-contracts/qa-process.js +764 -0
- package/engine/api-contracts/work-plan-prd.js +494 -0
- package/engine/api-validation.js +543 -0
- package/engine/cc-api-validation.js +586 -0
- package/engine/cc-worker-pool.js +9 -1
- package/engine/cleanup.js +393 -271
- package/engine/cli.js +65 -7
- package/engine/comment-classifier.js +70 -5
- package/engine/consolidation.js +123 -5
- package/engine/content-api-validation.js +606 -0
- package/engine/create-pr-worktree.js +22 -1
- package/engine/db/migrations/027-review-learning-lifecycle.js +162 -0
- package/engine/dispatch.js +33 -0
- package/engine/distribution.js +189 -0
- package/engine/execution-model.js +59 -0
- package/engine/features.js +12 -0
- package/engine/github.js +67 -25
- package/engine/harness-context.js +313 -0
- package/engine/inbox-store.js +1 -1
- package/engine/kb-sweep.js +161 -38
- package/engine/keep-process-sweep.js +43 -3
- package/engine/lifecycle.js +1066 -165
- package/engine/llm.js +4 -0
- package/engine/managed-spawn.js +463 -48
- package/engine/meeting.js +197 -34
- package/engine/memory-retrieval.js +280 -0
- package/engine/memory-store.js +821 -38
- package/engine/model-discovery.js +17 -3
- package/engine/pipeline.js +802 -14
- package/engine/plan-prd-validation.js +745 -0
- package/engine/playbook.js +206 -17
- package/engine/pooled-agent-process.js +6 -1
- package/engine/pr-action.js +1 -1
- package/engine/pr-issue-validation.js +653 -0
- package/engine/pr-resolve.js +75 -13
- package/engine/prd-store.js +39 -6
- package/engine/preflight.js +7 -2
- package/engine/process-utils.js +79 -31
- package/engine/projects.js +198 -12
- package/engine/promotion.js +371 -0
- package/engine/qa-from-prd.js +3 -0
- package/engine/qa-process-validation.js +572 -0
- package/engine/qa-runbooks.js +41 -37
- package/engine/qa-runners/maestro.js +11 -7
- package/engine/qa-runners/playwright.js +37 -14
- package/engine/qa-runners.js +9 -10
- package/engine/qa-runs.js +124 -11
- package/engine/qa-sessions.js +139 -40
- package/engine/quarantine-refs.js +81 -11
- package/engine/queries.js +163 -9
- package/engine/review-learning-backfill.js +482 -0
- package/engine/review-learning.js +1236 -0
- package/engine/runtimes/claude.js +60 -2
- package/engine/runtimes/codex.js +57 -16
- package/engine/runtimes/contract.js +21 -0
- package/engine/runtimes/copilot.js +51 -3
- package/engine/runtimes/index.js +1 -0
- package/engine/scheduler.js +201 -32
- package/engine/settings-validation.js +909 -0
- package/engine/shared.js +705 -208
- package/engine/spawn-agent.js +48 -21
- package/engine/steering-constraints.js +31 -0
- package/engine/steering-store.js +10 -2
- package/engine/steering.js +14 -2
- package/engine/timeout.js +404 -111
- package/engine/untrusted-fence.js +24 -10
- package/engine/watch-actions.js +7 -2
- package/engine/watches.js +353 -51
- package/engine/work-item-validation.js +561 -0
- package/engine/work-items-store.js +61 -1
- package/engine/worktree-gc.js +656 -37
- package/engine/worktree-preflight.js +154 -0
- package/engine.js +2068 -342
- package/package.json +1 -1
- package/playbooks/_pr-description-audit.md +88 -41
- package/playbooks/build-fix-complex.md +3 -3
- package/playbooks/docs.md +2 -2
- package/playbooks/fix.md +18 -18
- package/playbooks/implement.md +18 -18
- package/playbooks/qa-session-draft.md +10 -12
- package/playbooks/qa-session-execute.md +16 -12
- package/playbooks/qa-validate.md +8 -7
- package/playbooks/shared-rules.md +45 -0
- package/playbooks/test.md +4 -0
- package/prompts/cc-system.md +1 -1
- package/skills/check-self-authored-review-comment/SKILL.md +2 -2
|
@@ -63,7 +63,7 @@ function renderSkills(skills) {
|
|
|
63
63
|
let html = '<div style="display:flex;gap:4px;margin-bottom:8px;flex-wrap:wrap">';
|
|
64
64
|
for (const t of tabs) {
|
|
65
65
|
const active = _skillsTab === t.key;
|
|
66
|
-
html += '<button class="pr-pager-btn" style="font-size:var(--text-
|
|
66
|
+
html += '<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px;' +
|
|
67
67
|
(active ? 'background:var(--green);color:#fff;border-color:var(--green)' : '') +
|
|
68
68
|
'" onclick="_skillsSelectTab(' + _jsArg(t.key) + ')">' + escHtml(t.label) + '</button>';
|
|
69
69
|
}
|
|
@@ -73,7 +73,7 @@ function renderSkills(skills) {
|
|
|
73
73
|
// native locations (~/.claude/skills, ~/.copilot/skills, plugin skills) are
|
|
74
74
|
// only visible to that runtime. The "agent" tab (~/.agents/skills) is the
|
|
75
75
|
// cross-runtime portable bucket and IS visible to every runtime.
|
|
76
|
-
html += '<div style="font-size:var(--text-
|
|
76
|
+
html += '<div style="font-size:var(--text-sm);font-style:italic;color:var(--muted);margin-bottom:8px;line-height:1.4">' +
|
|
77
77
|
'Skills are reference docs agents read on demand — they are not injected wholesale into prompts. ' +
|
|
78
78
|
'Each tab reflects what the matching runtime would see; runtime-native skills are NOT cross-visible. ' +
|
|
79
79
|
'The agent tab (~/.agents/skills) is the cross-runtime portable bucket — visible to every runtime.' +
|
|
@@ -90,10 +90,10 @@ function renderSkills(skills) {
|
|
|
90
90
|
html += '<div style="display:flex;flex-direction:column;gap:6px">';
|
|
91
91
|
for (const r of page) {
|
|
92
92
|
const meta = _skillMetaOf(r.source);
|
|
93
|
-
const autoTag = r.autoGenerated ? '<span style="font-size:var(--text-
|
|
93
|
+
const autoTag = r.autoGenerated ? '<span style="font-size:var(--text-sm);background:rgba(63,185,80,0.15);color:var(--green);padding:1px 4px;border-radius:3px;margin-left:4px">auto</span>' : '';
|
|
94
94
|
html += '<div class="inbox-item" onclick="openSkill(' + _jsArg(r.file) + ',' + _jsArg(r.source || 'claude-code') + ',' + _jsArg(r.dir || '') + ')" style="border-left-color:var(--green)">' +
|
|
95
95
|
'<div class="inbox-name"><span style="color:var(--green);font-weight:700">' + meta.icon + ' ' + escHtml(r.name) + '</span>' + autoTag +
|
|
96
|
-
'<span style="font-size:var(--text-
|
|
96
|
+
'<span style="font-size:var(--text-sm);color:var(--muted);margin-left:auto">' + escHtml(meta.label) + '</span>' +
|
|
97
97
|
'</div>' +
|
|
98
98
|
(r.description ? '<div class="inbox-preview" style="font-size:var(--text-sm);color:var(--text)">' + escHtml(r.description) + '</div>' : '') +
|
|
99
99
|
'</div>';
|
|
@@ -103,11 +103,11 @@ function renderSkills(skills) {
|
|
|
103
103
|
// Pagination controls
|
|
104
104
|
if (totalPages > 1) {
|
|
105
105
|
html += '<div style="display:flex;align-items:center;gap:8px;margin-top:6px;justify-content:center">';
|
|
106
|
-
html += '<button class="pr-pager-btn" style="font-size:var(--text-
|
|
106
|
+
html += '<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px" ' +
|
|
107
107
|
(_skillsPage === 0 ? 'disabled' : '') +
|
|
108
108
|
' onclick="_skillsPage--;renderSkills(window._lastSkills)">«</button>';
|
|
109
109
|
html += '<span style="font-size:var(--text-sm);color:var(--muted)">' + (_skillsPage + 1) + ' / ' + totalPages + '</span>';
|
|
110
|
-
html += '<button class="pr-pager-btn" style="font-size:var(--text-
|
|
110
|
+
html += '<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px" ' +
|
|
111
111
|
(_skillsPage >= totalPages - 1 ? 'disabled' : '') +
|
|
112
112
|
' onclick="_skillsPage++;renderSkills(window._lastSkills)">»</button>';
|
|
113
113
|
html += '</div>';
|
|
@@ -260,7 +260,7 @@ function _renderJsonObj(obj, state) {
|
|
|
260
260
|
if (!text || text.length <= 10) return '';
|
|
261
261
|
var truncated = text.length > 3000;
|
|
262
262
|
var displayText = truncated ? text.slice(0, 3000) + '...' : text;
|
|
263
|
-
return '<div style="background:var(--surface);border-left:2px solid var(--border);padding:2px 8px;margin:0 0 2px 16px;font-size:var(--text-
|
|
263
|
+
return '<div style="background:var(--surface);border-left:2px solid var(--border);padding:2px 8px;margin:0 0 2px 16px;font-size:var(--text-sm);font-family:monospace;color:var(--muted);max-height:160px;overflow-y:auto;white-space:pre-wrap;cursor:pointer" onclick="this.style.maxHeight=this.style.maxHeight===\'160px\'?\'none\':\'160px\'">' + escHtml(displayText) + '</div>';
|
|
264
264
|
}
|
|
265
265
|
function toolKey(name, input) {
|
|
266
266
|
try { return String(name || 'tool') + '|' + JSON.stringify(input || {}); }
|
|
@@ -406,7 +406,7 @@ function renderAgentOutput(text) {
|
|
|
406
406
|
var msg = trimmed.replace('[human-steering] ', '');
|
|
407
407
|
fragments.push('<div style="align-self:flex-end;background:var(--blue);color:#fff;padding:6px 12px;border-radius:12px 12px 2px 12px;max-width:80%;margin:4px 0;font-size:var(--text-md)">' +
|
|
408
408
|
'<span style="margin-right:4px">❯</span>' + escHtml(msg) +
|
|
409
|
-
'<div style="font-size:var(--text-
|
|
409
|
+
'<div style="font-size:var(--text-sm);opacity:0.7;margin-top:2px">\u2713 Queued</div></div>');
|
|
410
410
|
continue;
|
|
411
411
|
}
|
|
412
412
|
|
|
@@ -444,7 +444,7 @@ function renderAgentOutput(text) {
|
|
|
444
444
|
|
|
445
445
|
// Stderr
|
|
446
446
|
if (trimmed.startsWith('[stderr]')) {
|
|
447
|
-
fragments.push('<div style="font-size:var(--text-
|
|
447
|
+
fragments.push('<div style="font-size:var(--text-sm);color:var(--red);font-family:monospace;padding:1px 4px">' + escHtml(trimmed) + '</div>');
|
|
448
448
|
} else {
|
|
449
449
|
// Plain text fallback
|
|
450
450
|
fragments.push('<div style="font-size:var(--text-sm);color:var(--muted);font-family:monospace;padding:1px 4px">' + escHtml(trimmed) + '</div>');
|
|
@@ -550,7 +550,7 @@ function renderPager(opts) {
|
|
|
550
550
|
function pinButton(pinKey, pinned, source, opts) {
|
|
551
551
|
var extraStyle = (opts && opts.extraStyle) || '';
|
|
552
552
|
return '<button class="pr-pager-btn pin-btn' + (pinned ? ' pinned' : '') +
|
|
553
|
-
'" style="font-size:var(--text-
|
|
553
|
+
'" style="font-size:var(--text-sm);padding:2px 8px;' + extraStyle +
|
|
554
554
|
'" data-pin-key="' + escHtml(pinKey) +
|
|
555
555
|
'" onclick="event.stopPropagation();_togglePinAndRefresh(this.dataset.pinKey,\'' + source + '\')">' +
|
|
556
556
|
(pinned ? 'Unpin' : 'Pin') + '</button>';
|
|
@@ -1,20 +1,8 @@
|
|
|
1
1
|
// render-watches.js — Watch rendering and CRUD functions for the dashboard
|
|
2
2
|
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// watches.json, so mtime+size ETag surfaces them on the next 4 s poll.
|
|
7
|
-
// Falls back to data.watches when /state/ returns null.
|
|
8
|
-
async function fetchWatchesFromDisk() {
|
|
9
|
-
try {
|
|
10
|
-
const r = await fetch('/state/engine/watches.json');
|
|
11
|
-
if (!r.ok) return null;
|
|
12
|
-
const arr = await r.json();
|
|
13
|
-
return Array.isArray(arr) ? arr : null;
|
|
14
|
-
} catch {
|
|
15
|
-
return null;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
3
|
+
// Watch loading lives in dashboard/shared/watches-source.js
|
|
4
|
+
// (MinionsWatchesSource) so the classic and Slim surfaces resolve watches from
|
|
5
|
+
// the same SQL-backed endpoint. See refresh.js for the polling call site.
|
|
18
6
|
|
|
19
7
|
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
20
8
|
|
|
@@ -256,7 +244,7 @@ function renderWatches(watchesData, opts) {
|
|
|
256
244
|
// Pause/Resume button (F7: marks as `disabled` while POST in flight)
|
|
257
245
|
var pauseBusyCls = _watchToggleInFlight.has(w.id) ? ' disabled' : '';
|
|
258
246
|
if (w.status === 'active') {
|
|
259
|
-
html += '<button class="pr-pager-btn' + pauseBusyCls + '" style="font-size:var(--text-
|
|
247
|
+
html += '<button class="pr-pager-btn' + pauseBusyCls + '" style="font-size:var(--text-sm);padding:1px 6px;color:var(--yellow);border-color:var(--yellow);margin-right:4px" onclick="event.stopPropagation();toggleWatchPause(\'' + escHtml(w.id) + '\',true)" title="Pause">⏸</button>';
|
|
260
248
|
} else if (w.status === 'paused') {
|
|
261
249
|
html += '<button class="btn-action' + pauseBusyCls + '" style="margin-right:4px" onclick="event.stopPropagation();toggleWatchPause(\'' + escHtml(w.id) + '\',false)" title="Resume">▶</button>';
|
|
262
250
|
}
|
|
@@ -4,28 +4,6 @@ let allWorkItems = [];
|
|
|
4
4
|
let wiPage = 0;
|
|
5
5
|
const WI_PER_PAGE = 20;
|
|
6
6
|
|
|
7
|
-
// Engine-enrichment field names that live on the /api/status workItems slice
|
|
8
|
-
// but are NOT on disk in projects/<name>/work-items.json. The /state/ fetch
|
|
9
|
-
// recovers raw disk state — these get overlaid from the most recent
|
|
10
|
-
// /api/status snapshot (window._lastWorkItems) so the table still renders the
|
|
11
|
-
// PR chip, pending-reason badge, blockedBy hint, etc. The disk fields win
|
|
12
|
-
// for everything else, so a status flip from `pending` → `done` lands within
|
|
13
|
-
// one /state poll regardless of how stale /api/status got.
|
|
14
|
-
const _WI_ENRICHMENT_FIELDS = [
|
|
15
|
-
'_pr', '_prUrl', '_pendingReason', '_skipReason', '_blockedBy',
|
|
16
|
-
'_humanFeedback', '_reopened', '_managedSpawnPartial', '_securityFlag',
|
|
17
|
-
'_artifacts', 'referencesCount', 'acceptanceCriteriaCount',
|
|
18
|
-
// W-mq5xg5e9000nec0e — slim shape marker so the detail/edit modals know
|
|
19
|
-
// to hydrate the full description via GET /api/work-items/<id> rather than
|
|
20
|
-
// pre-filling textareas with the truncated snippet.
|
|
21
|
-
'_descriptionTruncated',
|
|
22
|
-
// W-mq08kuog001110a6 — _preDispatchEval IS persisted to disk by
|
|
23
|
-
// engine/dispatch.js#_persistInvalidWorkItem, but the slim /api/status
|
|
24
|
-
// overlay may drop it on later passes. Carry it across the overlay so
|
|
25
|
-
// the Needs-Attention badge survives /state polls.
|
|
26
|
-
'_preDispatchEval',
|
|
27
|
-
];
|
|
28
|
-
|
|
29
7
|
// W-mq08kuog001110a6 — Needs-Attention badge for stuck pending work items.
|
|
30
8
|
//
|
|
31
9
|
// A pending WI is "needs attention" when the engine is intentionally refusing
|
|
@@ -40,10 +18,21 @@ const _WI_ENRICHMENT_FIELDS = [
|
|
|
40
18
|
// Does NOT trigger for transient reasons that auto-clear:
|
|
41
19
|
// * cooldown / retry_cooldown — wait it out
|
|
42
20
|
// * already_dispatched — engine reconciles on next tick
|
|
21
|
+
// * agents_busy — an eligible agent exists but every one is currently busy;
|
|
22
|
+
// the engine auto-dispatches on a later tick when a slot frees. This is
|
|
23
|
+
// the transient sibling of no_agent (W-mrzlzgju0002e6f0): the engine
|
|
24
|
+
// classifies the two at the source via resolveAgentReservation, so the
|
|
25
|
+
// UI never has to guess whether a "no agent" pending state is actionable.
|
|
43
26
|
// * branch_locked — wait for the holding dispatch
|
|
27
|
+
// * worktree_branch_held — configured checkout is re-checked each tick
|
|
44
28
|
// * dependency_unmet — auto-clears when upstream dep finishes (engine discovery loop + DEPENDENCY_MET watch)
|
|
45
29
|
// * live_checkout_busy — one live-mode dispatch at a time; clears when running dispatch finishes
|
|
46
30
|
//
|
|
31
|
+
// no_agent (kept as a trigger) now means the engine found NO eligible agent
|
|
32
|
+
// that could EVER take this work (empty/over-budget agent roster or an
|
|
33
|
+
// unresolvable route) — a genuine operator-actionable state. Transient
|
|
34
|
+
// capacity exhaustion is emitted as agents_busy instead.
|
|
35
|
+
//
|
|
47
36
|
// Returns { kind, short, full } when the item needs attention, else null.
|
|
48
37
|
// Pure helper (no DOM / escapeHtml deps) so it can be unit-tested in Node.
|
|
49
38
|
function needsAttentionInfo(item) {
|
|
@@ -75,7 +64,8 @@ function needsAttentionInfo(item) {
|
|
|
75
64
|
return { kind: 'pr_not_found', short: prMsg, full: prMsg };
|
|
76
65
|
}
|
|
77
66
|
if (reason === 'no_agent') {
|
|
78
|
-
var noAgentMsg = 'No agent
|
|
67
|
+
var noAgentMsg = 'No eligible agent can take this work (type=' + (item.type || 'unknown') +
|
|
68
|
+
'). The agent roster is empty, over budget, or routing.md has no resolvable match — assign or configure an agent.';
|
|
79
69
|
return { kind: 'no_agent', short: noAgentMsg, full: noAgentMsg };
|
|
80
70
|
}
|
|
81
71
|
if (reason === 'budget_exceeded') {
|
|
@@ -86,63 +76,27 @@ function needsAttentionInfo(item) {
|
|
|
86
76
|
return null;
|
|
87
77
|
}
|
|
88
78
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
try {
|
|
103
|
-
const r = await fetch('/state/projects/' + encodeURIComponent(name) + '/work-items.json');
|
|
104
|
-
if (!r.ok) return [];
|
|
105
|
-
const arr = await r.json();
|
|
106
|
-
if (!Array.isArray(arr)) return [];
|
|
107
|
-
return arr.map((item) => {
|
|
108
|
-
if (!item || typeof item !== 'object') return null;
|
|
109
|
-
const stamped = Object.assign({}, item, { _source: name });
|
|
110
|
-
const cached = cachedById && cachedById.get(item.id);
|
|
111
|
-
if (cached) {
|
|
112
|
-
for (const k of _WI_ENRICHMENT_FIELDS) {
|
|
113
|
-
if (stamped[k] === undefined && cached[k] !== undefined) {
|
|
114
|
-
stamped[k] = cached[k];
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
// referencesCount / acceptanceCriteriaCount don't live on disk;
|
|
119
|
-
// derive from the raw arrays if present so the table icons render
|
|
120
|
-
// even before the cached snapshot catches up.
|
|
121
|
-
if (stamped.referencesCount == null && Array.isArray(stamped.references)) {
|
|
122
|
-
stamped.referencesCount = stamped.references.length;
|
|
123
|
-
}
|
|
124
|
-
if (stamped.acceptanceCriteriaCount == null && Array.isArray(stamped.acceptanceCriteria)) {
|
|
125
|
-
stamped.acceptanceCriteriaCount = stamped.acceptanceCriteria.length;
|
|
126
|
-
}
|
|
127
|
-
return stamped;
|
|
128
|
-
}).filter(Boolean);
|
|
129
|
-
} catch {
|
|
130
|
-
return [];
|
|
131
|
-
}
|
|
132
|
-
}));
|
|
133
|
-
// Flatten + dedupe by id (a project rename mid-flight could double-list).
|
|
134
|
-
const out = [];
|
|
135
|
-
const seen = new Set();
|
|
136
|
-
for (const arr of results) {
|
|
137
|
-
for (const item of arr) {
|
|
138
|
-
if (!item.id || seen.has(item.id)) continue;
|
|
139
|
-
seen.add(item.id);
|
|
140
|
-
out.push(item);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
return out;
|
|
79
|
+
function formatPendingReason(item, detailed) {
|
|
80
|
+
var reason = item && item._pendingReason ? String(item._pendingReason) : '';
|
|
81
|
+
if (!reason) return '';
|
|
82
|
+
if (reason === 'already_dispatched') return 'Queued — waiting for available agent slot';
|
|
83
|
+
if (reason === 'agents_busy') return 'Waiting for an available agent — all eligible agents are busy';
|
|
84
|
+
if (reason !== 'worktree_branch_held') return reason.replace(/_/g, ' ');
|
|
85
|
+
if (!detailed) return 'operator checkout holds branch';
|
|
86
|
+
|
|
87
|
+
var diagnostic = item._worktreeBranchHeld || {};
|
|
88
|
+
var branch = diagnostic.branch || item.branch || '(unknown branch)';
|
|
89
|
+
var holderPath = diagnostic.holderPath || '(unknown checkout path)';
|
|
90
|
+
return 'Branch ' + branch + ' is checked out by configured operator checkout ' + holderPath +
|
|
91
|
+
'. The work item remains pending and will dispatch automatically once that checkout releases the branch.';
|
|
144
92
|
}
|
|
145
93
|
|
|
94
|
+
// Work items are served fresh by GET /api/work-items (see refresh.js), which
|
|
95
|
+
// re-runs queries.getWorkItems() server-side so _pr/_prUrl/_pendingReason/
|
|
96
|
+
// _preDispatchEval/_descriptionTruncated are always current. The per-project
|
|
97
|
+
// work-items.json mirrors this file used to read were retired by the SQL-only
|
|
98
|
+
// cutover, and the client-side enrichment overlay they fed is gone with them.
|
|
99
|
+
|
|
146
100
|
// Track retry state per work item so loading/success/error survives re-renders
|
|
147
101
|
const _wiRetryState = {}; // { [id]: { status: 'pending'|'done'|'error', message?, until? } }
|
|
148
102
|
function setWiRetryState(id, state) { _wiRetryState[id] = state; }
|
|
@@ -156,15 +110,15 @@ function getWiRetryState(id) {
|
|
|
156
110
|
function wiRetryBtn(item) {
|
|
157
111
|
const rs = getWiRetryState(item.id);
|
|
158
112
|
if (rs && rs.status === 'pending') {
|
|
159
|
-
return '<span style="font-size:var(--text-
|
|
113
|
+
return '<span style="font-size:var(--text-sm);padding:1px 6px;color:var(--yellow);border:1px solid rgba(210,153,34,0.35);background:rgba(210,153,34,0.1);border-radius:3px;cursor:wait;margin-left:4px">Retrying\u2026</span>';
|
|
160
114
|
}
|
|
161
115
|
if (rs && rs.status === 'done') {
|
|
162
|
-
return '<span style="font-size:var(--text-
|
|
116
|
+
return '<span style="font-size:var(--text-sm);padding:1px 6px;color:var(--green);border:1px solid rgba(63,185,80,0.35);background:rgba(63,185,80,0.1);border-radius:3px;margin-left:4px">Requeued</span>';
|
|
163
117
|
}
|
|
164
118
|
if (rs && rs.status === 'error') {
|
|
165
|
-
return '<span style="font-size:var(--text-
|
|
119
|
+
return '<span style="font-size:var(--text-sm);padding:1px 6px;color:var(--red);border:1px solid rgba(248,81,73,0.35);background:rgba(248,81,73,0.1);border-radius:3px;margin-left:4px;cursor:pointer" title="' + escapeHtml(rs.message || 'Retry failed') + ' — click to try again" onclick="event.stopPropagation();retryWorkItem(\'' + escapeHtml(item.id) + '\',\'' + escapeHtml(item._source || '') + '\')">Retry failed</span>';
|
|
166
120
|
}
|
|
167
|
-
return '<button class="pr-pager-btn" style="font-size:var(--text-
|
|
121
|
+
return '<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:1px 6px;color:var(--yellow);border-color:var(--yellow);margin-left:4px" onclick="event.stopPropagation();retryWorkItem(\'' + escapeHtml(item.id) + '\',\'' + escapeHtml(item._source || '') + '\')">Retry</button>';
|
|
168
122
|
}
|
|
169
123
|
|
|
170
124
|
function wiRow(item) {
|
|
@@ -177,13 +131,13 @@ function wiRow(item) {
|
|
|
177
131
|
const prLink = item._pr
|
|
178
132
|
? '<a class="pr-title" href="' + escapeHtml(item._prUrl || '#') + '" target="_blank" style="font-size:var(--text-sm)">' + escapeHtml(item._pr) + '</a>'
|
|
179
133
|
: (item.branchStrategy === 'shared-branch' && item.status === 'done')
|
|
180
|
-
? '<span style="font-size:var(--text-
|
|
181
|
-
: '<span style="color:var(--muted)">—</span>';
|
|
134
|
+
? '<span style="font-size:var(--text-sm);color:var(--muted)" title="Part of shared branch — aggregate PR created at verify stage">shared branch</span>'
|
|
135
|
+
: (item.failReason ? '' : '<span style="color:var(--muted)">—</span>');
|
|
182
136
|
// W-mqrdn6qq / W-mrkqc0pl — ALL failure/error snippets (retryable AND
|
|
183
137
|
// non-retryable) surface their red failReason snippet in the PR column,
|
|
184
138
|
// never the Agent column.
|
|
185
139
|
var failSnippet = item.failReason
|
|
186
|
-
? '<span style="display:block;font-size:var(--text-
|
|
140
|
+
? '<span style="display:block;font-size:var(--text-sm);color:var(--red)" title="' + escapeHtml(item.failReason) + '">' + escapeHtml(item.failReason.slice(0, 30)) + '</span>'
|
|
187
141
|
: '';
|
|
188
142
|
// PR follow-up chip (W-mpej3cox00099466) — surfaced when the WI was spun
|
|
189
143
|
// off from a PR comment via meta.pr_followup. Links to the parent PR.
|
|
@@ -193,16 +147,16 @@ function wiRow(item) {
|
|
|
193
147
|
var prRef = prFollowup.parent_pr_id || prFollowup.parent_pr_url;
|
|
194
148
|
var prNumMatch = String(prRef).match(/(\d+)(?!.*\d)/);
|
|
195
149
|
var prLabel = prNumMatch ? ('PR #' + prNumMatch[1]) : 'parent PR';
|
|
196
|
-
followupChip = ' <a class="pr-badge draft" style="font-size:var(--text-
|
|
150
|
+
followupChip = ' <a class="pr-badge draft" style="font-size:var(--text-sm);text-decoration:none" target="_blank" rel="noopener" href="' + escapeHtml(prFollowup.parent_pr_url) + '" title="Follow-up dispatched from ' + escapeHtml(prRef) + (prFollowup.parent_comment_author ? ' by ' + escapeHtml(prFollowup.parent_comment_author) : '') + '" onclick="event.stopPropagation()">↩ from ' + escapeHtml(prLabel) + '</a>';
|
|
197
151
|
}
|
|
198
152
|
return '<tr data-wi-id="' + escapeHtml(item.id) + '" style="cursor:pointer" onclick="if(shouldIgnoreSelectionClick(event))return;openWorkItemDetail(\'' + escapeHtml(item.id) + '\',\'' + escapeHtml(item._source || '') + '\')">' +
|
|
199
153
|
'<td style="min-width:280px;max-width:320px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="' + escapeHtml((item.title || '').slice(0, 200)) + '">' + escapeHtml(item.title || '') + followupChip + '</td>' +
|
|
200
154
|
'<td><span style="font-size:var(--text-sm);color:var(--muted)">' + escapeHtml(item._source || '') + '</span>' +
|
|
201
|
-
(item.scope === 'fan-out' ? ' <span class="pr-badge ' + (item.status === 'done' || item.status === 'failed' ? 'draft' : 'building') + '" style="font-size:var(--text-
|
|
155
|
+
(item.scope === 'fan-out' ? ' <span class="pr-badge ' + (item.status === 'done' || item.status === 'failed' ? 'draft' : 'building') + '" style="font-size:var(--text-sm)">fan-out</span>' : '') + '</td>' +
|
|
202
156
|
'<td>' + typeBadge(item.type) + '</td>' +
|
|
203
157
|
'<td>' + priBadge(item.priority) + '</td>' +
|
|
204
158
|
'<td>' + statusBadge(item.status || 'pending') +
|
|
205
|
-
(item._reopened ? ' <span style="font-size:var(--text-
|
|
159
|
+
(item._reopened ? ' <span style="font-size:var(--text-sm);color:var(--purple);margin-left:4px" title="This item was reopened from a previously completed state">reopened</span>' : '') +
|
|
206
160
|
(function() {
|
|
207
161
|
// W-mq08kuog001110a6 — Needs-Attention badge for stuck pending WIs.
|
|
208
162
|
// Click anywhere on the row to open the modal where the full reason
|
|
@@ -210,12 +164,12 @@ function wiRow(item) {
|
|
|
210
164
|
var info = needsAttentionInfo(item);
|
|
211
165
|
if (!info) return '';
|
|
212
166
|
var tooltip = (info.short || '').replace(/\s+/g, ' ').slice(0, 120);
|
|
213
|
-
return ' <span class="pr-badge needs-attention" style="font-size:var(--text-
|
|
167
|
+
return ' <span class="pr-badge needs-attention" style="font-size:var(--text-sm);margin-left:4px" title="' + escapeHtml(tooltip) + ' — click row for full reason">⚠ Needs attention</span>';
|
|
214
168
|
})() +
|
|
215
|
-
(item._pendingReason && item.status === 'pending' && item._pendingReason !== 'already_dispatched' ? ' <span style="font-size:var(--text-
|
|
216
|
-
(item._pendingReason === 'already_dispatched' && item.status === 'pending' ? ' <span style="font-size:var(--text-
|
|
169
|
+
(item._pendingReason && item.status === 'pending' && item._pendingReason !== 'already_dispatched' ? ' <span style="font-size:var(--text-sm);color:var(--muted);margin-left:4px" title="Pending reason: ' + escapeHtml(formatPendingReason(item, true)) + '">' + escapeHtml(formatPendingReason(item, false)) + '</span>' : '') +
|
|
170
|
+
(item._pendingReason === 'already_dispatched' && item.status === 'pending' ? ' <span style="font-size:var(--text-sm);color:var(--blue);margin-left:4px" title="In dispatch queue, waiting to be assigned">queued</span>' : '') +
|
|
217
171
|
(item._managedSpawnPartial && Array.isArray(item._managedSpawnPartial.failed) && item._managedSpawnPartial.failed.length
|
|
218
|
-
? ' <span style="font-size:var(--text-
|
|
172
|
+
? ' <span style="font-size:var(--text-sm);color:var(--yellow);margin-left:4px;border:1px solid var(--yellow);padding:0 4px;border-radius:6px" title="managed-spawn: '
|
|
219
173
|
+ escapeHtml(((item._managedSpawnPartial.healthy || []).length) + '/' + (((item._managedSpawnPartial.healthy || []).length) + item._managedSpawnPartial.failed.length))
|
|
220
174
|
+ ' healthy — failed: '
|
|
221
175
|
+ escapeHtml(item._managedSpawnPartial.failed.map(function(f){ return (f && f.name) || '?'; }).join(', '))
|
|
@@ -223,7 +177,7 @@ function wiRow(item) {
|
|
|
223
177
|
+ escapeHtml(((item._managedSpawnPartial.healthy || []).length) + '/' + (((item._managedSpawnPartial.healthy || []).length) + item._managedSpawnPartial.failed.length))
|
|
224
178
|
+ ' healthy</span>'
|
|
225
179
|
: '') +
|
|
226
|
-
(item._skipReason && item.status === 'pending' && item._skipReason !== 'live_checkout_busy' ? ' <span style="font-size:var(--text-
|
|
180
|
+
(item._skipReason && item.status === 'pending' && item._skipReason !== 'live_checkout_busy' ? ' <span style="font-size:var(--text-sm);color:var(--yellow);margin-left:4px" title="Dispatch blocked: ' + escapeHtml(item._skipReason) + (item._blockedBy ? ' (by ' + escapeHtml(item._blockedBy) + ')' : '') + '">' + escapeHtml(item._skipReason.replace(/_/g, ' ')) + (item._blockedBy ? ' <span style="color:var(--muted)">(' + escapeHtml(item._blockedBy) + ')</span>' : '') + '</span>' : '') +
|
|
227
181
|
(item.status === 'failed' ? ' ' + wiRetryBtn(item) : '') +
|
|
228
182
|
'</td>' +
|
|
229
183
|
'<td>' +
|
|
@@ -233,7 +187,7 @@ function wiRow(item) {
|
|
|
233
187
|
'</td>' +
|
|
234
188
|
'<td>' + prLink + (failSnippet ? failSnippet : '') + '</td>' +
|
|
235
189
|
'<td><span class="pr-date">' + escapeHtml((item.created || '').slice(0, 16).replace('T', ' ')) + '</span></td>' +
|
|
236
|
-
'<td style="white-space:nowrap;font-size:var(--text-
|
|
190
|
+
'<td style="white-space:nowrap;font-size:var(--text-sm);color:var(--muted)">' +
|
|
237
191
|
(function() {
|
|
238
192
|
// /api/status slims the full arrays into integer counts
|
|
239
193
|
// (dashboard.js:1796-1797: referencesCount / acceptanceCriteriaCount).
|
|
@@ -249,15 +203,15 @@ function wiRow(item) {
|
|
|
249
203
|
(acCount ? '<span title="' + acCount + ' acceptance criteria">☑' + acCount + '</span>' : '');
|
|
250
204
|
})() +
|
|
251
205
|
'</td>' +
|
|
252
|
-
'<td style="white-space:nowrap">' +
|
|
206
|
+
'<td style="white-space:nowrap;text-align:right">' +
|
|
253
207
|
// W-mqqa76pi — "View live output" opens the agent slide-in panel (live-output
|
|
254
208
|
// tab auto-selected when the agent is working). Only for genuinely active
|
|
255
209
|
// dispatches with a resolvable assigned agent id.
|
|
256
|
-
((item.status === 'dispatched' && (item.dispatched_to || item.agent)) ? '<button class="pr-pager-btn" style="font-size:var(--text-
|
|
257
|
-
((item.status === 'pending' || item.status === 'failed') ? '<button class="pr-pager-btn" style="font-size:var(--text-
|
|
258
|
-
((item.status === 'done' || item.status === 'failed') ? '<button class="pr-pager-btn" style="font-size:var(--text-
|
|
259
|
-
((item.status === 'done' || item.status === 'failed') && !item._humanFeedback ? '<button class="btn-action" style="margin-right:4px" onclick="event.stopPropagation();feedbackWorkItem(\'' + escapeHtml(item.id) + '\',\'' + escapeHtml(item._source || '') + '\')" title="Give feedback">👍👎</button>' : (item._humanFeedback ? '<span style="font-size:var(--text-
|
|
260
|
-
((item.status === 'pending' || item.status === 'dispatched' || item.status === 'queued' || item.status === 'failed') ? '<button class="pr-pager-btn" style="font-size:var(--text-
|
|
210
|
+
((item.status === 'dispatched' && (item.dispatched_to || item.agent)) ? '<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:1px 6px;color:var(--green);border-color:var(--green);margin-right:4px" onclick="event.stopPropagation();openArtifact(\'agent\',\'' + escapeHtml(item.dispatched_to || item.agent) + '\')" title="View live output">📺</button>' : '') +
|
|
211
|
+
((item.status === 'pending' || item.status === 'failed') ? '<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:1px 6px;color:var(--blue);border-color:var(--blue);margin-right:4px" onclick="event.stopPropagation();editWorkItem(\'' + escapeHtml(item.id) + '\',\'' + escapeHtml(item._source || '') + '\')" title="Edit work item">✎</button>' : '') +
|
|
212
|
+
((item.status === 'done' || item.status === 'failed') ? '<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:1px 6px;color:var(--muted);border-color:var(--muted);margin-right:4px" onclick="event.stopPropagation();archiveWorkItem(\'' + escapeHtml(item.id) + '\',\'' + escapeHtml(item._source || '') + '\')" title="Archive work item">📦</button>' : '') +
|
|
213
|
+
((item.status === 'done' || item.status === 'failed') && !item._humanFeedback ? '<button class="btn-action" style="margin-right:4px" onclick="event.stopPropagation();feedbackWorkItem(\'' + escapeHtml(item.id) + '\',\'' + escapeHtml(item._source || '') + '\')" title="Give feedback">👍👎</button>' : (item._humanFeedback ? '<span style="font-size:var(--text-sm)" title="Feedback given">' + (item._humanFeedback.rating === 'up' ? '👍' : '👎') + '</span> ' : '')) +
|
|
214
|
+
((item.status === 'pending' || item.status === 'dispatched' || item.status === 'queued' || item.status === 'failed') ? '<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:1px 6px;color:var(--orange);border-color:var(--orange);margin-right:4px" onclick="event.stopPropagation();cancelWorkItem(\'' + escapeHtml(item.id) + '\',\'' + escapeHtml(item._source || '') + '\')" title="Cancel work item and kill agent">🚫</button>' : '') +
|
|
261
215
|
'<button class="btn-destructive" onclick="event.stopPropagation();deleteWorkItem(\'' + escapeHtml(item.id) + '\',\'' + escapeHtml(item._source || '') + '\')" title="Delete work item and kill agent">✕</button>' +
|
|
262
216
|
'</td>' +
|
|
263
217
|
'</tr>';
|
|
@@ -705,6 +659,24 @@ function _wiDeriveCanonicalPrIdFromUrl(url) {
|
|
|
705
659
|
return null;
|
|
706
660
|
}
|
|
707
661
|
|
|
662
|
+
// W-ms0ytfdd000d5a26 — provenance hint for the WI detail modal's Model field.
|
|
663
|
+
// Explains WHERE the shown model came from so an operator can tell a recorded
|
|
664
|
+
// fact from a projection. Mirrors the `source` values produced by
|
|
665
|
+
// engine/execution-model.js#resolveWorkItemModel.
|
|
666
|
+
function _wiModelSourceHint(m) {
|
|
667
|
+
var runtime = m.runtime || 'runtime';
|
|
668
|
+
var hint;
|
|
669
|
+
if (m.source === 'runtime-reported') hint = 'reported by ' + runtime;
|
|
670
|
+
else if (m.source === 'requested') hint = 'sent to ' + runtime + ' at dispatch';
|
|
671
|
+
else if (m.source === 'runtime-default') hint = 'no model pinned';
|
|
672
|
+
else if (m.source === 'configured') hint = 'pinned in current config';
|
|
673
|
+
else hint = 'no runtime recorded';
|
|
674
|
+
// Nothing was persisted for this item, so the value is what the NEXT dispatch
|
|
675
|
+
// would use — not a record of a past run.
|
|
676
|
+
if (!m.persisted) hint += ' · projected (no dispatch record)';
|
|
677
|
+
return hint;
|
|
678
|
+
}
|
|
679
|
+
|
|
708
680
|
function _wiRenderDetail(item) {
|
|
709
681
|
const field = (label, value) => value ? '<div style="margin-bottom:8px"><span style="color:var(--muted);font-size:var(--text-sm);text-transform:uppercase;letter-spacing:0.5px">' + label + '</span><div style="margin-top:2px">' + value + '</div></div>' : '';
|
|
710
682
|
const badge = (cls, text) => '<span class="pr-badge ' + cls + '">' + escapeHtml(text) + '</span>';
|
|
@@ -713,14 +685,14 @@ function _wiRenderDetail(item) {
|
|
|
713
685
|
let html = '<div style="display:flex;flex-direction:column;gap:4px;font-size:var(--text-lg)">';
|
|
714
686
|
html += '<div style="display:flex;gap:8px;align-items:center;margin-bottom:8px">' +
|
|
715
687
|
badge(statusCls, item.status || 'pending') +
|
|
716
|
-
(item._reopened ? ' <span style="font-size:var(--text-
|
|
688
|
+
(item._reopened ? ' <span style="font-size:var(--text-sm);color:var(--purple);margin-left:4px" title="This item was reopened from a previously completed state">reopened</span>' : '') + ' ' +
|
|
717
689
|
'<span class="dispatch-type ' + (item.type || 'implement') + '">' + escapeHtml(item.type || 'implement') + '</span>' +
|
|
718
690
|
'<span class="prd-item-priority ' + (item.priority || '') + '">' + escapeHtml(item.priority || 'medium') + '</span>' +
|
|
719
691
|
(function() {
|
|
720
692
|
// W-mq08kuog001110a6 — Needs-Attention chip in the detail modal header.
|
|
721
693
|
var info = needsAttentionInfo(item);
|
|
722
694
|
if (!info) return '';
|
|
723
|
-
return ' <span class="pr-badge needs-attention" style="font-size:var(--text-
|
|
695
|
+
return ' <span class="pr-badge needs-attention" style="font-size:var(--text-sm);margin-left:4px" title="See "Why this is blocked" below">⚠ Needs attention</span>';
|
|
724
696
|
})() +
|
|
725
697
|
'</div>';
|
|
726
698
|
// W-mqqa76pi — Prominent "View live output" button for an actively-dispatched
|
|
@@ -764,6 +736,22 @@ function _wiRenderDetail(item) {
|
|
|
764
736
|
}
|
|
765
737
|
html += field('Description', '<div id="wi-detail-desc" style="font-size:var(--text-md);max-height:320px;overflow-y:auto;padding:8px 10px;background:var(--surface2);border:1px solid var(--border);border-radius:var(--radius-sm)">' + _descHtml + '</div>');
|
|
766
738
|
html += field('Agent', escapeHtml(item.dispatched_to || item.agent || 'Auto'));
|
|
739
|
+
// W-ms0ytfdd000d5a26 — the model this work item actually ran on. Resolved
|
|
740
|
+
// server-side (engine/queries.js#resolveWorkItemModelInfo) so persisted
|
|
741
|
+
// dispatch evidence — what the runtime reported, else what Minions requested
|
|
742
|
+
// — always beats current config. Items with no dispatch record show the
|
|
743
|
+
// projection from current config, marked as such. When nothing concrete is
|
|
744
|
+
// known we render "<runtime> default" rather than blanking or guessing.
|
|
745
|
+
(function() {
|
|
746
|
+
var m = item._model;
|
|
747
|
+
if (!m || typeof m !== 'object') return;
|
|
748
|
+
var label = m.label || m.model;
|
|
749
|
+
if (!label) return;
|
|
750
|
+
var hint = _wiModelSourceHint(m);
|
|
751
|
+
html += field('Model',
|
|
752
|
+
'<code style="font-size:var(--text-sm);background:var(--surface2);padding:2px 6px;border-radius:var(--radius-sm)">' + escapeHtml(String(label)) + '</code>' +
|
|
753
|
+
(hint ? ' <span style="color:var(--muted);font-size:var(--text-sm)">' + escapeHtml(hint) + '</span>' : ''));
|
|
754
|
+
})();
|
|
767
755
|
html += field('Project', escapeHtml(item._source || 'central'));
|
|
768
756
|
// P-714ef144 — surface meta.workdir in the detail modal so operators can
|
|
769
757
|
// confirm the agent's actual cwd. Renders nothing when unset (which is
|
|
@@ -791,7 +779,7 @@ function _wiRenderDetail(item) {
|
|
|
791
779
|
(item.cancelledAt ? '<div style="margin-top:2px;color:var(--muted);font-size:var(--text-sm)">' + escapeHtml(formatLocalDateTime(item.cancelledAt)) + '</div>' : '');
|
|
792
780
|
html += field('Cancellation Reason', _cancelBody);
|
|
793
781
|
}
|
|
794
|
-
if (item._pendingReason && item.status === 'pending') html += field('Pending Reason',
|
|
782
|
+
if (item._pendingReason && item.status === 'pending') html += field('Pending Reason', escapeHtml(formatPendingReason(item, true)));
|
|
795
783
|
if (item._managedSpawnPartial && Array.isArray(item._managedSpawnPartial.failed) && item._managedSpawnPartial.failed.length) {
|
|
796
784
|
var _msp = item._managedSpawnPartial;
|
|
797
785
|
var _mspHealthy = Array.isArray(_msp.healthy) ? _msp.healthy : [];
|
|
@@ -899,8 +887,8 @@ function _wiRenderDetail(item) {
|
|
|
899
887
|
// Artifacts — branch (non-navigation) + navigation chips routed through
|
|
900
888
|
// renderArtifactLink so chip click → openArtifact() → modal-stack push +
|
|
901
889
|
// URL hash + browser-Back integration come for free (P-e265cd31).
|
|
902
|
-
// NOTE (issue #603): item._source is the WI's *project name* (stamped
|
|
903
|
-
//
|
|
890
|
+
// NOTE (issue #603): item._source is the WI's *project name* (stamped
|
|
891
|
+
// server-side by queries.getWorkItems / used by every action button to route
|
|
904
892
|
// retry/cancel/delete/etc.), NOT a plan/PRD file id. A prior "Source" chip
|
|
905
893
|
// here treated item._source as if it held a genuine source-plan/PRD
|
|
906
894
|
// filename, which rendered a bogus chip labeled with the project name on
|
|
@@ -1189,9 +1177,9 @@ function _wiInjectSourceChipFromFrontmatter(content, containerId) {
|
|
|
1189
1177
|
}
|
|
1190
1178
|
|
|
1191
1179
|
if (typeof window !== 'undefined') {
|
|
1192
|
-
window.MinionsWork = { wiRow, renderWorkItems, editWorkItem, submitWorkItemEdit, deleteWorkItem, archiveWorkItem, toggleWorkItemArchive, retryWorkItem, wiPrev, wiNext, feedbackWorkItem,
|
|
1180
|
+
window.MinionsWork = { wiRow, renderWorkItems, editWorkItem, submitWorkItemEdit, deleteWorkItem, archiveWorkItem, toggleWorkItemArchive, retryWorkItem, wiPrev, wiNext, feedbackWorkItem, openCreateWorkItemModal, openWorkItemDetail, openAllWorkItems, viewAgentOutput, openInboxNote, needsAttentionInfo };
|
|
1193
1181
|
}
|
|
1194
1182
|
// exported for testing — pure helpers safe to require under Node (no DOM deps).
|
|
1195
1183
|
if (typeof module !== 'undefined' && module.exports) {
|
|
1196
|
-
module.exports = { needsAttentionInfo };
|
|
1184
|
+
module.exports = { needsAttentionInfo, formatPendingReason };
|
|
1197
1185
|
}
|