feat(session-limits): LS7-front — UI limites de session (badge + compte à rebours + filet humain)

Expose la surface produit des limites de session côté React/TS,
au-dessus du câblage backend (9df5923).

- domain/index.ts : 5 variantes ajoutées au union DomainEvent
  (agentRateLimited / ResumeScheduled / ResumeCancelled / Resumed /
  RateLimitSuspected).
- ports/index.ts : cancelResume(agentId) ajouté à InputGateway.
- adapters/input.ts : TauriInputGateway.cancelResume → invoke("cancel_resume").
- adapters/mock/index.ts : MockInputGateway.cancelResume
  (cancelledResumes / cancelResumeResult).
- features/agents/useAgents.ts : état limitByAgent + action cancelResume.
- features/agents/AgentLimitBadge.tsx (nouveau) : badge + compte à rebours
  + bouton Annuler + helpers purs.
- features/agents/AgentsPanel.tsx : câblage du badge.

Tests : useAgentsLimits.test.tsx (13) + AgentLimitBadge.test.tsx (11),
suite agents 63 tests verts, tsc --noEmit propre.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-17 08:06:56 +02:00
parent 9df592389c
commit 4fad0423e7
9 changed files with 702 additions and 1 deletions

View File

@ -21,6 +21,7 @@ import { TerminalView } from "@/features/terminals/TerminalView";
import { useDrift } from "@/features/templates/useDrift";
import { useGateways } from "@/app/di";
import { useAgents } from "./useAgents";
import { AgentLimitBadge } from "./AgentLimitBadge";
export interface AgentsPanelProps {
/** The project whose agents to manage. */
@ -327,6 +328,8 @@ export function AgentsPanel({ projectId, projectRoot = "" }: AgentsPanelProps) {
// Source of this agent's last orchestration delegation (mcp vs
// file), if any has been observed. Absent ⇒ no badge.
const delegationSource = vm.delegationSourceByRequester[a.id];
// Session-limit state (ARCHITECTURE §21), if the agent is limited.
const limitState = vm.limitByAgent[a.id];
return (
<li
key={a.id}
@ -373,6 +376,14 @@ export function AgentsPanel({ projectId, projectRoot = "" }: AgentsPanelProps) {
)}
</button>
{limitState && (
<AgentLimitBadge
state={limitState}
busy={vm.busy}
onCancelResume={() => void vm.cancelResume(a.id)}
/>
)}
<div className="flex flex-wrap items-center gap-1.5">
{/* Profile hot-swap selector (Chantier A). Changing the
engine abandons the conversation history → confirmation. */}