fix: skip web push notification when chat is interrupted (#22630)

When a user interrupts a chat, the status transitions to `waiting` which
previously triggered an "Agent has finished running." web push
notification. This is incorrect — the user interrupted it themselves, so
no notification is needed.

## Changes

### `coderd/chatd/chatd.go`
- Added `wasInterrupted` flag alongside the existing `status` variable
- Set the flag when `ErrInterrupted` is detected in the error handler
- Added `!wasInterrupted` to the web push dispatch condition

### `coderd/chatd/chatd_test.go`
- Added `TestInterruptChatDoesNotSendWebPushNotification` that creates a
chat with a mock webpush dispatcher, processes it, interrupts it, and
verifies no push notification was dispatched
- Added `mockWebpushDispatcher` implementing the `webpush.Dispatcher`
interface
This commit is contained in:
Kyle Carberry
2026-03-05 09:08:17 +00:00
committed by GitHub
parent c79e8f2707
commit 7bcd9f6de8
2 changed files with 116 additions and 4 deletions
+6 -4
View File
@@ -1673,6 +1673,7 @@ func (p *Server) processChat(ctx context.Context, chat database.Chat) {
// Determine the final status and last error to set when we're done.
status := database.ChatStatusWaiting
wasInterrupted := false
lastError := ""
remainingQueuedMessages := []database.ChatQueuedMessage{}
shouldPublishQueueUpdate := false
@@ -1802,10 +1803,10 @@ func (p *Server) processChat(ctx context.Context, chat database.Chat) {
// Send a web push notification when the agent finishes
// processing. We only notify for terminal states (waiting
// = success, error = failure) and skip sub-agent chats to
// avoid spamming the user with notifications for internal
// delegation.
if p.webpushDispatcher != nil && p.webpushDispatcher.PublicKey() != "" && !chat.ParentChatID.Valid {
// = success, error = failure) and skip sub-agent chats
// and user-interrupted chats to avoid unnecessary
// notifications.
if p.webpushDispatcher != nil && p.webpushDispatcher.PublicKey() != "" && !chat.ParentChatID.Valid && !wasInterrupted {
if status == database.ChatStatusWaiting || status == database.ChatStatusError {
pushMsg := codersdk.WebpushMessage{
Title: chat.Title,
@@ -1833,6 +1834,7 @@ func (p *Server) processChat(ctx context.Context, chat database.Chat) {
if errors.Is(err, chatloop.ErrInterrupted) || errors.Is(context.Cause(chatCtx), chatloop.ErrInterrupted) {
logger.Info(ctx, "chat interrupted")
status = database.ChatStatusWaiting
wasInterrupted = true
return
}
if isShutdownCancellation(ctx, chatCtx, err) {