fix: Retry sends message with retry flag to avoid duplicate storage

Added retry:true flag to sendChatMessage input. Server skips storing
the user message when retry is set. Frontend uses a dedicated
retryLastMessage function that skips the optimistic message add.
This commit is contained in:
Lukas May
2026-03-04 11:25:43 +01:00
parent 84dcb0193d
commit 354950bb8a
3 changed files with 33 additions and 17 deletions

View File

@@ -26,6 +26,7 @@ export function chatSessionProcedures(publicProcedure: ProcedureBuilder) {
targetId: z.string().min(1),
initiativeId: z.string().min(1),
message: z.string().min(1),
retry: z.boolean().optional(),
provider: z.string().optional(),
}))
.mutation(async ({ ctx, input }) => {
@@ -49,18 +50,20 @@ export function chatSessionProcedures(publicProcedure: ProcedureBuilder) {
});
}
// Store user message
await chatRepo.createMessage({
chatSessionId: session.id,
role: 'user',
content: input.message,
});
// Store user message (skip on retry — message already exists)
if (!input.retry) {
await chatRepo.createMessage({
chatSessionId: session.id,
role: 'user',
content: input.message,
});
ctx.eventBus.emit({
type: 'chat:message_created' as const,
timestamp: new Date(),
payload: { chatSessionId: session.id, role: 'user' as const },
});
ctx.eventBus.emit({
type: 'chat:message_created' as const,
timestamp: new Date(),
payload: { chatSessionId: session.id, role: 'user' as const },
});
}
// Check if agent exists and is waiting for input
if (session.agentId) {

View File

@@ -42,7 +42,7 @@ function ChatSlideOverInner({
initiativeId: string;
onClose: () => void;
}) {
const { messages, agentStatus, agentError, sendMessage, closeSession, isSending } =
const { messages, agentStatus, agentError, sendMessage, retryLastMessage, closeSession, isSending } =
useChatSession(target.type, target.id, initiativeId);
const scrollRef = useRef<HTMLDivElement>(null);
@@ -145,11 +145,7 @@ function ChatSlideOverInner({
variant="outline"
size="sm"
className="h-6 gap-1.5 text-xs"
onClick={() => {
// Re-send the last user message to retry
const lastUserMsg = [...messages].reverse().find(m => m.role === 'user');
if (lastUserMsg) sendMessage(lastUserMsg.content);
}}
onClick={retryLastMessage}
>
<RotateCcw className="h-3 w-3" />
Retry

View File

@@ -30,6 +30,7 @@ export interface UseChatSessionResult {
agentStatus: ChatAgentStatus;
agentError: string | null;
sendMessage: (message: string) => void;
retryLastMessage: () => void;
closeSession: () => void;
isSending: boolean;
isLoading: boolean;
@@ -163,6 +164,21 @@ export function useChatSession(
[targetType, targetId, initiativeId],
);
const messagesRef = useRef(messages);
messagesRef.current = messages;
const retryLastMessage = useCallback(() => {
const lastUserMsg = [...messagesRef.current].reverse().find(m => m.role === 'user');
if (!lastUserMsg) return;
sendMutateRef.current({
targetType,
targetId,
initiativeId,
message: lastUserMsg.content,
retry: true,
});
}, [targetType, targetId, initiativeId]);
const closeSession = useCallback(() => {
const s = sessionRef.current;
if (s) {
@@ -179,6 +195,7 @@ export function useChatSession(
agentStatus,
agentError,
sendMessage,
retryLastMessage,
closeSession,
isSending,
isLoading,