feat(frontend): surface UI des tâches de fond (F1-F4)

Expose les tâches de fond côté front : modèle domaine et
normalisation du work state, panneau ProjectWorkStatePanel,
intégration dans LayoutGrid et ProjectsView, et abonnement aux
événements backgroundTaskChanged / agentInboxChanged /
agentWakeChanged pour rafraîchir l'état. npm run build + npm test
(449) verts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-02 21:33:51 +02:00
parent e05edc6863
commit 5d88c952ec
6 changed files with 450 additions and 9 deletions

View File

@ -317,6 +317,16 @@ function LeafView({
? workState?.agents.find((row) => row.agentId === agentId)
: undefined;
const activeTicket = agentWork?.tickets.find((ticket) => ticket.status === "inProgress");
const queuedTickets = agentWork?.tickets.filter((ticket) => ticket.status === "queued") ?? [];
const inboxDepth = Math.max(
agentWork?.inboxDepth ?? 0,
agentWork?.inbox?.length ?? 0,
queuedTickets.length,
);
const completedBackgroundTasks =
agentWork?.backgroundTasks?.filter((task) =>
task.status === "completed" || task.status === "failed" || task.status === "cancelled",
) ?? [];
const busyByWorkState = agentWork?.busy.state === "busy" || Boolean(activeTicket);
const delegatedTicket = activeTicket?.source.kind === "agent" ? activeTicket : undefined;
const historyConversationId =
@ -661,7 +671,7 @@ function LeafView({
</button>
)}
</div>
{agentId && (busyByWorkState || historyConversationId) && (
{agentId && (busyByWorkState || inboxDepth > 0 || completedBackgroundTasks.length > 0 || historyConversationId) && (
<div
role="status"
aria-live="polite"
@ -699,6 +709,60 @@ function LeafView({
: "Busy"}
</span>
)}
{!busyByWorkState && inboxDepth > 0 && (
<span
title={`${inboxDepth} pending message${inboxDepth === 1 ? "" : "s"}`}
style={{
maxWidth: 180,
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
border: "1px solid var(--color-primary, #5b9bd5)",
borderRadius: 3,
background: "rgba(91, 155, 213, 0.14)",
color: "var(--color-primary, #5b9bd5)",
padding: "1px 6px",
fontSize: 11,
fontWeight: 600,
}}
>
Queued · {inboxDepth}
</span>
)}
{busyByWorkState && inboxDepth > 0 && (
<span
title={`${inboxDepth} queued message${inboxDepth === 1 ? "" : "s"}`}
style={{
flexShrink: 0,
border: "1px solid var(--color-primary, #5b9bd5)",
borderRadius: 3,
background: "rgba(91, 155, 213, 0.14)",
color: "var(--color-primary, #5b9bd5)",
padding: "1px 6px",
fontSize: 11,
fontWeight: 600,
}}
>
Queued {inboxDepth}
</span>
)}
{completedBackgroundTasks.length > 0 && (
<span
title={`${completedBackgroundTasks.length} background task result${completedBackgroundTasks.length === 1 ? "" : "s"}`}
style={{
flexShrink: 0,
border: "1px solid var(--color-success, #57b36a)",
borderRadius: 3,
background: "rgba(87, 179, 106, 0.14)",
color: "var(--color-success, #57b36a)",
padding: "1px 6px",
fontSize: 11,
fontWeight: 600,
}}
>
Task done {completedBackgroundTasks.length}
</span>
)}
{busyByWorkState && (
<button
type="button"