@elizaos/plugin-inbox 2.0.3-beta.6 → 2.0.3-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/inbox.d.ts +69 -0
- package/dist/actions/inbox.d.ts.map +1 -0
- package/dist/actions/inbox.js +345 -0
- package/dist/actions/inbox.js.map +1 -0
- package/dist/components/inbox/InboxSpatialView.d.ts +54 -0
- package/dist/components/inbox/InboxSpatialView.d.ts.map +1 -0
- package/dist/components/inbox/InboxSpatialView.js +171 -0
- package/dist/components/inbox/InboxSpatialView.js.map +1 -0
- package/dist/components/inbox/InboxView.d.ts +64 -0
- package/dist/components/inbox/InboxView.d.ts.map +1 -0
- package/dist/components/inbox/InboxView.js +169 -0
- package/dist/components/inbox/InboxView.js.map +1 -0
- package/dist/components/inbox/inbox-view-bundle.d.ts +2 -0
- package/dist/components/inbox/inbox-view-bundle.d.ts.map +1 -0
- package/dist/components/inbox/inbox-view-bundle.js +5 -0
- package/dist/components/inbox/inbox-view-bundle.js.map +1 -0
- package/dist/db/index.d.ts +3 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +3 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/schema.d.ts +1729 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +79 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/db/sql.d.ts +32 -0
- package/dist/db/sql.d.ts.map +1 -0
- package/dist/db/sql.js +130 -0
- package/dist/db/sql.js.map +1 -0
- package/dist/inbox/channel-deep-links.d.ts +7 -0
- package/dist/inbox/channel-deep-links.d.ts.map +1 -0
- package/dist/inbox/channel-deep-links.js +97 -0
- package/dist/inbox/channel-deep-links.js.map +1 -0
- package/dist/inbox/config.d.ts +7 -0
- package/dist/inbox/config.d.ts.map +1 -0
- package/dist/inbox/config.js +61 -0
- package/dist/inbox/config.js.map +1 -0
- package/dist/inbox/email-curation.d.ts +174 -0
- package/dist/inbox/email-curation.d.ts.map +1 -0
- package/dist/inbox/email-curation.js +1056 -0
- package/dist/inbox/email-curation.js.map +1 -0
- package/dist/inbox/email-unsubscribe-types.d.ts +71 -0
- package/dist/inbox/email-unsubscribe-types.d.ts.map +1 -0
- package/dist/inbox/email-unsubscribe-types.js +1 -0
- package/dist/inbox/email-unsubscribe-types.js.map +1 -0
- package/dist/inbox/gmail-normalize.d.ts +99 -0
- package/dist/inbox/gmail-normalize.d.ts.map +1 -0
- package/dist/inbox/gmail-normalize.js +937 -0
- package/dist/inbox/gmail-normalize.js.map +1 -0
- package/dist/inbox/google-gmail-seam.d.ts +52 -0
- package/dist/inbox/google-gmail-seam.d.ts.map +1 -0
- package/dist/inbox/google-gmail-seam.js +263 -0
- package/dist/inbox/google-gmail-seam.js.map +1 -0
- package/dist/inbox/message-fetcher.d.ts +47 -0
- package/dist/inbox/message-fetcher.d.ts.map +1 -0
- package/dist/inbox/message-fetcher.js +461 -0
- package/dist/inbox/message-fetcher.js.map +1 -0
- package/dist/inbox/migration.d.ts +46 -0
- package/dist/inbox/migration.d.ts.map +1 -0
- package/dist/inbox/migration.js +114 -0
- package/dist/inbox/migration.js.map +1 -0
- package/dist/inbox/reflection.d.ts +40 -0
- package/dist/inbox/reflection.d.ts.map +1 -0
- package/dist/inbox/reflection.js +142 -0
- package/dist/inbox/reflection.js.map +1 -0
- package/dist/inbox/repository.d.ts +58 -0
- package/dist/inbox/repository.d.ts.map +1 -0
- package/dist/inbox/repository.js +376 -0
- package/dist/inbox/repository.js.map +1 -0
- package/dist/inbox/service.d.ts +149 -0
- package/dist/inbox/service.d.ts.map +1 -0
- package/dist/inbox/service.js +247 -0
- package/dist/inbox/service.js.map +1 -0
- package/dist/inbox/triage-classifier.d.ts +28 -0
- package/dist/inbox/triage-classifier.d.ts.map +1 -0
- package/dist/inbox/triage-classifier.js +306 -0
- package/dist/inbox/triage-classifier.js.map +1 -0
- package/dist/inbox/types.d.ts +124 -0
- package/dist/inbox/types.d.ts.map +1 -0
- package/dist/inbox/types.js +1 -0
- package/dist/inbox/types.js.map +1 -0
- package/dist/inbox/unsubscribe-repository.d.ts +14 -0
- package/dist/inbox/unsubscribe-repository.d.ts.map +1 -0
- package/dist/inbox/unsubscribe-repository.js +112 -0
- package/dist/inbox/unsubscribe-repository.js.map +1 -0
- package/dist/inbox/unsubscribe-service.d.ts +41 -0
- package/dist/inbox/unsubscribe-service.d.ts.map +1 -0
- package/dist/inbox/unsubscribe-service.js +351 -0
- package/dist/inbox/unsubscribe-service.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +70 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.d.ts +4 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +38 -0
- package/dist/plugin.js.map +1 -0
- package/dist/providers/cross-channel-context.d.ts +21 -0
- package/dist/providers/cross-channel-context.d.ts.map +1 -0
- package/dist/providers/cross-channel-context.js +96 -0
- package/dist/providers/cross-channel-context.js.map +1 -0
- package/dist/providers/inbox-triage.d.ts +12 -0
- package/dist/providers/inbox-triage.d.ts.map +1 -0
- package/dist/providers/inbox-triage.js +98 -0
- package/dist/providers/inbox-triage.js.map +1 -0
- package/dist/register-terminal-view.d.ts +15 -0
- package/dist/register-terminal-view.d.ts.map +1 -0
- package/dist/register-terminal-view.js +21 -0
- package/dist/register-terminal-view.js.map +1 -0
- package/dist/register.d.ts +9 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.js +5 -0
- package/dist/register.js.map +1 -0
- package/dist/types.d.ts +42 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +25 -0
- package/dist/types.js.map +1 -0
- package/dist/views/bundle.js +315 -0
- package/dist/views/bundle.js.map +1 -0
- package/package.json +9 -9
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/inbox/gmail-normalize.ts"],"sourcesContent":["/**\n * Gmail-domain normalization primitives (pure, inbox-domain).\n *\n * Normalization, summarization, and synthesis helpers for Gmail triage / search\n * / spam-review / reply-draft / recommendation feeds. Operates on\n * already-fetched LifeOps Gmail DTOs; carries no Gmail-client dependency.\n * Depends only on `node:crypto` and `@elizaos/shared` (LifeOps contract\n * types/constants + the LifeOps service-constants / normalize / email-fence\n * primitives). Per the plugin-inbox boundary, this module MUST NOT import from\n * `@elizaos/plugin-personal-assistant`. PA keeps a thin re-export shim at\n * `lifeops/service-normalize-gmail.ts` for its historical importers.\n */\n\nimport crypto from \"node:crypto\";\nimport {\n fail,\n GOOGLE_CALENDAR_CACHE_TTL_MS,\n GOOGLE_GMAIL_CACHE_TTL_MS,\n LIFEOPS_GMAIL_BULK_OPERATIONS,\n LIFEOPS_GMAIL_DRAFT_TONES,\n LIFEOPS_GMAIL_SPAM_REVIEW_STATUSES,\n type LifeOpsCalendarEvent,\n type LifeOpsConnectorGrant,\n type LifeOpsGmailBatchReplyDraftsFeed,\n type LifeOpsGmailBulkOperation,\n type LifeOpsGmailMessageSummary,\n type LifeOpsGmailNeedsResponseFeed,\n type LifeOpsGmailRecommendation,\n type LifeOpsGmailRecommendationsFeed,\n type LifeOpsGmailReplyDraft,\n type LifeOpsGmailSearchFeed,\n type LifeOpsGmailSpamReviewFeed,\n type LifeOpsGmailSpamReviewItem,\n type LifeOpsGmailSpamReviewStatus,\n type LifeOpsGmailTriageFeed,\n type LifeOpsGmailUnrespondedFeed,\n normalizeEnumValue,\n normalizeFiniteNumber,\n normalizeOptionalString,\n requireNonEmptyString,\n} from \"@elizaos/shared\";\n\nexport type SyncedGoogleGmailMessageSummary = Omit<\n LifeOpsGmailMessageSummary,\n | \"id\"\n | \"agentId\"\n | \"provider\"\n | \"side\"\n | \"syncedAt\"\n | \"updatedAt\"\n | \"connectorAccountId\"\n | \"grantId\"\n | \"accountEmail\"\n>;\n\nexport function normalizeGmailSearchQuery(value: unknown): string {\n const query = requireNonEmptyString(value, \"query\");\n if (query.length > 500) {\n fail(400, \"query must be 500 characters or fewer\");\n }\n return query;\n}\n\nexport function normalizeGmailBulkOperation(\n value: unknown,\n): LifeOpsGmailBulkOperation {\n return normalizeEnumValue(value, \"operation\", LIFEOPS_GMAIL_BULK_OPERATIONS);\n}\n\nexport function normalizeGmailUnrespondedOlderThanDays(value: unknown): number {\n if (value === undefined || value === null || value === \"\") {\n return 3;\n }\n const days = Math.trunc(normalizeFiniteNumber(value, \"olderThanDays\"));\n if (days < 1 || days > 3650) {\n fail(400, \"olderThanDays must be between 1 and 3650\");\n }\n return days;\n}\n\nexport function parseGmailRelativeDuration(value: string): number | null {\n const match = value\n .trim()\n .toLowerCase()\n .match(/^(\\d+)([dmy])$/);\n if (!match) {\n return null;\n }\n const amount = Number(match[1]);\n if (!Number.isFinite(amount) || amount <= 0) {\n return null;\n }\n const unit = match[2];\n const days =\n unit === \"d\" ? amount : unit === \"m\" ? amount * 30 : amount * 365;\n return days * 24 * 60 * 60 * 1000;\n}\n\nexport function parseGmailDateBoundary(value: string): number | null {\n const normalized = value.trim().replace(/\\//g, \"-\");\n const match = normalized.match(/^(\\d{4})-(\\d{1,2})-(\\d{1,2})$/);\n if (!match) {\n return null;\n }\n const year = Number(match[1]);\n const month = Number(match[2]);\n const day = Number(match[3]);\n if (\n !Number.isFinite(year) ||\n !Number.isFinite(month) ||\n !Number.isFinite(day) ||\n month < 1 ||\n month > 12 ||\n day < 1 ||\n day > 31\n ) {\n return null;\n }\n return Date.UTC(year, month - 1, day, 0, 0, 0, 0);\n}\n\nexport function splitMailboxLikeList(value: string): string[] {\n const parts: string[] = [];\n let current = \"\";\n let inQuotes = false;\n let angleDepth = 0;\n\n for (let index = 0; index < value.length; index += 1) {\n const char = value[index];\n const next = value[index + 1];\n if (char === '\"') {\n inQuotes = !inQuotes;\n current += char;\n continue;\n }\n if (!inQuotes && char === \"<\") {\n angleDepth += 1;\n current += char;\n continue;\n }\n if (!inQuotes && char === \">\") {\n angleDepth = Math.max(0, angleDepth - 1);\n current += char;\n continue;\n }\n if (!inQuotes && angleDepth === 0 && char === \"|\" && next === \"|\") {\n const trimmed = current.trim();\n if (trimmed.length > 0) {\n parts.push(trimmed);\n }\n current = \"\";\n index += 1;\n continue;\n }\n if (\n !inQuotes &&\n angleDepth === 0 &&\n (char === \",\" || char === \";\" || char === \"\\n\")\n ) {\n const trimmed = current.trim();\n if (trimmed.length > 0) {\n parts.push(trimmed);\n }\n current = \"\";\n continue;\n }\n current += char;\n }\n\n const trimmed = current.trim();\n if (trimmed.length > 0) {\n parts.push(trimmed);\n }\n return parts;\n}\n\nexport function extractNormalizedEmailAddress(value: string): string | null {\n const trimmed = value.trim().replace(/^mailto:/i, \"\");\n if (!trimmed) {\n return null;\n }\n const angleMatch = trimmed.match(/<\\s*([^<>\\s@]+@[^<>\\s@]+)\\s*>/u);\n const rawCandidate =\n angleMatch?.[1] ??\n trimmed.match(/([^\\s<>()\"';,]+@[^\\s<>()\"';,]+)/u)?.[1] ??\n trimmed;\n const normalized = rawCandidate\n .trim()\n .replace(/^[\"']+|[\"']+$/g, \"\")\n .replace(/[>;,\\s]+$/g, \"\")\n .toLowerCase();\n return /^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$/u.test(normalized) ? normalized : null;\n}\n\nexport function normalizeOptionalMessageIdArray(\n value: unknown,\n field: string,\n): string[] | undefined {\n if (value === undefined) {\n return undefined;\n }\n if (!Array.isArray(value)) {\n fail(400, `${field} must be an array`);\n }\n const items: string[] = [];\n const seen = new Set<string>();\n for (const [index, candidate] of value.entries()) {\n const item = requireNonEmptyString(candidate, `${field}[${index}]`);\n if (seen.has(item)) {\n continue;\n }\n seen.add(item);\n items.push(item);\n }\n if (items.length > 50) {\n fail(400, `${field} must contain 50 items or fewer`);\n }\n return items;\n}\n\nexport function normalizeOptionalGmailLabelIdArray(\n value: unknown,\n field: string,\n): string[] | undefined {\n if (value === undefined) {\n return undefined;\n }\n if (!Array.isArray(value)) {\n fail(400, `${field} must be an array`);\n }\n const items: string[] = [];\n const seen = new Set<string>();\n for (const [index, candidate] of value.entries()) {\n const item = requireNonEmptyString(candidate, `${field}[${index}]`);\n if (item.length > 128) {\n fail(400, `${field}[${index}] must be 128 characters or fewer`);\n }\n if (!/^[A-Za-z0-9_:-]+$/.test(item)) {\n fail(400, `${field}[${index}] is not a valid Gmail label id`);\n }\n if (seen.has(item)) {\n continue;\n }\n seen.add(item);\n items.push(item);\n }\n if (items.length > 20) {\n fail(400, `${field} must contain 20 items or fewer`);\n }\n return items;\n}\n\nexport function normalizeGmailSearchQueryMatches(\n query: string,\n message: LifeOpsGmailMessageSummary,\n): boolean {\n const all = [\n message.subject,\n message.from,\n message.fromEmail ?? \"\",\n message.replyTo ?? \"\",\n message.snippet,\n ...message.to,\n ...message.cc,\n ...message.labels,\n ]\n .join(\" \")\n .toLowerCase();\n const sender = [message.from, message.fromEmail ?? \"\", message.replyTo ?? \"\"]\n .join(\" \")\n .toLowerCase();\n const subject = message.subject.toLowerCase();\n const to = message.to.join(\" \").toLowerCase();\n const cc = message.cc.join(\" \").toLowerCase();\n const labels = message.labels.join(\" \").toLowerCase();\n const receivedAtMs = Date.parse(message.receivedAt);\n const nowMs = Date.now();\n const tokens: string[] = [];\n let current = \"\";\n let inQuotes = false;\n let braceDepth = 0;\n for (const char of query.trim()) {\n if (char === '\"') {\n inQuotes = !inQuotes;\n current += char;\n continue;\n }\n if (!inQuotes && char === \"{\") {\n braceDepth += 1;\n current += char;\n continue;\n }\n if (!inQuotes && char === \"}\") {\n braceDepth = Math.max(0, braceDepth - 1);\n current += char;\n continue;\n }\n if (!inQuotes && braceDepth === 0 && /\\s/.test(char)) {\n if (current.length > 0) {\n tokens.push(current);\n current = \"\";\n }\n continue;\n }\n current += char;\n }\n if (current.length > 0) {\n tokens.push(current);\n }\n if (tokens.length === 0) {\n return false;\n }\n\n const matchesToken = (token: string): boolean => {\n const normalizedToken = token.trim();\n if (normalizedToken.length === 0) {\n return true;\n }\n const isNegated = normalizedToken.startsWith(\"-\");\n const tokenBody = isNegated\n ? normalizedToken.slice(1).trim()\n : normalizedToken;\n if (!tokenBody) {\n return true;\n }\n if (tokenBody.startsWith(\"{\") && tokenBody.endsWith(\"}\")) {\n const groupMembers = tokenBody\n .slice(1, -1)\n .trim()\n .split(/\\s+/)\n .map((entry) => entry.trim())\n .filter(Boolean);\n if (groupMembers.length === 0) {\n return true;\n }\n const groupMatched = groupMembers.some((entry) => matchesToken(entry));\n return isNegated ? !groupMatched : groupMatched;\n }\n const operatorMatch = tokenBody.match(/^([a-z_]+):(.*)$/i);\n const rawValue = operatorMatch?.[2] ?? tokenBody;\n const value = rawValue.replace(/^\"|\"$/g, \"\").trim().toLowerCase();\n if (value.length === 0) {\n return true;\n }\n\n const labelTokens = message.labels.map((label) => label.toLowerCase());\n const hasAttachment =\n typeof message.metadata.hasAttachments === \"boolean\"\n ? message.metadata.hasAttachments === true\n : /\\battach(?:ed|ment|ments)?\\b/i.test(\n `${message.subject} ${message.snippet}`,\n );\n const matched = (() => {\n if (!operatorMatch) {\n return all.includes(value);\n }\n\n const operator = (operatorMatch[1] ?? \"\").toLowerCase();\n switch (operator) {\n case \"from\":\n if (value === \"me\") {\n return labelTokens.includes(\"sent\");\n }\n return sender.includes(value);\n case \"subject\":\n return subject.includes(value);\n case \"to\":\n return to.includes(value);\n case \"cc\":\n return cc.includes(value);\n case \"label\":\n case \"labels\":\n return labels.includes(value);\n case \"category\":\n return labelTokens.includes(`category_${value}`);\n case \"in\":\n return value === \"anywhere\" ? true : labelTokens.includes(value);\n case \"has\":\n return value === \"attachment\" ? hasAttachment : all.includes(value);\n case \"is\":\n if (value === \"unread\") {\n return message.isUnread;\n }\n if (value === \"read\") {\n return !message.isUnread;\n }\n if (value === \"important\") {\n return message.isImportant;\n }\n if (value === \"starred\") {\n return labelTokens.includes(\"starred\");\n }\n return all.includes(value);\n case \"newer_than\": {\n const relativeMs = parseGmailRelativeDuration(value);\n return relativeMs === null\n ? all.includes(value)\n : receivedAtMs >= nowMs - relativeMs;\n }\n case \"older_than\": {\n const relativeMs = parseGmailRelativeDuration(value);\n return relativeMs === null\n ? all.includes(value)\n : receivedAtMs <= nowMs - relativeMs;\n }\n case \"after\": {\n const boundary = parseGmailDateBoundary(value);\n return boundary === null\n ? all.includes(value)\n : receivedAtMs >= boundary;\n }\n case \"before\": {\n const boundary = parseGmailDateBoundary(value);\n return boundary === null\n ? all.includes(value)\n : receivedAtMs < boundary;\n }\n default:\n return all.includes(value);\n }\n })();\n return isNegated ? !matched : matched;\n };\n\n return tokens.every((token) => {\n const normalizedToken = token.trim();\n if (normalizedToken.length === 0) {\n return true;\n }\n const operatorMatch = normalizedToken.match(/^([a-z_]+):(.*)$/i);\n const operator = operatorMatch?.[1]?.toLowerCase();\n const operatorValue = operatorMatch?.[2];\n if (operator === \"or\" && operatorValue) {\n return matchesToken(operatorValue);\n }\n return matchesToken(normalizedToken);\n });\n}\n\nexport function filterGmailMessagesBySearch(args: {\n messages: LifeOpsGmailMessageSummary[];\n query?: string;\n replyNeededOnly?: boolean;\n}): LifeOpsGmailMessageSummary[] {\n const query = normalizeOptionalString(args.query);\n const filtered = query\n ? args.messages.filter((message) =>\n normalizeGmailSearchQueryMatches(query, message),\n )\n : args.messages;\n const replyNeededOnly = args.replyNeededOnly === true;\n return filtered\n .filter((message) => !replyNeededOnly || message.likelyReplyNeeded)\n .sort(compareGmailMessagePriority);\n}\n\nexport function compareGmailMessagePriority(\n left: LifeOpsGmailMessageSummary,\n right: LifeOpsGmailMessageSummary,\n): number {\n if (left.isImportant !== right.isImportant) {\n return right.isImportant ? 1 : -1;\n }\n if (left.likelyReplyNeeded !== right.likelyReplyNeeded) {\n return right.likelyReplyNeeded ? 1 : -1;\n }\n if (left.isUnread !== right.isUnread) {\n return right.isUnread ? 1 : -1;\n }\n return Date.parse(right.receivedAt) - Date.parse(left.receivedAt);\n}\n\nexport function normalizeGmailDraftTone(\n value: unknown,\n): \"brief\" | \"neutral\" | \"warm\" {\n return normalizeEnumValue(\n value ?? \"neutral\",\n \"tone\",\n LIFEOPS_GMAIL_DRAFT_TONES,\n );\n}\n\nexport function normalizeOptionalStringArray(\n value: unknown,\n field: string,\n): string[] | undefined {\n if (value === undefined) {\n return undefined;\n }\n const rawValues = Array.isArray(value)\n ? value\n : typeof value === \"string\"\n ? splitMailboxLikeList(value)\n : fail(400, `${field} must be an array or string`);\n const items: string[] = [];\n const seen = new Set<string>();\n for (const [index, candidate] of rawValues.entries()) {\n const source = requireNonEmptyString(candidate, `${field}[${index}]`);\n const item = extractNormalizedEmailAddress(source);\n if (!item) {\n fail(400, `${field}[${index}] must be a valid email address`);\n }\n if (seen.has(item)) {\n continue;\n }\n seen.add(item);\n items.push(item);\n }\n return items;\n}\n\nexport function normalizeGmailReplyBody(value: unknown): string {\n const body = requireNonEmptyString(value, \"bodyText\");\n if (body.length > 8000) {\n fail(400, \"bodyText must be 8000 characters or fewer\");\n }\n return body;\n}\n\nexport function summarizeGmailSearch(\n messages: LifeOpsGmailMessageSummary[],\n): LifeOpsGmailSearchFeed[\"summary\"] {\n return {\n totalCount: messages.length,\n unreadCount: messages.filter((message) => message.isUnread).length,\n importantCount: messages.filter((message) => message.isImportant).length,\n replyNeededCount: messages.filter((message) => message.likelyReplyNeeded)\n .length,\n };\n}\n\nexport function summarizeGmailBatchReplyDrafts(\n drafts: LifeOpsGmailReplyDraft[],\n): LifeOpsGmailBatchReplyDraftsFeed[\"summary\"] {\n return {\n totalCount: drafts.length,\n sendAllowedCount: drafts.filter((draft) => draft.sendAllowed).length,\n requiresConfirmationCount: drafts.filter(\n (draft) => draft.requiresConfirmation,\n ).length,\n };\n}\n\nexport function collectCalendarEventContactEmails(\n event: LifeOpsCalendarEvent,\n): Set<string> {\n const emails = new Set<string>();\n const organizerEmail =\n typeof event.organizer?.email === \"string\"\n ? event.organizer.email.trim().toLowerCase()\n : \"\";\n if (organizerEmail) {\n emails.add(organizerEmail);\n }\n for (const attendee of event.attendees) {\n const email = attendee.email?.trim().toLowerCase() || \"\";\n if (email) {\n emails.add(email);\n }\n }\n return emails;\n}\n\nexport function extractSubjectTokens(subject: string): string[] {\n return subject\n .toLowerCase()\n .split(/[^a-z0-9]+/)\n .filter((token) => token.length >= 4);\n}\n\nexport function findLinkedMailForCalendarEvent(\n event: LifeOpsCalendarEvent,\n messages: LifeOpsGmailMessageSummary[],\n): LifeOpsGmailMessageSummary[] {\n const relatedEmails = collectCalendarEventContactEmails(event);\n const subjectTokens = new Set(extractSubjectTokens(event.title));\n\n return messages\n .filter((message) => {\n if (\n message.fromEmail &&\n relatedEmails.has(message.fromEmail.toLowerCase())\n ) {\n return true;\n }\n if (\n message.to.some((entry) =>\n relatedEmails.has(entry.trim().toLowerCase()),\n ) ||\n message.cc.some((entry) =>\n relatedEmails.has(entry.trim().toLowerCase()),\n )\n ) {\n return true;\n }\n const messageTokens = extractSubjectTokens(message.subject);\n return messageTokens.some((token) => subjectTokens.has(token));\n })\n .sort((left, right) => {\n const receivedDelta =\n Date.parse(right.receivedAt) - Date.parse(left.receivedAt);\n if (receivedDelta !== 0) {\n return receivedDelta;\n }\n return compareGmailMessagePriority(left, right);\n })\n .slice(0, 3);\n}\n\nexport function isGmailSyncStateFresh(args: {\n syncedAt: string;\n maxResults: number;\n requestedMaxResults: number;\n now: Date;\n}): boolean {\n const syncedAtMs = Date.parse(args.syncedAt);\n if (!Number.isFinite(syncedAtMs)) {\n return false;\n }\n if (args.now.getTime() - syncedAtMs > GOOGLE_GMAIL_CACHE_TTL_MS) {\n return false;\n }\n return args.maxResults >= args.requestedMaxResults;\n}\n\nexport function summarizeGmailTriage(\n messages: LifeOpsGmailMessageSummary[],\n): LifeOpsGmailTriageFeed[\"summary\"] {\n return {\n unreadCount: messages.filter((message) => message.isUnread).length,\n importantNewCount: messages.filter(\n (message) => message.isUnread && message.isImportant,\n ).length,\n likelyReplyNeededCount: messages.filter(\n (message) => message.likelyReplyNeeded,\n ).length,\n };\n}\n\nexport function summarizeGmailNeedsResponse(\n messages: LifeOpsGmailMessageSummary[],\n): LifeOpsGmailNeedsResponseFeed[\"summary\"] {\n return {\n totalCount: messages.length,\n unreadCount: messages.filter((message) => message.isUnread).length,\n importantCount: messages.filter((message) => message.isImportant).length,\n };\n}\n\nexport function summarizeGmailUnresponded(\n threads: LifeOpsGmailUnrespondedFeed[\"threads\"],\n): LifeOpsGmailUnrespondedFeed[\"summary\"] {\n return {\n totalCount: threads.length,\n oldestDaysWaiting:\n threads.length > 0\n ? Math.max(...threads.map((thread) => thread.daysWaiting))\n : null,\n };\n}\n\nfunction recommendationMessage(\n message: LifeOpsGmailMessageSummary,\n): LifeOpsGmailRecommendation[\"sampleMessages\"][number] {\n return {\n messageId: message.id,\n subject: message.subject,\n from: message.from,\n fromEmail: message.fromEmail,\n receivedAt: message.receivedAt,\n snippet: message.snippet,\n labels: message.labels,\n };\n}\n\nfunction hasGmailLabel(\n message: LifeOpsGmailMessageSummary,\n labelId: string,\n): boolean {\n const normalized = labelId.trim().toUpperCase();\n return message.labels.some(\n (label) => label.trim().toUpperCase() === normalized,\n );\n}\n\nexport function isGmailSpamReviewCandidate(\n message: LifeOpsGmailMessageSummary,\n): boolean {\n const metadata = message.metadata;\n const metadataClassification =\n typeof metadata.spamClassification === \"string\"\n ? metadata.spamClassification.trim().toLowerCase()\n : \"\";\n const metadataThreat =\n typeof metadata.threatCategory === \"string\"\n ? metadata.threatCategory.trim().toLowerCase()\n : \"\";\n const triageReason = message.triageReason.trim().toLowerCase();\n return (\n hasGmailLabel(message, \"SPAM\") ||\n hasGmailLabel(message, \"PHISHING\") ||\n metadata.spam === true ||\n metadata.phishing === true ||\n metadataClassification === \"spam\" ||\n metadataClassification === \"phishing\" ||\n metadataThreat === \"phishing\" ||\n /\\b(?:spam|phish(?:ing)?)\\b/.test(triageReason)\n );\n}\n\nexport function buildGmailSpamReviewItem(args: {\n message: LifeOpsGmailMessageSummary;\n grantId: string;\n accountEmail: string | null;\n now: string;\n}): LifeOpsGmailSpamReviewItem {\n const message = args.message;\n const isPhishing =\n hasGmailLabel(message, \"PHISHING\") ||\n message.metadata.phishing === true ||\n message.metadata.spamClassification === \"phishing\" ||\n message.metadata.threatCategory === \"phishing\" ||\n /\\bphish(?:ing)?\\b/i.test(message.triageReason);\n const isGmailSpam = hasGmailLabel(message, \"SPAM\");\n const rationale = isPhishing\n ? \"Gmail or upstream triage flagged this message as a phishing candidate; review it before reporting spam.\"\n : isGmailSpam\n ? \"Gmail labels this message as spam; review it before reporting or deleting.\"\n : \"LifeOps classified this Gmail message as a spam candidate; review it before reporting spam.\";\n const confidence = isGmailSpam ? 0.92 : isPhishing ? 0.88 : 0.76;\n return {\n id: createGmailSpamReviewItemId(\n message.agentId,\n message.provider,\n message.side,\n args.grantId,\n message.externalId,\n ),\n agentId: message.agentId,\n provider: message.provider,\n side: message.side,\n grantId: args.grantId,\n accountEmail: args.accountEmail,\n messageId: message.id,\n externalMessageId: message.externalId,\n threadId: message.threadId,\n subject: message.subject,\n from: message.from,\n fromEmail: message.fromEmail,\n receivedAt: message.receivedAt,\n snippet: message.snippet,\n labels: message.labels,\n rationale,\n confidence,\n status: \"pending\",\n createdAt: args.now,\n updatedAt: args.now,\n reviewedAt: null,\n };\n}\n\nexport function normalizeGmailSpamReviewStatus(\n value: unknown,\n field = \"status\",\n): LifeOpsGmailSpamReviewStatus {\n return normalizeEnumValue(value, field, LIFEOPS_GMAIL_SPAM_REVIEW_STATUSES);\n}\n\nexport function summarizeGmailSpamReviewItems(\n items: LifeOpsGmailSpamReviewItem[],\n): LifeOpsGmailSpamReviewFeed[\"summary\"] {\n return {\n totalCount: items.length,\n pendingCount: items.filter((item) => item.status === \"pending\").length,\n confirmedSpamCount: items.filter((item) => item.status === \"confirmed_spam\")\n .length,\n notSpamCount: items.filter((item) => item.status === \"not_spam\").length,\n dismissedCount: items.filter((item) => item.status === \"dismissed\").length,\n };\n}\n\nfunction isAutomatedLowValueGmailMessage(\n message: LifeOpsGmailMessageSummary,\n): boolean {\n const precedence =\n typeof message.metadata.precedence === \"string\"\n ? message.metadata.precedence.trim().toLowerCase()\n : \"\";\n return (\n !message.likelyReplyNeeded &&\n (Boolean(message.metadata.listId) ||\n precedence === \"bulk\" ||\n precedence === \"list\" ||\n hasGmailLabel(message, \"CATEGORY_PROMOTIONS\"))\n );\n}\n\ntype GmailRecommendationGrouping =\n | \"reply_needed\"\n | \"automated_low_value\"\n | \"spam_review\";\n\ntype GmailRecommendationBodyStatus = \"available\" | \"summary_only\" | \"missing\";\n\ninterface GmailRecommendationPolicyDetails {\n grouping: GmailRecommendationGrouping;\n signals: string[];\n reasons: string[];\n exclusionReasons: string[];\n operationAllowed: boolean;\n requiresHumanConfirmation: boolean;\n emailContentIsUntrusted: true;\n}\n\ninterface GmailRecommendationContextReadiness {\n bodyStatus: GmailRecommendationBodyStatus;\n bodyAvailableCount: number;\n snippetAvailableCount: number;\n threadLinkAvailableCount: number;\n replyHeaderAvailableCount: number;\n requiresBodyReadBeforeDraft: boolean;\n summaryFields: string[];\n missingContext: string[];\n}\n\ntype LifeOpsGmailAgentReadyRecommendation = LifeOpsGmailRecommendation & {\n policy: GmailRecommendationPolicyDetails;\n contextReadiness: GmailRecommendationContextReadiness;\n};\n\nfunction metadataString(\n metadata: Record<string, unknown>,\n field: string,\n): string | null {\n const value = metadata[field];\n return typeof value === \"string\" && value.trim().length > 0\n ? value.trim()\n : null;\n}\n\nfunction metadataBoolean(\n metadata: Record<string, unknown>,\n field: string,\n): boolean {\n return metadata[field] === true;\n}\n\nfunction uniqueStrings(\n values: readonly (string | null | undefined)[],\n): string[] {\n const items: string[] = [];\n const seen = new Set<string>();\n for (const value of values) {\n if (!value || seen.has(value)) {\n continue;\n }\n seen.add(value);\n items.push(value);\n }\n return items;\n}\n\nfunction hasGmailBodyTextContext(message: LifeOpsGmailMessageSummary): boolean {\n return [\"bodyText\", \"plainTextBody\", \"bodyPlainText\", \"textBody\"].some(\n (field) => metadataString(message.metadata, field) !== null,\n );\n}\n\nfunction hasGmailReplyHeaderContext(\n message: LifeOpsGmailMessageSummary,\n): boolean {\n return (\n metadataString(message.metadata, \"messageIdHeader\") !== null ||\n metadataString(message.metadata, \"referencesHeader\") !== null\n );\n}\n\nfunction gmailPolicySignalsForMessage(\n message: LifeOpsGmailMessageSummary,\n): string[] {\n const precedence = metadataString(message.metadata, \"precedence\")\n ?.toLowerCase()\n .replace(/\\s+/g, \"_\");\n const autoSubmitted = metadataString(message.metadata, \"autoSubmitted\")\n ?.toLowerCase()\n .replace(/\\s+/g, \"_\");\n const spamClassification = metadataString(\n message.metadata,\n \"spamClassification\",\n )\n ?.toLowerCase()\n .replace(/\\s+/g, \"_\");\n const threatCategory = metadataString(message.metadata, \"threatCategory\")\n ?.toLowerCase()\n .replace(/\\s+/g, \"_\");\n const triageReason = message.triageReason.toLowerCase();\n\n return uniqueStrings([\n message.likelyReplyNeeded ? \"likely_reply_needed\" : \"reply_not_needed\",\n message.isUnread ? \"unread\" : \"read\",\n message.isImportant ? \"important\" : \"not_important\",\n hasGmailLabel(message, \"INBOX\") ? \"label:inbox\" : null,\n hasGmailLabel(message, \"SPAM\") ? \"label:spam\" : null,\n hasGmailLabel(message, \"PHISHING\") ? \"label:phishing\" : null,\n hasGmailLabel(message, \"CATEGORY_PROMOTIONS\")\n ? \"label:category_promotions\"\n : null,\n metadataString(message.metadata, \"listId\") !== null\n ? \"header:list_id\"\n : null,\n precedence ? `header:precedence:${precedence}` : null,\n autoSubmitted ? `header:auto_submitted:${autoSubmitted}` : null,\n metadataBoolean(message.metadata, \"spam\") ? \"metadata:spam\" : null,\n metadataBoolean(message.metadata, \"phishing\") ? \"metadata:phishing\" : null,\n spamClassification\n ? `metadata:spam_classification:${spamClassification}`\n : null,\n threatCategory ? `metadata:threat_category:${threatCategory}` : null,\n triageReason.includes(\"direct-unread-reply-needed\")\n ? \"triage:direct_unread_reply_needed\"\n : null,\n triageReason.includes(\"automated-header\")\n ? \"triage:automated_header\"\n : null,\n ]);\n}\n\nfunction buildGmailRecommendationContextReadiness(args: {\n kind: LifeOpsGmailRecommendation[\"kind\"];\n messages: LifeOpsGmailMessageSummary[];\n}): GmailRecommendationContextReadiness {\n const bodyAvailableCount = args.messages.filter(\n hasGmailBodyTextContext,\n ).length;\n const snippetAvailableCount = args.messages.filter(\n (message) => message.snippet.trim().length > 0,\n ).length;\n const threadLinkAvailableCount = args.messages.filter(\n (message) => message.htmlLink !== null,\n ).length;\n const replyHeaderAvailableCount = args.messages.filter(\n hasGmailReplyHeaderContext,\n ).length;\n const requiresBodyReadBeforeDraft = args.kind === \"reply\";\n const bodyStatus: GmailRecommendationBodyStatus =\n bodyAvailableCount === args.messages.length\n ? \"available\"\n : snippetAvailableCount > 0\n ? \"summary_only\"\n : \"missing\";\n\n return {\n bodyStatus,\n bodyAvailableCount,\n snippetAvailableCount,\n threadLinkAvailableCount,\n replyHeaderAvailableCount,\n requiresBodyReadBeforeDraft,\n summaryFields: uniqueStrings([\n \"subject\",\n \"sender\",\n \"recipients\",\n \"received_at\",\n \"labels\",\n \"triage_reason\",\n snippetAvailableCount > 0 ? \"snippet\" : null,\n threadLinkAvailableCount > 0 ? \"thread_link\" : null,\n replyHeaderAvailableCount > 0 ? \"reply_headers\" : null,\n ]),\n missingContext: uniqueStrings([\n bodyAvailableCount === args.messages.length ? null : \"body_text\",\n requiresBodyReadBeforeDraft && replyHeaderAvailableCount === 0\n ? \"reply_headers\"\n : null,\n ]),\n };\n}\n\nfunction buildRecommendation(args: {\n id: string;\n kind: LifeOpsGmailRecommendation[\"kind\"];\n title: string;\n rationale: string;\n operation: LifeOpsGmailRecommendation[\"operation\"];\n messages: LifeOpsGmailMessageSummary[];\n grouping: GmailRecommendationGrouping;\n policyReasons: string[];\n exclusionReasons: string[];\n query?: string | null;\n labelIds?: string[];\n destructive?: boolean;\n confidence: number;\n}): LifeOpsGmailAgentReadyRecommendation | null {\n const messageIds = args.messages.map((message) => message.id);\n if (messageIds.length === 0) {\n return null;\n }\n const destructive = args.destructive === true;\n return {\n id: args.id,\n kind: args.kind,\n title: args.title,\n rationale: args.rationale,\n operation: args.operation,\n messageIds,\n query: args.query ?? null,\n labelIds: args.labelIds ?? [],\n affectedCount: messageIds.length,\n destructive,\n requiresConfirmation: true,\n confidence: args.confidence,\n sampleMessages: args.messages.slice(0, 5).map(recommendationMessage),\n policy: {\n grouping: args.grouping,\n signals: uniqueStrings(\n args.messages.flatMap((message) =>\n gmailPolicySignalsForMessage(message),\n ),\n ),\n reasons: args.policyReasons,\n exclusionReasons: args.exclusionReasons,\n operationAllowed: args.operation !== null,\n requiresHumanConfirmation: true,\n emailContentIsUntrusted: true,\n },\n contextReadiness: buildGmailRecommendationContextReadiness({\n kind: args.kind,\n messages: args.messages,\n }),\n };\n}\n\nexport function buildGmailRecommendations(\n messages: LifeOpsGmailMessageSummary[],\n): LifeOpsGmailRecommendation[] {\n const recommendations: Array<LifeOpsGmailAgentReadyRecommendation | null> =\n [];\n const replyMessages = messages\n .filter(\n (message) =>\n message.likelyReplyNeeded && !isGmailSpamReviewCandidate(message),\n )\n .slice(0, 25);\n recommendations.push(\n buildRecommendation({\n id: \"gmail-reply-needed\",\n kind: \"reply\",\n title: \"Draft replies for messages that need you\",\n rationale:\n \"These direct unread Gmail threads look reply-worthy; spam and phishing candidates stay in review.\",\n operation: null,\n messages: replyMessages,\n grouping: \"reply_needed\",\n policyReasons: [\n \"Only messages classified as likely reply-needed are included.\",\n \"No mailbox mutation is attached; this recommendation prepares draft work only.\",\n \"Full message bodies should be read before asking a model to draft replies.\",\n ],\n exclusionReasons: [\n \"Spam and phishing candidates are excluded from reply drafting.\",\n \"Automated, list, and promotional mail is excluded by the reply-needed classifier.\",\n ],\n confidence: replyMessages.length > 0 ? 0.84 : 0,\n }),\n );\n\n const archiveMessages = messages\n .filter(\n (message) =>\n hasGmailLabel(message, \"INBOX\") &&\n !isGmailSpamReviewCandidate(message) &&\n isAutomatedLowValueGmailMessage(message),\n )\n .slice(0, 50);\n recommendations.push(\n buildRecommendation({\n id: \"gmail-archive-low-value\",\n kind: \"archive\",\n title: \"Archive low-value automated mail\",\n rationale:\n \"These inbox messages carry list, bulk, or promotions signals; reply-needed and spam-review messages are excluded.\",\n operation: \"archive\",\n messages: archiveMessages,\n grouping: \"automated_low_value\",\n policyReasons: [\n \"Only current inbox messages are eligible for archive recommendations.\",\n \"Automated, list, bulk, or promotions signals are required.\",\n \"Messages that look reply-worthy are not archived by this recommendation.\",\n ],\n exclusionReasons: [\n \"Spam and phishing candidates are routed to review instead of archive.\",\n \"Personal or ambiguous messages without automated-mail signals are excluded.\",\n ],\n confidence: archiveMessages.length > 0 ? 0.78 : 0,\n }),\n );\n\n const markReadMessages = messages\n .filter(\n (message) =>\n message.isUnread &&\n !message.isImportant &&\n !message.likelyReplyNeeded &&\n !isGmailSpamReviewCandidate(message) &&\n isAutomatedLowValueGmailMessage(message),\n )\n .slice(0, 50);\n recommendations.push(\n buildRecommendation({\n id: \"gmail-mark-read-low-value\",\n kind: \"mark_read\",\n title: \"Mark low-value automated mail as read\",\n rationale:\n \"These unread messages are automated or promotional; important, reply-needed, and spam-review messages are excluded.\",\n operation: \"mark_read\",\n messages: markReadMessages,\n grouping: \"automated_low_value\",\n policyReasons: [\n \"Only unread automated or promotional messages are eligible.\",\n \"Important and reply-needed messages remain visible to the owner.\",\n \"Mark-read does not delete or move messages.\",\n ],\n exclusionReasons: [\n \"Spam and phishing candidates are routed to review instead of mark-read.\",\n \"Important messages and likely reply-needed threads are excluded.\",\n ],\n confidence: markReadMessages.length > 0 ? 0.74 : 0,\n }),\n );\n\n const spamMessages = messages.filter(isGmailSpamReviewCandidate).slice(0, 25);\n recommendations.push(\n buildRecommendation({\n id: \"gmail-review-spam\",\n kind: \"review_spam\",\n title: \"Review spam folder candidates\",\n rationale:\n \"These messages carry Gmail spam, phishing, or upstream spam-review signals and need review before mutation.\",\n operation: null,\n messages: spamMessages,\n grouping: \"spam_review\",\n policyReasons: [\n \"Spam and phishing signals are collected into a review-only recommendation.\",\n \"No delete, trash, or report-spam operation is preselected.\",\n \"Human confirmation is required before any destructive mailbox action.\",\n ],\n exclusionReasons: [\n \"Spam-review candidates are excluded from archive, mark-read, and reply-draft groups.\",\n ],\n destructive: false,\n confidence: spamMessages.length > 0 ? 0.9 : 0,\n }),\n );\n\n return recommendations.filter(\n (recommendation): recommendation is LifeOpsGmailAgentReadyRecommendation =>\n recommendation !== null,\n );\n}\n\nexport function summarizeGmailRecommendations(\n recommendations: LifeOpsGmailRecommendation[],\n): LifeOpsGmailRecommendationsFeed[\"summary\"] {\n return {\n totalCount: recommendations.length,\n replyCount: recommendations.filter(\n (recommendation) => recommendation.kind === \"reply\",\n ).length,\n archiveCount: recommendations.filter(\n (recommendation) => recommendation.kind === \"archive\",\n ).length,\n markReadCount: recommendations.filter(\n (recommendation) => recommendation.kind === \"mark_read\",\n ).length,\n spamReviewCount: recommendations.filter(\n (recommendation) => recommendation.kind === \"review_spam\",\n ).length,\n destructiveCount: recommendations.filter(\n (recommendation) => recommendation.destructive,\n ).length,\n };\n}\n\n/**\n * Re-export shim. `wrapUntrustedEmailContent` moved to `@elizaos/shared`\n * alongside the email classifier that depends on it; this preserves the\n * historical import path for in-plugin callers.\n */\nexport { wrapUntrustedEmailContent } from \"@elizaos/shared\";\n\nexport function buildFallbackGmailReplyDraftBody(args: {\n message: LifeOpsGmailMessageSummary;\n tone: \"brief\" | \"neutral\" | \"warm\";\n intent?: string;\n includeQuotedOriginal: boolean;\n senderName: string;\n}): string {\n const recipientLabel =\n args.message.from.split(\"<\")[0]?.trim() || args.message.fromEmail || \"\";\n const greeting = recipientLabel ? `${recipientLabel},` : \"\";\n const subject = args.message.subject.trim() || \"your message\";\n const bodyCore = args.intent?.trim()\n ? args.intent.trim()\n : args.tone === \"brief\"\n ? `Thanks for the note about ${subject}. I saw it and will follow up shortly.`\n : args.tone === \"warm\"\n ? `Thanks for reaching out about ${subject}. I reviewed your note and wanted to follow up.`\n : `Thanks for the note about ${subject}. I reviewed your message and wanted to follow up.`;\n const bodyLines = [greeting, bodyCore, args.senderName].filter(\n (line) => line.trim().length > 0,\n );\n if (args.includeQuotedOriginal && args.message.snippet.trim().length > 0) {\n bodyLines.push(\n \"\",\n ...args.message.snippet\n .trim()\n .split(\"\\n\")\n .map((line) => `> ${line.trim()}`),\n );\n }\n\n return bodyLines.join(\"\\n\");\n}\n\nexport function normalizeGeneratedGmailReplyDraftBody(\n value: string,\n): string | null {\n const withoutThink = value.replace(/<think>[\\s\\S]*?<\\/think>/gi, \" \").trim();\n if (!withoutThink) {\n return null;\n }\n const withoutCodeFences = withoutThink\n .replace(/^```[a-z0-9_-]*\\s*/i, \"\")\n .replace(/\\s*```$/i, \"\")\n .trim();\n const withoutSubject = withoutCodeFences.replace(/^subject:\\s*.+\\n+/i, \"\");\n const normalized = withoutSubject\n .replace(/\\r\\n/g, \"\\n\")\n .replace(/^[\"'`]+|[\"'`]+$/g, \"\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n return normalized.length > 0 ? normalized : null;\n}\n\nexport function buildGmailReplyPreviewLines(bodyText: string): string[] {\n const lines = bodyText\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.length > 0)\n .slice(0, 3);\n return lines.length > 0 ? lines : [bodyText.trim()].filter(Boolean);\n}\n\nexport function buildGmailReplyDraft(args: {\n message: LifeOpsGmailMessageSummary;\n senderName: string;\n sendAllowed: boolean;\n bodyText: string;\n}): LifeOpsGmailReplyDraft {\n const recipient = args.message.replyTo ?? args.message.fromEmail ?? null;\n if (!recipient) {\n fail(409, \"The selected Gmail message has no replyable sender.\");\n }\n\n return {\n messageId: args.message.id,\n threadId: args.message.threadId,\n subject: args.message.subject,\n to: [recipient.toLowerCase()],\n cc: [],\n bodyText: args.bodyText,\n previewLines: buildGmailReplyPreviewLines(args.bodyText),\n sendAllowed: args.sendAllowed,\n requiresConfirmation: true,\n };\n}\n\nexport function createCalendarEventId(\n agentId: string,\n provider: LifeOpsConnectorGrant[\"provider\"],\n side: LifeOpsConnectorGrant[\"side\"],\n calendarId: string,\n externalId: string,\n): string {\n const digest = crypto\n .createHash(\"sha256\")\n .update(`${agentId}:${provider}:${side}:${calendarId}:${externalId}`)\n .digest(\"hex\");\n return `life-calendar-${digest.slice(0, 32)}`;\n}\n\nexport function createGmailMessageId(\n agentId: string,\n provider: LifeOpsConnectorGrant[\"provider\"],\n side: LifeOpsConnectorGrant[\"side\"],\n grantId: string,\n externalMessageId: string,\n): string {\n const digest = crypto\n .createHash(\"sha256\")\n .update(\n `${agentId}:${provider}:${side}:gmail:${grantId}:${externalMessageId}`,\n )\n .digest(\"hex\");\n return `life-gmail-${digest.slice(0, 32)}`;\n}\n\nexport function createGmailSpamReviewItemId(\n agentId: string,\n provider: LifeOpsConnectorGrant[\"provider\"],\n side: LifeOpsConnectorGrant[\"side\"],\n grantId: string,\n externalMessageId: string,\n): string {\n const digest = crypto\n .createHash(\"sha256\")\n .update(\n `${agentId}:${provider}:${side}:gmail-spam-review:${grantId}:${externalMessageId}`,\n )\n .digest(\"hex\");\n return `life-gmail-spam-${digest.slice(0, 32)}`;\n}\n\nexport function materializeGmailMessageSummary(args: {\n agentId: string;\n side: LifeOpsConnectorGrant[\"side\"];\n grantId: string;\n accountEmail?: string | null;\n message: SyncedGoogleGmailMessageSummary;\n syncedAt: string;\n}): LifeOpsGmailMessageSummary {\n return {\n id: createGmailMessageId(\n args.agentId,\n \"google\",\n args.side,\n args.grantId,\n args.message.externalId,\n ),\n agentId: args.agentId,\n provider: \"google\",\n side: args.side,\n ...args.message,\n grantId: args.grantId,\n accountEmail: args.accountEmail ?? undefined,\n syncedAt: args.syncedAt,\n updatedAt: args.syncedAt,\n };\n}\n\nexport function isCalendarSyncStateFresh(args: {\n syncedAt: string;\n timeMin: string;\n timeMax: string;\n windowStartAt: string;\n windowEndAt: string;\n now: Date;\n}): boolean {\n const syncedAtMs = Date.parse(args.syncedAt);\n if (!Number.isFinite(syncedAtMs)) {\n return false;\n }\n if (args.now.getTime() - syncedAtMs > GOOGLE_CALENDAR_CACHE_TTL_MS) {\n return false;\n }\n return (\n Date.parse(args.windowStartAt) <= Date.parse(args.timeMin) &&\n Date.parse(args.windowEndAt) >= Date.parse(args.timeMax)\n );\n}\n"],"mappings":"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","names":["trimmed"]}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gmail runtime-service seam for the inbox unsubscribe back-end.
|
|
3
|
+
*
|
|
4
|
+
* The inbox plugin legitimately consumes Gmail. This module resolves the
|
|
5
|
+
* `@elizaos/plugin-google` runtime service (`runtime.getService("google")`),
|
|
6
|
+
* derives the account-scoped connector grant the unsubscribe path needs, and
|
|
7
|
+
* exposes the narrow Gmail surface that surface uses: search, mailto/HTTP
|
|
8
|
+
* unsubscribe send, sender-filter creation, and thread trashing.
|
|
9
|
+
*
|
|
10
|
+
* It is the focused successor to the resolver helpers the unsubscribe path used
|
|
11
|
+
* out of PA's `google-plugin-delegates.ts` (`requireGoogleWorkspaceService`,
|
|
12
|
+
* `requireGoogleServiceMethod`, `resolveGoogleConnectorAccount`,
|
|
13
|
+
* `googleGrantFromAccount`, `accountIdForGrant`, the Gmail-triage grant gate).
|
|
14
|
+
* It carries no dependency on `@elizaos/plugin-personal-assistant`; capability
|
|
15
|
+
* derivation is reproduced here from the connector-account metadata/scopes
|
|
16
|
+
* (the same mapping PA performs) so the grant is self-contained.
|
|
17
|
+
*/
|
|
18
|
+
import { type IAgentRuntime } from "@elizaos/core";
|
|
19
|
+
import type { GoogleParsedMailto } from "@elizaos/plugin-google";
|
|
20
|
+
import { type LifeOpsConnectorGrant, type LifeOpsConnectorSide, type LifeOpsGmailSearchFeed } from "@elizaos/shared";
|
|
21
|
+
declare function accountIdForGrant(grant: LifeOpsConnectorGrant): string;
|
|
22
|
+
/**
|
|
23
|
+
* The Gmail surface the inbox unsubscribe back-end needs, resolved once from
|
|
24
|
+
* the runtime so the service does not reach into `runtime.getService` itself.
|
|
25
|
+
*/
|
|
26
|
+
export interface InboxGmailGateway {
|
|
27
|
+
/** Resolve the Gmail-triage grant (throws 409/403 if unconnected/ungranted). */
|
|
28
|
+
requireGmailGrant(): Promise<LifeOpsConnectorGrant>;
|
|
29
|
+
/** Search synced Gmail messages, returning a search feed with header metadata. */
|
|
30
|
+
searchGmail(args: {
|
|
31
|
+
grant: LifeOpsConnectorGrant;
|
|
32
|
+
query: string;
|
|
33
|
+
maxResults: number;
|
|
34
|
+
includeSpamTrash?: boolean;
|
|
35
|
+
now?: Date;
|
|
36
|
+
}): Promise<LifeOpsGmailSearchFeed>;
|
|
37
|
+
/** Send a List-Unsubscribe mailto request for the account behind the grant. */
|
|
38
|
+
sendMailtoUnsubscribeEmail(accountId: string, mailto: GoogleParsedMailto): Promise<void>;
|
|
39
|
+
/** Create a Gmail filter that trashes future mail from a sender. */
|
|
40
|
+
createGmailFilterForSender(accountId: string, fromAddress: string): Promise<{
|
|
41
|
+
filterId: string | null;
|
|
42
|
+
}>;
|
|
43
|
+
/** Trash an existing Gmail thread. */
|
|
44
|
+
trashGmailThread(accountId: string, threadId: string): Promise<void>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Build the inbox Gmail gateway bound to a runtime. `requestedSide` defaults to
|
|
48
|
+
* the owner side (the unsubscribe path operates on the owner's mailbox).
|
|
49
|
+
*/
|
|
50
|
+
export declare function createInboxGmailGateway(runtime: IAgentRuntime, agentId: string, requestedSide?: LifeOpsConnectorSide): InboxGmailGateway;
|
|
51
|
+
export { accountIdForGrant };
|
|
52
|
+
//# sourceMappingURL=google-gmail-seam.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"google-gmail-seam.d.ts","sourceRoot":"","sources":["../../src/inbox/google-gmail-seam.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAGL,KAAK,aAAa,EACnB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAEV,kBAAkB,EAEnB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAEL,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EAEzB,KAAK,sBAAsB,EAE5B,MAAM,iBAAiB,CAAC;AA0MzB,iBAAS,iBAAiB,CAAC,KAAK,EAAE,qBAAqB,GAAG,MAAM,CAS/D;AAyDD;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,gFAAgF;IAChF,iBAAiB,IAAI,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACpD,kFAAkF;IAClF,WAAW,CAAC,IAAI,EAAE;QAChB,KAAK,EAAE,qBAAqB,CAAC;QAC7B,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAC3B,GAAG,CAAC,EAAE,IAAI,CAAC;KACZ,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;IACpC,+EAA+E;IAC/E,0BAA0B,CACxB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,oEAAoE;IACpE,0BAA0B,CACxB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IACxC,sCAAsC;IACtC,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtE;AAcD;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,MAAM,EACf,aAAa,GAAE,oBAA8B,GAC5C,iBAAiB,CA6EnB;AAED,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getConnectorAccountManager
|
|
3
|
+
} from "@elizaos/core";
|
|
4
|
+
import {
|
|
5
|
+
fail
|
|
6
|
+
} from "@elizaos/shared";
|
|
7
|
+
const GOOGLE_CONNECTOR_ACCOUNT_GRANT_PREFIX = "connector-account:";
|
|
8
|
+
function isRecord(value) {
|
|
9
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
10
|
+
}
|
|
11
|
+
function stringValue(value) {
|
|
12
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
13
|
+
}
|
|
14
|
+
function stringArray(value) {
|
|
15
|
+
return Array.isArray(value) ? value.map((item) => typeof item === "string" ? item.trim() : "").filter(Boolean) : [];
|
|
16
|
+
}
|
|
17
|
+
function accountMetadata(account) {
|
|
18
|
+
return isRecord(account.metadata) ? account.metadata : {};
|
|
19
|
+
}
|
|
20
|
+
function googleSideForAccount(account) {
|
|
21
|
+
return account.role === "AGENT" ? "agent" : "owner";
|
|
22
|
+
}
|
|
23
|
+
function googleCapabilitiesForAccount(account) {
|
|
24
|
+
const meta = accountMetadata(account);
|
|
25
|
+
const scopes = stringArray(meta.grantedScopes);
|
|
26
|
+
const capabilities = /* @__PURE__ */ new Set([
|
|
27
|
+
"google.basic_identity"
|
|
28
|
+
]);
|
|
29
|
+
if (scopes.some((scope) => scope.includes("gmail.readonly"))) {
|
|
30
|
+
capabilities.add("google.gmail.triage");
|
|
31
|
+
}
|
|
32
|
+
if (scopes.some((scope) => scope.includes("gmail.send"))) {
|
|
33
|
+
capabilities.add("google.gmail.send");
|
|
34
|
+
capabilities.add("google.gmail.triage");
|
|
35
|
+
}
|
|
36
|
+
if (scopes.some(
|
|
37
|
+
(scope) => scope.includes("gmail.modify") || scope.includes("gmail.settings")
|
|
38
|
+
)) {
|
|
39
|
+
capabilities.add("google.gmail.manage");
|
|
40
|
+
capabilities.add("google.gmail.triage");
|
|
41
|
+
}
|
|
42
|
+
return [...capabilities];
|
|
43
|
+
}
|
|
44
|
+
function googleAccountEmail(account) {
|
|
45
|
+
const meta = accountMetadata(account);
|
|
46
|
+
return (stringValue(meta.email) ?? stringValue(account.displayHandle) ?? null)?.toLowerCase() ?? null;
|
|
47
|
+
}
|
|
48
|
+
function grantIdForAccount(accountId) {
|
|
49
|
+
return `${GOOGLE_CONNECTOR_ACCOUNT_GRANT_PREFIX}${accountId}`;
|
|
50
|
+
}
|
|
51
|
+
function accountIdFromGrantId(grantId) {
|
|
52
|
+
const normalized = stringValue(grantId);
|
|
53
|
+
if (!normalized) return null;
|
|
54
|
+
return normalized.startsWith(GOOGLE_CONNECTOR_ACCOUNT_GRANT_PREFIX) ? normalized.slice(GOOGLE_CONNECTOR_ACCOUNT_GRANT_PREFIX.length) : normalized;
|
|
55
|
+
}
|
|
56
|
+
function grantFromAccount(args) {
|
|
57
|
+
const { account, agentId } = args;
|
|
58
|
+
const capabilities = googleCapabilitiesForAccount(account);
|
|
59
|
+
const meta = accountMetadata(account);
|
|
60
|
+
const createdAt = new Date(account.createdAt).toISOString();
|
|
61
|
+
const updatedAt = new Date(account.updatedAt).toISOString();
|
|
62
|
+
return {
|
|
63
|
+
id: grantIdForAccount(account.id),
|
|
64
|
+
agentId,
|
|
65
|
+
provider: "google",
|
|
66
|
+
side: googleSideForAccount(account),
|
|
67
|
+
identity: {},
|
|
68
|
+
grantedScopes: stringArray(meta.grantedScopes),
|
|
69
|
+
capabilities,
|
|
70
|
+
tokenRef: null,
|
|
71
|
+
mode: "local",
|
|
72
|
+
executionTarget: "local",
|
|
73
|
+
sourceOfTruth: "connector_account",
|
|
74
|
+
preferredByAgent: meta.isDefault === true,
|
|
75
|
+
cloudConnectionId: null,
|
|
76
|
+
connectorAccountId: account.id,
|
|
77
|
+
identityEmail: googleAccountEmail(account),
|
|
78
|
+
metadata: {
|
|
79
|
+
...meta,
|
|
80
|
+
connectorAccountId: account.id,
|
|
81
|
+
connectorAccountProvider: "google"
|
|
82
|
+
},
|
|
83
|
+
lastRefreshAt: updatedAt,
|
|
84
|
+
createdAt,
|
|
85
|
+
updatedAt
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
async function listGoogleConnectorAccounts(runtime, requestedSide) {
|
|
89
|
+
const manager = getConnectorAccountManager(runtime);
|
|
90
|
+
const accounts = await manager.listAccounts("google");
|
|
91
|
+
return accounts.filter(
|
|
92
|
+
(account) => account.status !== "disabled" && account.status !== "revoked"
|
|
93
|
+
).filter(
|
|
94
|
+
(account) => requestedSide ? googleSideForAccount(account) === requestedSide : true
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
async function resolveGoogleConnectorAccount(args) {
|
|
98
|
+
const accountId = accountIdFromGrantId(args.grantId);
|
|
99
|
+
const accounts = await listGoogleConnectorAccounts(
|
|
100
|
+
args.runtime,
|
|
101
|
+
args.requestedSide
|
|
102
|
+
);
|
|
103
|
+
if (accountId) {
|
|
104
|
+
return accounts.find(
|
|
105
|
+
(account) => account.id === accountId || account.externalId === accountId || account.displayHandle === accountId
|
|
106
|
+
) ?? null;
|
|
107
|
+
}
|
|
108
|
+
return accounts.find(
|
|
109
|
+
(account) => account.status === "connected" && accountMetadata(account).isDefault === true
|
|
110
|
+
) ?? accounts.find((account) => account.status === "connected") ?? accounts[0] ?? null;
|
|
111
|
+
}
|
|
112
|
+
function requireGoogleWorkspaceService(runtime) {
|
|
113
|
+
const service = runtime.getService("google");
|
|
114
|
+
if (!isRecord(service)) {
|
|
115
|
+
fail(
|
|
116
|
+
503,
|
|
117
|
+
"Google Workspace service is not registered. Enable @elizaos/plugin-google before using inbox Gmail features."
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
return service;
|
|
121
|
+
}
|
|
122
|
+
function requireGoogleServiceMethod(runtime, method) {
|
|
123
|
+
const service = requireGoogleWorkspaceService(runtime);
|
|
124
|
+
const fn = service[method];
|
|
125
|
+
if (typeof fn !== "function") {
|
|
126
|
+
fail(
|
|
127
|
+
501,
|
|
128
|
+
`@elizaos/plugin-google does not expose ${String(method)} for account-scoped inbox access.`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
return fn.bind(
|
|
132
|
+
service
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
function accountIdForGrant(grant) {
|
|
136
|
+
return stringValue(grant.connectorAccountId) ?? accountIdFromGrantId(grant.id) ?? fail(
|
|
137
|
+
409,
|
|
138
|
+
"Google connector account id is missing. Reconnect Google through connector account management."
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
function gmailMessageFromGoogle(args) {
|
|
142
|
+
const { message, grant, agentId, syncedAt } = args;
|
|
143
|
+
const labels = message.labelIds ?? [];
|
|
144
|
+
const fromName = message.from?.name?.trim();
|
|
145
|
+
const fromEmail = message.from?.email?.trim() ?? null;
|
|
146
|
+
const externalId = message.id;
|
|
147
|
+
const receivedAt = message.receivedAt ?? syncedAt;
|
|
148
|
+
return {
|
|
149
|
+
id: `${agentId}:google:${grant.side}:gmail:${externalId}`,
|
|
150
|
+
externalId,
|
|
151
|
+
agentId,
|
|
152
|
+
provider: "google",
|
|
153
|
+
side: grant.side,
|
|
154
|
+
threadId: message.threadId ?? externalId,
|
|
155
|
+
subject: message.subject ?? "(no subject)",
|
|
156
|
+
from: fromName || fromEmail || "Unknown sender",
|
|
157
|
+
fromEmail,
|
|
158
|
+
replyTo: message.replyTo?.email ?? null,
|
|
159
|
+
to: (message.to ?? []).map((item) => item.email),
|
|
160
|
+
cc: (message.cc ?? []).map((item) => item.email),
|
|
161
|
+
snippet: message.snippet ?? message.bodyText?.slice(0, 240) ?? "",
|
|
162
|
+
receivedAt,
|
|
163
|
+
isUnread: labels.includes("UNREAD"),
|
|
164
|
+
isImportant: labels.includes("IMPORTANT"),
|
|
165
|
+
likelyReplyNeeded: labels.includes("INBOX") && !labels.includes("SENT"),
|
|
166
|
+
triageScore: labels.includes("IMPORTANT") ? 90 : labels.includes("UNREAD") ? 70 : 40,
|
|
167
|
+
triageReason: labels.includes("IMPORTANT") ? "Marked important in Gmail." : labels.includes("UNREAD") ? "Unread inbox message." : "Recent Gmail message.",
|
|
168
|
+
labels,
|
|
169
|
+
htmlLink: null,
|
|
170
|
+
metadata: {
|
|
171
|
+
googlePlugin: true,
|
|
172
|
+
headers: message.headers ?? {},
|
|
173
|
+
bodyHtml: message.bodyHtml
|
|
174
|
+
},
|
|
175
|
+
syncedAt,
|
|
176
|
+
updatedAt: syncedAt,
|
|
177
|
+
connectorAccountId: grant.connectorAccountId ?? void 0,
|
|
178
|
+
grantId: grant.id,
|
|
179
|
+
accountEmail: grant.identityEmail ?? void 0
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function summarizeSearch(messages) {
|
|
183
|
+
return {
|
|
184
|
+
totalCount: messages.length,
|
|
185
|
+
unreadCount: messages.filter((message) => message.isUnread).length,
|
|
186
|
+
importantCount: messages.filter((message) => message.isImportant).length,
|
|
187
|
+
replyNeededCount: messages.filter((message) => message.likelyReplyNeeded).length
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
function createInboxGmailGateway(runtime, agentId, requestedSide = "owner") {
|
|
191
|
+
return {
|
|
192
|
+
async requireGmailGrant() {
|
|
193
|
+
const account = await resolveGoogleConnectorAccount({
|
|
194
|
+
runtime,
|
|
195
|
+
requestedSide
|
|
196
|
+
});
|
|
197
|
+
if (account?.status !== "connected") {
|
|
198
|
+
fail(409, "Google Gmail is not connected.");
|
|
199
|
+
}
|
|
200
|
+
const grant = grantFromAccount({ account, agentId });
|
|
201
|
+
if (!grant.capabilities.includes("google.gmail.triage")) {
|
|
202
|
+
fail(403, "Google Gmail triage access has not been granted.");
|
|
203
|
+
}
|
|
204
|
+
return grant;
|
|
205
|
+
},
|
|
206
|
+
async searchGmail(args) {
|
|
207
|
+
const searchMessages = requireGoogleServiceMethod(
|
|
208
|
+
runtime,
|
|
209
|
+
"searchMessages"
|
|
210
|
+
);
|
|
211
|
+
const syncedAt = (args.now ?? /* @__PURE__ */ new Date()).toISOString();
|
|
212
|
+
const query = args.includeSpamTrash ? `${args.query} in:anywhere` : args.query;
|
|
213
|
+
const googleMessages = await searchMessages({
|
|
214
|
+
accountId: accountIdForGrant(args.grant),
|
|
215
|
+
query,
|
|
216
|
+
limit: args.maxResults
|
|
217
|
+
});
|
|
218
|
+
const messages = googleMessages.map(
|
|
219
|
+
(message) => gmailMessageFromGoogle({
|
|
220
|
+
message,
|
|
221
|
+
grant: args.grant,
|
|
222
|
+
agentId,
|
|
223
|
+
syncedAt
|
|
224
|
+
})
|
|
225
|
+
);
|
|
226
|
+
return {
|
|
227
|
+
query: args.query,
|
|
228
|
+
messages,
|
|
229
|
+
source: "synced",
|
|
230
|
+
syncedAt,
|
|
231
|
+
summary: summarizeSearch(messages)
|
|
232
|
+
};
|
|
233
|
+
},
|
|
234
|
+
async sendMailtoUnsubscribeEmail(accountId, mailto) {
|
|
235
|
+
const send = requireGoogleServiceMethod(
|
|
236
|
+
runtime,
|
|
237
|
+
"sendMailtoUnsubscribeEmail"
|
|
238
|
+
);
|
|
239
|
+
await send({ accountId, mailto });
|
|
240
|
+
},
|
|
241
|
+
async createGmailFilterForSender(accountId, fromAddress) {
|
|
242
|
+
const createFilter = requireGoogleServiceMethod(
|
|
243
|
+
runtime,
|
|
244
|
+
"createGmailFilterForSender"
|
|
245
|
+
);
|
|
246
|
+
const filter = await createFilter({
|
|
247
|
+
accountId,
|
|
248
|
+
fromAddress,
|
|
249
|
+
trash: true
|
|
250
|
+
});
|
|
251
|
+
return { filterId: filter.filterId };
|
|
252
|
+
},
|
|
253
|
+
async trashGmailThread(accountId, threadId) {
|
|
254
|
+
const trash = requireGoogleServiceMethod(runtime, "trashGmailThread");
|
|
255
|
+
await trash({ accountId, threadId });
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
export {
|
|
260
|
+
accountIdForGrant,
|
|
261
|
+
createInboxGmailGateway
|
|
262
|
+
};
|
|
263
|
+
//# sourceMappingURL=google-gmail-seam.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/inbox/google-gmail-seam.ts"],"sourcesContent":["/**\n * Gmail runtime-service seam for the inbox unsubscribe back-end.\n *\n * The inbox plugin legitimately consumes Gmail. This module resolves the\n * `@elizaos/plugin-google` runtime service (`runtime.getService(\"google\")`),\n * derives the account-scoped connector grant the unsubscribe path needs, and\n * exposes the narrow Gmail surface that surface uses: search, mailto/HTTP\n * unsubscribe send, sender-filter creation, and thread trashing.\n *\n * It is the focused successor to the resolver helpers the unsubscribe path used\n * out of PA's `google-plugin-delegates.ts` (`requireGoogleWorkspaceService`,\n * `requireGoogleServiceMethod`, `resolveGoogleConnectorAccount`,\n * `googleGrantFromAccount`, `accountIdForGrant`, the Gmail-triage grant gate).\n * It carries no dependency on `@elizaos/plugin-personal-assistant`; capability\n * derivation is reproduced here from the connector-account metadata/scopes\n * (the same mapping PA performs) so the grant is self-contained.\n */\n\nimport {\n type ConnectorAccount,\n getConnectorAccountManager,\n type IAgentRuntime,\n} from \"@elizaos/core\";\nimport type {\n GoogleMessageSummary,\n GoogleParsedMailto,\n IGoogleWorkspaceService,\n} from \"@elizaos/plugin-google\";\nimport {\n fail,\n type LifeOpsConnectorGrant,\n type LifeOpsConnectorSide,\n type LifeOpsGmailMessageSummary,\n type LifeOpsGmailSearchFeed,\n type LifeOpsGoogleCapability,\n} from \"@elizaos/shared\";\n\nconst GOOGLE_CONNECTOR_ACCOUNT_GRANT_PREFIX = \"connector-account:\";\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction stringValue(value: unknown): string | null {\n return typeof value === \"string\" && value.trim().length > 0\n ? value.trim()\n : null;\n}\n\nfunction stringArray(value: unknown): string[] {\n return Array.isArray(value)\n ? value\n .map((item) => (typeof item === \"string\" ? item.trim() : \"\"))\n .filter(Boolean)\n : [];\n}\n\nfunction accountMetadata(account: ConnectorAccount): Record<string, unknown> {\n return isRecord(account.metadata) ? account.metadata : {};\n}\n\nfunction googleSideForAccount(\n account: Pick<ConnectorAccount, \"role\">,\n): LifeOpsConnectorSide {\n return account.role === \"AGENT\" ? \"agent\" : \"owner\";\n}\n\n/**\n * Derive the Google capabilities granted to a connector account from its\n * metadata + granted OAuth scopes. Mirrors PA's grant derivation for the\n * Gmail-relevant subset (triage / send / manage); identity is always implied.\n */\nfunction googleCapabilitiesForAccount(\n account: ConnectorAccount,\n): LifeOpsGoogleCapability[] {\n const meta = accountMetadata(account);\n const scopes = stringArray(meta.grantedScopes);\n const capabilities = new Set<LifeOpsGoogleCapability>([\n \"google.basic_identity\",\n ]);\n if (scopes.some((scope) => scope.includes(\"gmail.readonly\"))) {\n capabilities.add(\"google.gmail.triage\");\n }\n if (scopes.some((scope) => scope.includes(\"gmail.send\"))) {\n capabilities.add(\"google.gmail.send\");\n capabilities.add(\"google.gmail.triage\");\n }\n if (\n scopes.some(\n (scope) =>\n scope.includes(\"gmail.modify\") || scope.includes(\"gmail.settings\"),\n )\n ) {\n capabilities.add(\"google.gmail.manage\");\n capabilities.add(\"google.gmail.triage\");\n }\n return [...capabilities];\n}\n\nfunction googleAccountEmail(account: ConnectorAccount): string | null {\n const meta = accountMetadata(account);\n return (\n (\n stringValue(meta.email) ??\n stringValue(account.displayHandle) ??\n null\n )?.toLowerCase() ?? null\n );\n}\n\nfunction grantIdForAccount(accountId: string): string {\n return `${GOOGLE_CONNECTOR_ACCOUNT_GRANT_PREFIX}${accountId}`;\n}\n\nfunction accountIdFromGrantId(\n grantId: string | null | undefined,\n): string | null {\n const normalized = stringValue(grantId);\n if (!normalized) return null;\n return normalized.startsWith(GOOGLE_CONNECTOR_ACCOUNT_GRANT_PREFIX)\n ? normalized.slice(GOOGLE_CONNECTOR_ACCOUNT_GRANT_PREFIX.length)\n : normalized;\n}\n\nfunction grantFromAccount(args: {\n account: ConnectorAccount;\n agentId: string;\n}): LifeOpsConnectorGrant {\n const { account, agentId } = args;\n const capabilities = googleCapabilitiesForAccount(account);\n const meta = accountMetadata(account);\n const createdAt = new Date(account.createdAt).toISOString();\n const updatedAt = new Date(account.updatedAt).toISOString();\n return {\n id: grantIdForAccount(account.id),\n agentId,\n provider: \"google\",\n side: googleSideForAccount(account),\n identity: {},\n grantedScopes: stringArray(meta.grantedScopes),\n capabilities,\n tokenRef: null,\n mode: \"local\",\n executionTarget: \"local\",\n sourceOfTruth: \"connector_account\",\n preferredByAgent: meta.isDefault === true,\n cloudConnectionId: null,\n connectorAccountId: account.id,\n identityEmail: googleAccountEmail(account),\n metadata: {\n ...meta,\n connectorAccountId: account.id,\n connectorAccountProvider: \"google\",\n },\n lastRefreshAt: updatedAt,\n createdAt,\n updatedAt,\n } as LifeOpsConnectorGrant;\n}\n\nasync function listGoogleConnectorAccounts(\n runtime: IAgentRuntime,\n requestedSide?: LifeOpsConnectorSide,\n): Promise<ConnectorAccount[]> {\n const manager = getConnectorAccountManager(runtime);\n const accounts = await manager.listAccounts(\"google\");\n return accounts\n .filter(\n (account) =>\n account.status !== \"disabled\" && account.status !== \"revoked\",\n )\n .filter((account) =>\n requestedSide ? googleSideForAccount(account) === requestedSide : true,\n );\n}\n\nasync function resolveGoogleConnectorAccount(args: {\n runtime: IAgentRuntime;\n requestedSide?: LifeOpsConnectorSide;\n grantId?: string | null;\n}): Promise<ConnectorAccount | null> {\n const accountId = accountIdFromGrantId(args.grantId);\n const accounts = await listGoogleConnectorAccounts(\n args.runtime,\n args.requestedSide,\n );\n if (accountId) {\n return (\n accounts.find(\n (account) =>\n account.id === accountId ||\n account.externalId === accountId ||\n account.displayHandle === accountId,\n ) ?? null\n );\n }\n return (\n accounts.find(\n (account) =>\n account.status === \"connected\" &&\n accountMetadata(account).isDefault === true,\n ) ??\n accounts.find((account) => account.status === \"connected\") ??\n accounts[0] ??\n null\n );\n}\n\nfunction requireGoogleWorkspaceService(\n runtime: IAgentRuntime,\n): Record<string, unknown> {\n const service = runtime.getService(\"google\");\n if (!isRecord(service)) {\n fail(\n 503,\n \"Google Workspace service is not registered. Enable @elizaos/plugin-google before using inbox Gmail features.\",\n );\n }\n return service;\n}\n\nfunction requireGoogleServiceMethod<\n K extends keyof IGoogleWorkspaceService & string,\n>(runtime: IAgentRuntime, method: K): IGoogleWorkspaceService[K] {\n const service = requireGoogleWorkspaceService(runtime);\n const fn = service[method];\n if (typeof fn !== \"function\") {\n fail(\n 501,\n `@elizaos/plugin-google does not expose ${String(method)} for account-scoped inbox access.`,\n );\n }\n return (fn as (...args: unknown[]) => unknown).bind(\n service,\n ) as IGoogleWorkspaceService[K];\n}\n\nfunction accountIdForGrant(grant: LifeOpsConnectorGrant): string {\n return (\n stringValue(grant.connectorAccountId) ??\n accountIdFromGrantId(grant.id) ??\n fail(\n 409,\n \"Google connector account id is missing. Reconnect Google through connector account management.\",\n )\n );\n}\n\nfunction gmailMessageFromGoogle(args: {\n message: GoogleMessageSummary;\n grant: LifeOpsConnectorGrant;\n agentId: string;\n syncedAt: string;\n}): LifeOpsGmailMessageSummary {\n const { message, grant, agentId, syncedAt } = args;\n const labels = message.labelIds ?? [];\n const fromName = message.from?.name?.trim();\n const fromEmail = message.from?.email?.trim() ?? null;\n const externalId = message.id;\n const receivedAt = message.receivedAt ?? syncedAt;\n return {\n id: `${agentId}:google:${grant.side}:gmail:${externalId}`,\n externalId,\n agentId,\n provider: \"google\",\n side: grant.side,\n threadId: message.threadId ?? externalId,\n subject: message.subject ?? \"(no subject)\",\n from: fromName || fromEmail || \"Unknown sender\",\n fromEmail,\n replyTo: message.replyTo?.email ?? null,\n to: (message.to ?? []).map((item) => item.email),\n cc: (message.cc ?? []).map((item) => item.email),\n snippet: message.snippet ?? message.bodyText?.slice(0, 240) ?? \"\",\n receivedAt,\n isUnread: labels.includes(\"UNREAD\"),\n isImportant: labels.includes(\"IMPORTANT\"),\n likelyReplyNeeded: labels.includes(\"INBOX\") && !labels.includes(\"SENT\"),\n triageScore: labels.includes(\"IMPORTANT\")\n ? 90\n : labels.includes(\"UNREAD\")\n ? 70\n : 40,\n triageReason: labels.includes(\"IMPORTANT\")\n ? \"Marked important in Gmail.\"\n : labels.includes(\"UNREAD\")\n ? \"Unread inbox message.\"\n : \"Recent Gmail message.\",\n labels,\n htmlLink: null,\n metadata: {\n googlePlugin: true,\n headers: message.headers ?? {},\n bodyHtml: message.bodyHtml,\n },\n syncedAt,\n updatedAt: syncedAt,\n connectorAccountId: grant.connectorAccountId ?? undefined,\n grantId: grant.id,\n accountEmail: grant.identityEmail ?? undefined,\n };\n}\n\n/**\n * The Gmail surface the inbox unsubscribe back-end needs, resolved once from\n * the runtime so the service does not reach into `runtime.getService` itself.\n */\nexport interface InboxGmailGateway {\n /** Resolve the Gmail-triage grant (throws 409/403 if unconnected/ungranted). */\n requireGmailGrant(): Promise<LifeOpsConnectorGrant>;\n /** Search synced Gmail messages, returning a search feed with header metadata. */\n searchGmail(args: {\n grant: LifeOpsConnectorGrant;\n query: string;\n maxResults: number;\n includeSpamTrash?: boolean;\n now?: Date;\n }): Promise<LifeOpsGmailSearchFeed>;\n /** Send a List-Unsubscribe mailto request for the account behind the grant. */\n sendMailtoUnsubscribeEmail(\n accountId: string,\n mailto: GoogleParsedMailto,\n ): Promise<void>;\n /** Create a Gmail filter that trashes future mail from a sender. */\n createGmailFilterForSender(\n accountId: string,\n fromAddress: string,\n ): Promise<{ filterId: string | null }>;\n /** Trash an existing Gmail thread. */\n trashGmailThread(accountId: string, threadId: string): Promise<void>;\n}\n\nfunction summarizeSearch(\n messages: LifeOpsGmailMessageSummary[],\n): LifeOpsGmailSearchFeed[\"summary\"] {\n return {\n totalCount: messages.length,\n unreadCount: messages.filter((message) => message.isUnread).length,\n importantCount: messages.filter((message) => message.isImportant).length,\n replyNeededCount: messages.filter((message) => message.likelyReplyNeeded)\n .length,\n };\n}\n\n/**\n * Build the inbox Gmail gateway bound to a runtime. `requestedSide` defaults to\n * the owner side (the unsubscribe path operates on the owner's mailbox).\n */\nexport function createInboxGmailGateway(\n runtime: IAgentRuntime,\n agentId: string,\n requestedSide: LifeOpsConnectorSide = \"owner\",\n): InboxGmailGateway {\n return {\n async requireGmailGrant(): Promise<LifeOpsConnectorGrant> {\n const account = await resolveGoogleConnectorAccount({\n runtime,\n requestedSide,\n });\n if (account?.status !== \"connected\") {\n fail(409, \"Google Gmail is not connected.\");\n }\n const grant = grantFromAccount({ account, agentId });\n if (!grant.capabilities.includes(\"google.gmail.triage\")) {\n fail(403, \"Google Gmail triage access has not been granted.\");\n }\n return grant;\n },\n\n async searchGmail(args): Promise<LifeOpsGmailSearchFeed> {\n const searchMessages = requireGoogleServiceMethod(\n runtime,\n \"searchMessages\",\n );\n const syncedAt = (args.now ?? new Date()).toISOString();\n const query = args.includeSpamTrash\n ? `${args.query} in:anywhere`\n : args.query;\n const googleMessages = await searchMessages({\n accountId: accountIdForGrant(args.grant),\n query,\n limit: args.maxResults,\n });\n const messages = googleMessages.map((message) =>\n gmailMessageFromGoogle({\n message,\n grant: args.grant,\n agentId,\n syncedAt,\n }),\n );\n return {\n query: args.query,\n messages,\n source: \"synced\",\n syncedAt,\n summary: summarizeSearch(messages),\n };\n },\n\n async sendMailtoUnsubscribeEmail(accountId, mailto): Promise<void> {\n const send = requireGoogleServiceMethod(\n runtime,\n \"sendMailtoUnsubscribeEmail\",\n );\n await send({ accountId, mailto });\n },\n\n async createGmailFilterForSender(\n accountId,\n fromAddress,\n ): Promise<{ filterId: string | null }> {\n const createFilter = requireGoogleServiceMethod(\n runtime,\n \"createGmailFilterForSender\",\n );\n const filter = await createFilter({\n accountId,\n fromAddress,\n trash: true,\n });\n return { filterId: filter.filterId };\n },\n\n async trashGmailThread(accountId, threadId): Promise<void> {\n const trash = requireGoogleServiceMethod(runtime, \"trashGmailThread\");\n await trash({ accountId, threadId });\n },\n };\n}\n\nexport { accountIdForGrant };\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { IAgentRuntime } from "@elizaos/core";
|
|
2
|
+
import { type GetLifeOpsGmailTriageRequest, type LifeOpsGmailTriageFeed, type LifeOpsGoogleConnectorStatus, type LifeOpsXConnectorStatus, type LifeOpsXDm } from "@elizaos/shared";
|
|
3
|
+
import type { InboundMessage } from "./types.js";
|
|
4
|
+
export interface GmailInboxSource {
|
|
5
|
+
getGoogleConnectorStatus(requestUrl: URL): Promise<LifeOpsGoogleConnectorStatus>;
|
|
6
|
+
getGmailTriage(requestUrl: URL, request?: GetLifeOpsGmailTriageRequest): Promise<LifeOpsGmailTriageFeed>;
|
|
7
|
+
}
|
|
8
|
+
export interface XDmInboxSource {
|
|
9
|
+
getXConnectorStatus(): Promise<LifeOpsXConnectorStatus>;
|
|
10
|
+
syncXDms(opts?: {
|
|
11
|
+
limit?: number;
|
|
12
|
+
}): Promise<{
|
|
13
|
+
synced: number;
|
|
14
|
+
}>;
|
|
15
|
+
getXDms(opts?: {
|
|
16
|
+
limit?: number;
|
|
17
|
+
}): Promise<LifeOpsXDm[]>;
|
|
18
|
+
}
|
|
19
|
+
export declare function fetchChatMessages(runtime: IAgentRuntime, opts: {
|
|
20
|
+
/** Only scan these sources. Defaults to all connector-tagged chat rooms. */
|
|
21
|
+
sources?: string[];
|
|
22
|
+
/** Only return messages newer than this ISO timestamp. */
|
|
23
|
+
sinceIso?: string;
|
|
24
|
+
/** Max messages to return. */
|
|
25
|
+
limit?: number;
|
|
26
|
+
}): Promise<InboundMessage[]>;
|
|
27
|
+
export declare function fetchGmailMessages(source: GmailInboxSource, opts: {
|
|
28
|
+
sinceIso?: string;
|
|
29
|
+
limit?: number;
|
|
30
|
+
/** Filter to a single Gmail account by Google grant id. */
|
|
31
|
+
grantId?: string;
|
|
32
|
+
}): Promise<InboundMessage[]>;
|
|
33
|
+
export declare function fetchXDmMessages(source: XDmInboxSource, opts: {
|
|
34
|
+
sinceIso?: string;
|
|
35
|
+
limit?: number;
|
|
36
|
+
}): Promise<InboundMessage[]>;
|
|
37
|
+
export declare function fetchAllMessages(runtime: IAgentRuntime, opts: {
|
|
38
|
+
sources?: string[];
|
|
39
|
+
sinceIso?: string;
|
|
40
|
+
limit?: number;
|
|
41
|
+
includeGmail?: boolean;
|
|
42
|
+
gmailSource?: GmailInboxSource;
|
|
43
|
+
xDmSource?: XDmInboxSource;
|
|
44
|
+
/** Filter Gmail to a single account by Google grant id. */
|
|
45
|
+
gmailGrantId?: string;
|
|
46
|
+
}): Promise<InboundMessage[]>;
|
|
47
|
+
//# sourceMappingURL=message-fetcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-fetcher.d.ts","sourceRoot":"","sources":["../../src/inbox/message-fetcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAA6B,MAAM,eAAe,CAAC;AAC9E,OAAO,EAEL,KAAK,4BAA4B,EACjC,KAAK,sBAAsB,EAC3B,KAAK,4BAA4B,EACjC,KAAK,uBAAuB,EAC5B,KAAK,UAAU,EAEhB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAsBjD,MAAM,WAAW,gBAAgB;IAC/B,wBAAwB,CACtB,UAAU,EAAE,GAAG,GACd,OAAO,CAAC,4BAA4B,CAAC,CAAC;IACzC,cAAc,CACZ,UAAU,EAAE,GAAG,EACf,OAAO,CAAC,EAAE,4BAA4B,GACrC,OAAO,CAAC,sBAAsB,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,cAAc;IAC7B,mBAAmB,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAAC;IACxD,QAAQ,CAAC,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjE,OAAO,CAAC,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;CAC3D;AAED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,aAAa,EACtB,IAAI,EAAE;IACJ,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GACA,OAAO,CAAC,cAAc,EAAE,CAAC,CAkJ3B;AA0ED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE;IACJ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GACA,OAAO,CAAC,cAAc,EAAE,CAAC,CA+D3B;AAED,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE;IACJ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GACA,OAAO,CAAC,cAAc,EAAE,CAAC,CAmD3B;AAED,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,aAAa,EACtB,IAAI,EAAE;IACJ,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,2DAA2D;IAC3D,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GACA,OAAO,CAAC,cAAc,EAAE,CAAC,CAiD3B"}
|