diff --git a/frontend/packages/dashboard/src/common.css b/frontend/packages/dashboard/src/common.css index 47bba74c..ac4f238e 100644 --- a/frontend/packages/dashboard/src/common.css +++ b/frontend/packages/dashboard/src/common.css @@ -78,6 +78,10 @@ code { color: var(--cyan); border-color: var(--cyan); text-shadow: 0 0 6px rgba(137, 220, 235, 0.4); } +.badge-loose-ends { + color: var(--purple); border-color: var(--purple); + text-shadow: 0 0 6px rgba(203, 166, 247, 0.4); +} /* Context-window usage badges on dashboard container rows. */ .badge-ctx-ok { color: var(--green); border-color: var(--green); diff --git a/frontend/packages/dashboard/src/tabs.js b/frontend/packages/dashboard/src/tabs.js index 9b6fc238..f363009d 100644 --- a/frontend/packages/dashboard/src/tabs.js +++ b/frontend/packages/dashboard/src/tabs.js @@ -805,6 +805,26 @@ window.marked = marked; }, `⏰ ${c.pending_reminders}`)); } + // Pending questions where this agent is the asker (awaiting an + // answer) or the target (owes a reply). Derived live from + // questionsState so the badge updates instantly on QuestionAdded / + // QuestionResolved without a separate backend field. + const agentQCount = questionsState.pending.filter( + (q) => q.asker === c.name || q.target === c.name, + ).length; + if (agentQCount > 0) { + const askerCount = questionsState.pending.filter((q) => q.asker === c.name).length; + const targetCount = questionsState.pending.filter((q) => q.target === c.name).length; + const parts = []; + if (askerCount > 0) parts.push(`${askerCount} asked`); + if (targetCount > 0) parts.push(`${targetCount} to answer`); + head.append(el('span', + { + class: 'badge badge-loose-ends', + title: `pending questions: ${parts.join(', ')} — see the Q33R1ES tab`, + }, + `❓ ${agentQCount}`)); + } if (c.ctx_tokens != null) { const k = Math.round(c.ctx_tokens / 1000); // Thresholds track the model's real context window when the @@ -1459,6 +1479,7 @@ window.marked = marked; question_refs: ev.question_refs || [], }); renderQuestions(); + renderContainersFromState(); } function applyQuestionResolved(ev) { const idx = questionsState.pending.findIndex((q) => q.id === ev.id); @@ -1488,6 +1509,7 @@ window.marked = marked; } } renderQuestions(); + renderContainersFromState(); } // Filter selection for the questions section. Persisted so the // operator's preferred view (all / operator-targeted / peer)