feat(#1290): show per-agent loose-end question count in dashboard
Add a '❓ N' badge to each container row showing the count of pending questions where the agent is the asker (awaiting an answer) or the target (owes a reply). Derived live from questionsState — no backend field needed. Details: - New .badge-loose-ends CSS class (purple, matching Q33R1ES section colour) - Badge tooltip breaks down the count: 'N asked, N to answer' - renderContainersFromState() called on QuestionAdded/QuestionResolved so the badge updates instantly when question state changes - Existing ⏰ reminders badge kept separate (different signal)
This commit is contained in:
parent
bddeb5478e
commit
750f64b7f0
2 changed files with 26 additions and 0 deletions
|
|
@ -78,6 +78,10 @@ code {
|
||||||
color: var(--cyan); border-color: var(--cyan);
|
color: var(--cyan); border-color: var(--cyan);
|
||||||
text-shadow: 0 0 6px rgba(137, 220, 235, 0.4);
|
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. */
|
/* Context-window usage badges on dashboard container rows. */
|
||||||
.badge-ctx-ok {
|
.badge-ctx-ok {
|
||||||
color: var(--green); border-color: var(--green);
|
color: var(--green); border-color: var(--green);
|
||||||
|
|
|
||||||
|
|
@ -805,6 +805,26 @@ window.marked = marked;
|
||||||
},
|
},
|
||||||
`⏰ ${c.pending_reminders}`));
|
`⏰ ${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) {
|
if (c.ctx_tokens != null) {
|
||||||
const k = Math.round(c.ctx_tokens / 1000);
|
const k = Math.round(c.ctx_tokens / 1000);
|
||||||
// Thresholds track the model's real context window when the
|
// Thresholds track the model's real context window when the
|
||||||
|
|
@ -1459,6 +1479,7 @@ window.marked = marked;
|
||||||
question_refs: ev.question_refs || [],
|
question_refs: ev.question_refs || [],
|
||||||
});
|
});
|
||||||
renderQuestions();
|
renderQuestions();
|
||||||
|
renderContainersFromState();
|
||||||
}
|
}
|
||||||
function applyQuestionResolved(ev) {
|
function applyQuestionResolved(ev) {
|
||||||
const idx = questionsState.pending.findIndex((q) => q.id === ev.id);
|
const idx = questionsState.pending.findIndex((q) => q.id === ev.id);
|
||||||
|
|
@ -1488,6 +1509,7 @@ window.marked = marked;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
renderQuestions();
|
renderQuestions();
|
||||||
|
renderContainersFromState();
|
||||||
}
|
}
|
||||||
// Filter selection for the questions section. Persisted so the
|
// Filter selection for the questions section. Persisted so the
|
||||||
// operator's preferred view (all / operator-targeted / peer)
|
// operator's preferred view (all / operator-targeted / peer)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue