CopilotKit / CopilotKit/outpost
Decide whether email auto-answers: EMAIL is a registered delivery platform but postResponse throws
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 7
- Forks
- 3
- Avg merge
- 7d 16h
- Merged PRs (30d)
- 15
Description
EMAIL is registered as a delivery platform but cannot deliver, so every email ticket spends an AI call on an answer nobody receives.
What happens today
- An email arrives,
apps/web/src/app/api/webhooks/postmark/route.tscreates a ticket and enqueues oneAI_RESPONSEjob. handleAiResponseruns the full pipeline, generates the answer, persists it as aBOTmessage, and writessuggestedResponseon the ticket.hasAdapter('EMAIL')is true —EMAILis inADAPTER_SOURCESatpackages/outpost/shared/src/platforms/registry.ts:119— so the handler callsadapter.postResponse.EmailPostmarkAdapter.postResponsethrowsEmailPostmarkAdapter.postResponse not yet implemented(packages/outpost/shared/src/platforms/email-postmark.ts).- Since #170, that failure escalates to a human with a delivery-failure reason.
Net effect: the reporter receives nothing, a human answers anyway, and we paid for a model call plus an escalation whose stated reason ("delivery failed") describes a channel that was never able to deliver in the first place. The draft is still on the ticket, so a human can use it — but nothing about the current shape says that was the intent.
The decision
Two coherent options, and it's a product call rather than a technical one:
A. Email is human-answered. Remove TicketSource.EMAIL from ADAPTER_SOURCES so no AI_RESPONSE job is enqueued for email at all. Tickets are still created and threaded correctly (#170 fixed reply detection), and a person answers. One-line change, sends nothing outbound, no new failure modes, and it matches the observable behaviour today minus the waste. If a generated draft for the human is wanted, that's a separate feature — generate it without registering a delivery adapter.
B. Email auto-answers like Discord. Implement postResponse against the existing sender in packages/outpost/shared/src/email/sender.ts, which already supports inReplyTo / references / messageId. This is the larger option and it starts sending mail to customers automatically. It needs:
In-Reply-ToandReferencesset on the outbound message, or replies arrive as new threads in the customer's client.- The outbound
MessageIDpersisted. #170's reply detection leans onReferencesprecisely because we have never stored an outbound ID; storing it makesIn-Reply-Toresolution work directly. - A verified Postmark sending domain.
POSTMARK_API_KEYandPOSTMARK_FROM_EMAILare read atregistry.ts:96-97, but whether outbound is actually provisioned is unconfirmed. - Care around
SHADOW_MODE, so staging never mails a real customer. Note the worker-level gate already skips post-back entirely whenSHADOW_MODE=true, which covers this — worth an explicit test.
Option A is reversible in a line. Option B is not: a sent email cannot be recalled, and getting threading wrong is visible to customers.
Related, same area
EmailPostmarkAdapter.parseInboundEventdetects replies byIn-Reply-Toonly. #170 fixed this in the webhook route but not on the adapter path, so whoever routes Postmark throughEmailPostmarkAdapter+InboundHandlerinherits the original bug.- That refactor is itself worth doing under B: the email door currently bypasses
InboundHandler, so it has no team-member gate (a teammate emailing in gets an AI answer), nouserIdlink, and notruncateon stored content. - No expression index backs the JSON-path lookup on
Message.attachments.postmarkMessageIdadded in #170. Fine at current volume, a sequential scan later.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with apps/web/src/app/api/webhooks/postmark/route.ts, packages/outpost/shared/src/platforms/registry.ts, and packages/outpost/shared/src/platforms/email-postmark.ts to understand the current email flow. Read packages/outpost/shared/src/email/sender.ts and the worker-level SHADOW_MODE gate before choosing between human-answered email and outbound auto-replies. Done means the selected behavior is implemented safely, with email threading and the explicit SHADOW_MODE test covered if auto-replies are chosen.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, backend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100