import { z } from "zod"; import { GmailBaseTool } from "./base.js"; import { SEARCH_DESCRIPTION } from "./descriptions.js"; export class GmailSearch extends GmailBaseTool { constructor(fields) { super(fields); Object.defineProperty(this, "name", { enumerable: true, configurable: true, writable: true, value: "search_gmail" }); Object.defineProperty(this, "schema", { enumerable: true, configurable: true, writable: true, value: z.object({ query: z.string(), maxResults: z.number().optional(), resource: z.enum(["messages", "threads"]).optional(), }) }); Object.defineProperty(this, "description", { enumerable: true, configurable: true, writable: true, value: SEARCH_DESCRIPTION }); } async _call(arg) { const { query, maxResults = 10, resource = "messages" } = arg; const response = await this.gmail.users.messages.list({ userId: "me", q: query, maxResults, }); const { data } = response; if (!data) { throw new Error("No data returned from Gmail"); } const { messages } = data; if (!messages) { throw new Error("No messages returned from Gmail"); } if (resource === "messages") { const parsedMessages = await this.parseMessages(messages); return `Result for the query ${query}:\n${JSON.stringify(parsedMessages)}`; } else if (resource === "threads") { const parsedThreads = await this.parseThreads(messages); return `Result for the query ${query}:\n${JSON.stringify(parsedThreads)}`; } throw new Error(`Invalid resource: ${resource}`); } async parseMessages(messages) { const parsedMessages = await Promise.all(messages.map(async (message) => { const messageData = await this.gmail.users.messages.get({ userId: "me", format: "raw", id: message.id ?? "", }); const headers = messageData.data.payload?.headers || []; const subject = headers.find((header) => header.name === "Subject"); const sender = headers.find((header) => header.name === "From"); let body = ""; if (messageData.data.payload?.parts) { body = messageData.data.payload.parts .map((part) => part.body?.data ?? "") .join(""); } else if (messageData.data.payload?.body?.data) { body = messageData.data.payload.body.data; } return { id: message.id, threadId: message.threadId, snippet: message.snippet, body, subject, sender, }; })); return parsedMessages; } async parseThreads(threads) { const parsedThreads = await Promise.all(threads.map(async (thread) => { const threadData = await this.gmail.users.threads.get({ userId: "me", format: "raw", id: thread.id ?? "", }); const headers = threadData.data.messages?.[0]?.payload?.headers || []; const subject = headers.find((header) => header.name === "Subject"); const sender = headers.find((header) => header.name === "From"); let body = ""; if (threadData.data.messages?.[0]?.payload?.parts) { body = threadData.data.messages[0].payload.parts .map((part) => part.body?.data ?? "") .join(""); } else if (threadData.data.messages?.[0]?.payload?.body?.data) { body = threadData.data.messages[0].payload.body.data; } return { id: thread.id, snippet: thread.snippet, body, subject, sender, }; })); return parsedThreads; } }