import { tool } from "ai";
import { z } from "zod";
const API_KEY = process.env.OPENMAIL_API_KEY!;
const INBOX_ID = process.env.OPENMAIL_INBOX_ID!;
const BASE = "https://api.openmail.sh/v1";
const headers = { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" };
const sendEmail = tool({
description: "Send an email. Pass threadId to reply in an existing thread.",
inputSchema: z.object({
to: z.string().describe("Recipient email address"),
subject: z.string().describe("Email subject"),
body: z.string().describe("Plain text body"),
threadId: z.string().optional().describe("Thread ID to reply in"),
}),
execute: async ({ to, subject, body, threadId }) => {
const resp = await fetch(`${BASE}/inboxes/${INBOX_ID}/send`, {
method: "POST",
headers: { ...headers, "Idempotency-Key": crypto.randomUUID() },
body: JSON.stringify({ to, subject, body, ...(threadId && { threadId }) }),
});
if (!resp.ok) throw new Error(`OpenMail ${resp.status}`);
return resp.json();
},
});
const checkInbox = tool({
description: "List unread threads — use this to check for new mail.",
inputSchema: z.object({}),
execute: async () => {
const resp = await fetch(
`${BASE}/inboxes/${INBOX_ID}/threads?is_read=false&limit=20`,
{ headers },
);
if (!resp.ok) throw new Error(`OpenMail ${resp.status}`);
return resp.json();
},
});
const readThread = tool({
description: "Get all messages in a thread (oldest first) and mark it as read.",
inputSchema: z.object({
threadId: z.string().describe("Thread ID to read"),
}),
execute: async ({ threadId }) => {
const resp = await fetch(`${BASE}/threads/${threadId}/messages`, { headers });
if (!resp.ok) throw new Error(`OpenMail ${resp.status}`);
const messages = await resp.json();
await fetch(`${BASE}/threads/${threadId}`, {
method: "PATCH",
headers,
body: JSON.stringify({ is_read: true }),
});
return messages;
},
});