fix(chat): ignore self-emitted custom session echoes

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-05 15:29:03 +02:00
parent 525ea94b09
commit ce9ba0dc3d
2 changed files with 92 additions and 13 deletions

View File

@ -131,6 +131,7 @@ export function CustomAgentChatView({
const { agent, system } = useGateways();
const [turns, setTurns] = useState<ChatTurn[]>([]);
const [currentSession, setCurrentSession] = useState(sessionId);
const [externalSessionId, setExternalSessionId] = useState(sessionId);
const [draft, setDraft] = useState("");
const [attachment, setAttachment] = useState<string | null>(null);
const [opening, setOpening] = useState(false);
@ -141,6 +142,7 @@ export function CustomAgentChatView({
sessionRef.current = currentSession;
const mountedRef = useRef(false);
const openOrAttachCountRef = useRef(0);
const selfEmittedSessionIdRef = useRef<string | null | undefined>(undefined);
const onSessionIdRef = useRef(onSessionId);
onSessionIdRef.current = onSessionId;
const onConversationIdRef = useRef(onConversationId);
@ -167,6 +169,11 @@ export function CustomAgentChatView({
if (chunk.kind === "final" || chunk.kind === "error") setBusy(false);
}, []);
const publishSessionId = useCallback((nextSessionId: string | null) => {
selfEmittedSessionIdRef.current = nextSessionId;
onSessionIdRef.current(nextSessionId);
}, []);
const reattachStructuredSession = useCallback(
async (sid: string, options: { applyScrollback?: boolean } = {}) => {
if (!agent.reattachAgentChat) throw new Error("Structured reattach unavailable");
@ -249,7 +256,7 @@ export function CustomAgentChatView({
});
if (!mountedRef.current) return launched.sessionId;
setCurrentSession(launched.sessionId);
onSessionIdRef.current(launched.sessionId);
publishSessionId(launched.sessionId);
if (launched.assignedConversationId) {
onConversationIdRef.current(launched.assignedConversationId);
}
@ -268,7 +275,7 @@ export function CustomAgentChatView({
retryAttachNotFound = false;
if (mountedRef.current) {
setCurrentSession(null);
onSessionIdRef.current(null);
publishSessionId(null);
}
continue;
}
@ -289,6 +296,7 @@ export function CustomAgentChatView({
nodeId,
conversationId,
reattachStructuredSession,
publishSessionId,
],
);
@ -297,6 +305,18 @@ export function CustomAgentChatView({
if (el) el.scrollTop = el.scrollHeight;
}, [turns]);
useEffect(() => {
if (selfEmittedSessionIdRef.current === sessionId) {
console.debug("[ticket149] openOrAttach:self-session-echo:skip", {
timestamp: new Date().toISOString(),
sessionId,
});
selfEmittedSessionIdRef.current = undefined;
return;
}
setExternalSessionId(sessionId);
}, [sessionId]);
useEffect(() => {
if (!supported) return;
let cancelled = false;
@ -307,19 +327,19 @@ export function CustomAgentChatView({
console.debug("[ticket149] openOrAttach:start", {
timestamp: new Date().toISOString(),
execution,
sessionId,
sessionId: externalSessionId,
});
setOpening(true);
setError(null);
try {
if (sessionId) {
if (externalSessionId) {
try {
await reattachStructuredSession(sessionId, { applyScrollback: true });
await reattachStructuredSession(externalSessionId, { applyScrollback: true });
if (cancelled) return;
console.debug("[ticket149] openOrAttach:reattach-existing:success", {
timestamp: new Date().toISOString(),
execution,
sessionId,
sessionId: externalSessionId,
});
return;
} catch (e) {
@ -329,12 +349,12 @@ export function CustomAgentChatView({
if (!isNotFound(e)) throw e;
if (!cancelled) {
setCurrentSession(null);
onSessionIdRef.current(null);
publishSessionId(null);
}
console.debug("[ticket149] openOrAttach:reattach-existing:not-found", {
timestamp: new Date().toISOString(),
execution,
sessionId,
sessionId: externalSessionId,
error: instrumentationError(e),
});
}
@ -347,13 +367,13 @@ export function CustomAgentChatView({
console.debug("[ticket149] openOrAttach:recover:success", {
timestamp: new Date().toISOString(),
execution,
receivedSessionId: sessionId,
receivedSessionId: externalSessionId,
});
} catch (e) {
console.error("[ticket149] openOrAttach:error", {
timestamp: new Date().toISOString(),
execution,
sessionId,
sessionId: externalSessionId,
error: instrumentationError(e),
});
if (!cancelled) setError(describe(e));
@ -368,9 +388,10 @@ export function CustomAgentChatView({
};
}, [
supported,
sessionId,
externalSessionId,
reattachStructuredSession,
recoverStructuredSession,
publishSessionId,
]);
const canSend = useMemo(
@ -408,7 +429,7 @@ export function CustomAgentChatView({
if (isNotFound(e)) {
try {
setCurrentSession(null);
onSessionIdRef.current(null);
publishSessionId(null);
const recoveredSession = await recoverStructuredSession({
applyScrollback: false,
retryAttachNotFound: true,
@ -440,7 +461,7 @@ export function CustomAgentChatView({
} catch (e) {
if (isNotFound(e)) {
setCurrentSession(null);
onSessionIdRef.current(null);
publishSessionId(null);
setTurns((prev) => [...prev, { role: "tool", label: "Session déjà fermée." }]);
return;
}