fix(cli): les annonces MCP/inter-agent restent affichées pendant qu'un agent est busy — #232

This commit is contained in:
2026-08-21 19:40:13 +02:00
parent a51813fc44
commit aa85d4dfc8
6 changed files with 258 additions and 32 deletions

View File

@ -31,6 +31,7 @@ import type {
SlashCommand,
} from "@/domain";
import { useGateways } from "@/app/di";
import { AnnouncementsPreview } from "@/features/announcements";
import { usePluginRuntime } from "@/features/plugins/PluginRuntimeProvider";
import {
markProjectCliInteractionFinished,
@ -127,6 +128,10 @@ interface ChatErrorState {
openFailure: boolean;
}
interface RequesterDelegationState {
active: boolean;
}
function describe(e: unknown): string {
if (e && typeof e === "object" && "message" in e) {
return String((e as GatewayError).message);
@ -842,7 +847,7 @@ export function CustomAgentChatView({
onSessionId,
onConversationId,
}: CustomAgentChatViewProps) {
const { agent, profile: profileGateway, system } = useGateways();
const { agent, profile: profileGateway, system, workState } = useGateways();
const pluginRuntime = usePluginRuntime();
const initialConversationCacheKey = conversationCacheKey(
projectId,
@ -1013,6 +1018,33 @@ export function CustomAgentChatView({
[agentId, conversationId, projectId],
);
const displayTurns = turns;
const [requesterDelegation, setRequesterDelegation] =
useState<RequesterDelegationState>({ active: false });
const refreshRequesterDelegation = useCallback(async () => {
if (!workState) {
setRequesterDelegation({ active: false });
return;
}
try {
const state = await workState.getProjectWorkState(projectId);
const target = state.agents.find((row) =>
row.tickets.some(
(ticket) =>
ticket.status === "inProgress" &&
ticket.source.kind === "agent" &&
ticket.source.agentId === agentId,
),
);
setRequesterDelegation(
target
? { active: true }
: { active: false },
);
} catch {
setRequesterDelegation({ active: false });
}
}, [agentId, projectId, workState]);
const clearPersistedDraft = useCallback(async () => {
if (!draftScope) return;
@ -1034,6 +1066,36 @@ export function CustomAgentChatView({
setDraft(text);
}, []);
useEffect(() => {
void refreshRequesterDelegation();
}, [refreshRequesterDelegation]);
useEffect(() => {
if (!system?.onDomainEvent) return;
let unsubscribe: (() => void) | undefined;
let cancelled = false;
void system
.onDomainEvent((event) => {
if (
event.type === "agentBusyChanged" ||
event.type === "delegationReady" ||
event.type === "orchestratorRequestProcessed" ||
event.type === "agentInboxChanged" ||
event.type === "agentWakeChanged"
) {
void refreshRequesterDelegation();
}
})
.then((un) => {
if (cancelled) un();
else unsubscribe = un;
});
return () => {
cancelled = true;
unsubscribe?.();
};
}, [refreshRequesterDelegation, system]);
useEffect(() => {
mountedRef.current = true;
return () => {
@ -1914,7 +1976,7 @@ export function CustomAgentChatView({
<div
ref={chatRootRef}
data-testid="custom-agent-chat-view"
className="flex h-full min-h-0 flex-col overflow-hidden bg-surface text-content"
className="relative z-0 flex h-full min-h-0 flex-col overflow-hidden bg-surface text-content"
>
<div
role="toolbar"
@ -2076,6 +2138,13 @@ export function CustomAgentChatView({
data-testid="custom-agent-chat-scroll"
className="flex min-h-0 flex-1 basis-0 flex-col gap-2 overflow-x-hidden overflow-y-auto px-3 py-3"
>
<div className="shrink-0">
<AnnouncementsPreview
requester={agentId}
fallbackActive={requesterDelegation.active}
fallbackText="Délégation en cours."
/>
</div>
{opening && displayTurns.length === 0 ? (
<div className="flex items-center gap-2 text-sm text-muted">
<Spinner size={14} />