CopilotKit / CopilotKit/outpost

Decide whether email auto-answers: EMAIL is a registered delivery platform but postResponse throws

Open
#171 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area: integrations roadmap roadmap: now
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

  1. An email arrives, apps/web/src/app/api/webhooks/postmark/route.ts creates a ticket and enqueues one AI_RESPONSE job.
  2. handleAiResponse runs the full pipeline, generates the answer, persists it as a BOT message, and writes suggestedResponse on the ticket.
  3. hasAdapter('EMAIL') is true — EMAIL is in ADAPTER_SOURCES at packages/outpost/shared/src/platforms/registry.ts:119 — so the handler calls adapter.postResponse.
  4. EmailPostmarkAdapter.postResponse throws EmailPostmarkAdapter.postResponse not yet implemented (packages/outpost/shared/src/platforms/email-postmark.ts).
  5. 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-To and References set on the outbound message, or replies arrive as new threads in the customer's client.
  • The outbound MessageID persisted. #170's reply detection leans on References precisely because we have never stored an outbound ID; storing it makes In-Reply-To resolution work directly.
  • A verified Postmark sending domain. POSTMARK_API_KEY and POSTMARK_FROM_EMAIL are read at registry.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 when SHADOW_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.parseInboundEvent detects replies by In-Reply-To only. #170 fixed this in the webhook route but not on the adapter path, so whoever routes Postmark through EmailPostmarkAdapter + InboundHandler inherits 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), no userId link, and no truncate on stored content.
  • No expression index backs the JSON-path lookup on Message.attachments.postmarkMessageId added in #170. Fine at current volume, a sequential scan later.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.