Surface channel delivery failures on agent replies instead of swallowing them

The previous fix made a broken SMTP/Telegram account non-fatal — the
reply gets recorded either way now — but that also made delivery
failures completely invisible: an agent sees their reply "sent"
successfully with no indication the customer never actually received it.
Added messages.deliveryFailed, set whenever the outbound send throws, and
a warning badge on the message bubble so this is now visible instead of
silent.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gh2UXUQUVBWroWEnn1FLFG
This commit is contained in:
ogrechkoandClaude Sonnet 5 committed 2026-10-07 08:26:54 +00:00
1 parent 8d1f91187a
commit b92d9801ff
8 files changed
+1258 -14

No files matched your search

+20 -13
View File
@@ -56,7 +56,13 @@ export async function deliverAgentMessage(params: {
// shown in the thread) and the API route's catch-all turned it into a
// misleading "Ticket not found". A broken mail/bot account should degrade
// to "reply saved internally, not delivered" — never silent data loss.
// A ticket only ever has one channel, so at most one of the two branches
// below runs — this single flag is the outcome of whichever one does, and
// is what lets the agent tell "saved" from "the customer actually got
// this" in the UI (see messages.deliveryFailed).
let emailMessageId: string | undefined;
let deliveryFailed = false;
if (ticket.channel === "email") {
const context = await getEmailThreadContext(params.ticketId);
if (context) {
@@ -72,22 +78,12 @@ export async function deliverAgentMessage(params: {
});
} catch (err) {
console.error(`Failed to send reply email for ticket ${params.ticketId}:`, err);
deliveryFailed = true;
}
}
}
const result = await recordAgentReply({
ticketId: params.ticketId,
agentId: params.agentId,
agentName: params.agentName,
body: params.body,
emailMessageId,
attachments: attachmentInput ? [attachmentInput] : undefined,
});
if (result.ticket.channel === "telegram") {
} else if (ticket.channel === "telegram") {
const customer = await db.query.customers.findFirst({
where: eq(customers.id, result.ticket.customerId),
where: eq(customers.id, ticket.customerId),
});
if (customer?.telegramChatId) {
try {
@@ -102,9 +98,20 @@ export async function deliverAgentMessage(params: {
}
} catch (err) {
console.error(`Failed to deliver reply via Telegram for ticket ${params.ticketId}:`, err);
deliveryFailed = true;
}
}
}
const result = await recordAgentReply({
ticketId: params.ticketId,
agentId: params.agentId,
agentName: params.agentName,
body: params.body,
emailMessageId,
deliveryFailed,
attachments: attachmentInput ? [attachmentInput] : undefined,
});
return result;
}