feat: force a seller scan over active conversations from the inbox

Adds a "Scan for sellers" button to the conversations header, backed by
POST /api/conversations/seller-scan.

The sweep reuses the existing seller detection rather than inventing a
second one: the configured keyword/domain list, the seller-link funnel
patterns, and the aiReplyScreening confidences all decide the verdict, so
a swept conversation lands in the state the live reply loop would have
given it. Being operator-forced, it deliberately ignores filterSellers
and aiReplyScreening.enabled.

AI is the last resort. Conversations are skipped when already flagged or
killed, settled for free by the deterministic rules, or skipped against a
durable sellerScreenedKey watermark when nothing new has arrived since
the last clean sweep. Only the remainder reaches the model, batched eight
at a time through a new screenConversationsForSellers, so the system
prompt and round trip are paid per batch instead of per conversation. A
classifier outage degrades the sweep instead of failing it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Jamie BruntonandClaude Opus 5 committed 2026-08-10 13:42:55 +01:00
1 parent 0a4e0064f2
commit 827eb3a1e0
15 files changed
+1169 -1

No files matched your search

+1
View File
@@ -82,6 +82,7 @@ an administrator credential.
| `POST` | `/api/conversations/{id}/unread` | Return a conversation to the unread queue | admin | yes |
| `POST` | `/api/conversations/{id}/status` | Snooze, resolve or reopen a conversation | admin | yes |
| `POST` | `/api/conversations/bulk-read` | Mark many conversations read in one request | admin | yes |
| `POST` | `/api/conversations/seller-scan` | Force a seller pass over every active conversation | admin | yes |
| `GET` | `/api/media` | List received private media | read | no |
| `GET` | `/api/media/sync` | Get media sync state | read | no |
| `POST` | `/api/media/sync` | Trigger media sync | admin | yes |