@agent-native/core 0.101.6 → 0.101.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.
Files changed (101) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +6 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/a2a/artifact-response.ts +255 -12
  5. package/corpus/core/src/agent/thread-data-builder.ts +84 -11
  6. package/corpus/core/src/integrations/a2a-continuation-processor.ts +99 -7
  7. package/corpus/core/src/integrations/adapters/discord.ts +3 -1
  8. package/corpus/core/src/integrations/adapters/email.ts +4 -1
  9. package/corpus/core/src/integrations/adapters/google-docs.ts +4 -1
  10. package/corpus/core/src/integrations/adapters/microsoft-teams.ts +3 -1
  11. package/corpus/core/src/integrations/adapters/slack.ts +38 -9
  12. package/corpus/core/src/integrations/adapters/telegram.ts +32 -10
  13. package/corpus/core/src/integrations/adapters/whatsapp.ts +5 -1
  14. package/corpus/core/src/integrations/index.ts +1 -0
  15. package/corpus/core/src/integrations/types.ts +9 -2
  16. package/corpus/core/src/integrations/webhook-handler.ts +69 -45
  17. package/corpus/core/src/server/agent-chat/action-filters-a2a.ts +1 -0
  18. package/corpus/templates/content/.agents/skills/content/SKILL.md +31 -0
  19. package/corpus/templates/content/.agents/skills/document-editing/SKILL.md +35 -21
  20. package/corpus/templates/content/AGENTS.md +29 -7
  21. package/corpus/templates/content/actions/_database-source-utils.ts +30 -16
  22. package/corpus/templates/content/actions/_database-utils.ts +15 -1
  23. package/corpus/templates/content/actions/_local-file-documents.ts +53 -1
  24. package/corpus/templates/content/actions/_property-utils.ts +7 -1
  25. package/corpus/templates/content/actions/configure-document-property.ts +11 -0
  26. package/corpus/templates/content/actions/create-content-database.ts +11 -0
  27. package/corpus/templates/content/actions/create-document.ts +9 -0
  28. package/corpus/templates/content/actions/create-inline-content-database.ts +3 -0
  29. package/corpus/templates/content/actions/export-content-source.ts +1 -0
  30. package/corpus/templates/content/actions/get-content-database.ts +13 -1
  31. package/corpus/templates/content/actions/get-document.ts +12 -2
  32. package/corpus/templates/content/actions/import-content-source.ts +6 -0
  33. package/corpus/templates/content/actions/list-documents.ts +3 -0
  34. package/corpus/templates/content/actions/pull-document.ts +2 -0
  35. package/corpus/templates/content/actions/search-documents.ts +5 -1
  36. package/corpus/templates/content/actions/update-document.ts +16 -1
  37. package/corpus/templates/content/actions/view-screen.ts +15 -2
  38. package/corpus/templates/content/app/components/editor/DescriptionField.tsx +121 -0
  39. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +14 -0
  40. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +223 -34
  41. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +56 -8
  42. package/corpus/templates/content/app/i18n/zh-TW.ts +5 -0
  43. package/corpus/templates/content/app/i18n-data.ts +52 -0
  44. package/corpus/templates/content/changelog/2026-07-14-pages-and-databases-can-describe-themselves-with-guidance-fo.md +6 -0
  45. package/corpus/templates/content/changelog/2026-07-14-slack-follow-ups-now-retain-created-content-identity-and-per.md +6 -0
  46. package/corpus/templates/content/server/db/schema.ts +4 -0
  47. package/corpus/templates/content/server/lib/document-context.ts +76 -0
  48. package/corpus/templates/content/server/lib/public-documents.ts +3 -0
  49. package/corpus/templates/content/server/plugins/db.ts +6 -0
  50. package/corpus/templates/content/server/routes/api/document-agent-context.json.get.ts +5 -0
  51. package/corpus/templates/content/shared/api.ts +17 -0
  52. package/corpus/templates/content/shared/content-source.ts +7 -0
  53. package/corpus/templates/content/shared/properties.ts +2 -0
  54. package/dist/a2a/artifact-response.d.ts +15 -0
  55. package/dist/a2a/artifact-response.d.ts.map +1 -1
  56. package/dist/a2a/artifact-response.js +204 -5
  57. package/dist/a2a/artifact-response.js.map +1 -1
  58. package/dist/agent/thread-data-builder.d.ts +6 -0
  59. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  60. package/dist/agent/thread-data-builder.js +71 -8
  61. package/dist/agent/thread-data-builder.js.map +1 -1
  62. package/dist/integrations/a2a-continuation-processor.d.ts.map +1 -1
  63. package/dist/integrations/a2a-continuation-processor.js +72 -6
  64. package/dist/integrations/a2a-continuation-processor.js.map +1 -1
  65. package/dist/integrations/adapters/discord.d.ts.map +1 -1
  66. package/dist/integrations/adapters/discord.js +1 -0
  67. package/dist/integrations/adapters/discord.js.map +1 -1
  68. package/dist/integrations/adapters/email.d.ts.map +1 -1
  69. package/dist/integrations/adapters/email.js +2 -0
  70. package/dist/integrations/adapters/email.js.map +1 -1
  71. package/dist/integrations/adapters/google-docs.d.ts.map +1 -1
  72. package/dist/integrations/adapters/google-docs.js +2 -0
  73. package/dist/integrations/adapters/google-docs.js.map +1 -1
  74. package/dist/integrations/adapters/microsoft-teams.d.ts.map +1 -1
  75. package/dist/integrations/adapters/microsoft-teams.js +1 -0
  76. package/dist/integrations/adapters/microsoft-teams.js.map +1 -1
  77. package/dist/integrations/adapters/slack.d.ts.map +1 -1
  78. package/dist/integrations/adapters/slack.js +21 -6
  79. package/dist/integrations/adapters/slack.js.map +1 -1
  80. package/dist/integrations/adapters/telegram.d.ts.map +1 -1
  81. package/dist/integrations/adapters/telegram.js +11 -2
  82. package/dist/integrations/adapters/telegram.js.map +1 -1
  83. package/dist/integrations/adapters/whatsapp.d.ts.map +1 -1
  84. package/dist/integrations/adapters/whatsapp.js +3 -0
  85. package/dist/integrations/adapters/whatsapp.js.map +1 -1
  86. package/dist/integrations/index.d.ts +1 -1
  87. package/dist/integrations/index.d.ts.map +1 -1
  88. package/dist/integrations/index.js.map +1 -1
  89. package/dist/integrations/types.d.ts +8 -2
  90. package/dist/integrations/types.d.ts.map +1 -1
  91. package/dist/integrations/types.js.map +1 -1
  92. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  93. package/dist/integrations/webhook-handler.js +40 -43
  94. package/dist/integrations/webhook-handler.js.map +1 -1
  95. package/dist/observability/routes.d.ts +1 -1
  96. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  97. package/dist/resources/handlers.d.ts +1 -1
  98. package/dist/server/agent-chat/action-filters-a2a.d.ts.map +1 -1
  99. package/dist/server/agent-chat/action-filters-a2a.js +1 -0
  100. package/dist/server/agent-chat/action-filters-a2a.js.map +1 -1
  101. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"email.js","sourceRoot":"","sources":["../../../src/integrations/adapters/email.ts"],"names":[],"mappings":"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","sourcesContent":["import { timingSafeEqual } from \"node:crypto\";\n\nimport type { H3Event } from \"h3\";\nimport { getHeader, readRawBody as h3ReadRawBody } from \"h3\";\n\nimport { getDbExec } from \"../../db/client.js\";\nimport type { EnvKeyConfig } from \"../../server/create-server.js\";\nimport { resolveSecret } from \"../../server/credential-provider.js\";\nimport {\n sendEmail,\n isEmailConfigured,\n getEmailProvider,\n} from \"../../server/email.js\";\nimport { getIntegrationConfig } from \"../config-store.js\";\nimport type {\n PlatformAdapter,\n IncomingMessage,\n OutgoingMessage,\n IntegrationStatus,\n OutboundTarget,\n} from \"../types.js\";\n\n/** Max body length before truncation */\nconst EMAIL_MAX_BODY_LENGTH = 15000;\n\n/** Rate limit: max emails per sender within the window */\nconst RATE_LIMIT_MAX = 20;\n/** Rate limit window in ms (1 hour) */\nconst RATE_LIMIT_WINDOW_MS = 60 * 60 * 1000;\n\n/**\n * One-shot warning flags so we don't spam logs on every webhook.\n * Cleared per process — one warning per cold start is enough to surface\n * a misconfiguration without leaking config status to anyone with log access\n * (M6 in the webhook security audit).\n */\nlet _resendUnverifiedWarned = false;\nlet _sendgridUnverifiedWarned = false;\n\nfunction escapeLike(value: string): string {\n return value.replace(/[!%_]/g, (match) => `!${match}`);\n}\n\n/**\n * Returns true when the deployment is running in production mode and the\n * operator has NOT explicitly opted into accepting unverified webhooks for\n * local testing. In production we MUST refuse webhooks whose signature can't\n * be verified — accepting them with attacker-controlled `from:` addresses\n * lets the dispatch owner-resolution path run as the victim (C1 in the\n * webhook security audit).\n */\nfunction shouldRefuseWhenSecretMissing(): boolean {\n if (process.env.AGENT_NATIVE_ALLOW_UNVERIFIED_WEBHOOKS === \"1\") return false;\n return process.env.NODE_ENV === \"production\";\n}\n\n/**\n * Create an Email platform adapter for inbound/outbound email via\n * Resend or SendGrid webhooks.\n *\n * Required env vars:\n * - EMAIL_AGENT_ADDRESS — The email address the agent receives mail at\n *\n * One of these must also be set (checked via isEmailConfigured()):\n * - RESEND_API_KEY — For sending/receiving via Resend\n * - SENDGRID_API_KEY — For sending/receiving via SendGrid\n *\n * Optional:\n * - EMAIL_INBOUND_WEBHOOK_SECRET — Webhook signature verification secret\n */\nexport function emailAdapter(): PlatformAdapter {\n return {\n platform: \"email\",\n label: \"Email\",\n\n getRequiredEnvKeys(): EnvKeyConfig[] {\n return [\n {\n key: \"EMAIL_AGENT_ADDRESS\",\n label: \"Agent Email Address\",\n required: true,\n helpText:\n \"The email address people will use to message your agent (e.g. `agent@yourcompany.com`, or pick from your `<slug>.resend.app` sandbox).\",\n },\n {\n key: \"RESEND_API_KEY\",\n label: \"Resend API Key\",\n required: false,\n helpText:\n \"From resend.com → API keys (starts with `re_`). Either Resend or SendGrid is required for sending and receiving mail.\",\n },\n {\n key: \"SENDGRID_API_KEY\",\n label: \"SendGrid API Key\",\n required: false,\n helpText:\n \"From sendgrid.com → Settings → API Keys (starts with `SG.`). Either Resend or SendGrid is required.\",\n },\n {\n key: \"EMAIL_INBOUND_WEBHOOK_SECRET\",\n label: \"Inbound Webhook Secret\",\n required: false,\n helpText:\n \"Optional. From Resend (Webhooks → Signing Secret, starts with `whsec_`) or your SendGrid Inbound Parse basic-auth password. Used to verify inbound webhooks are real.\",\n },\n ];\n },\n\n async handleVerification(\n _event: H3Event,\n ): Promise<{ handled: boolean; response?: unknown }> {\n // Email webhooks don't need challenge handshakes\n return { handled: false };\n },\n\n async verifyWebhook(event: H3Event): Promise<boolean> {\n const secret =\n (await resolveSecret(\"EMAIL_INBOUND_WEBHOOK_SECRET\")) ?? undefined;\n const provider = await getEmailProvider();\n\n if (provider === \"resend\") {\n return verifyResendWebhook(event, secret);\n }\n\n if (provider === \"sendgrid\") {\n return verifySendGridWebhook(event, secret);\n }\n\n // No provider configured — reject\n console.warn(\"[email] No email provider configured, rejecting webhook\");\n return false;\n },\n\n async parseIncomingMessage(\n event: H3Event,\n ): Promise<IncomingMessage | null> {\n const provider = await getEmailProvider();\n const agentAddress = (\n await resolveSecret(\"EMAIL_AGENT_ADDRESS\")\n )?.toLowerCase();\n if (!agentAddress) {\n console.warn(\"[email] EMAIL_AGENT_ADDRESS not configured\");\n return null;\n }\n\n let parsed: ParsedEmail | null = null;\n\n if (provider === \"resend\") {\n parsed = await parseResendWebhook(event);\n } else if (provider === \"sendgrid\") {\n parsed = await parseSendGridWebhook(event);\n }\n\n if (!parsed) return null;\n\n // Rate limiting (SQL-backed heuristic — counts the sender's already-queued\n // tasks within the last hour). The previous in-memory map reset on every\n // serverless cold start, so the actual ceiling per attacker was\n // RATE_LIMIT_MAX × number_of_active_instances. SQL-backed counting holds\n // across instances. See H4 in the webhook security audit.\n const senderEmail = parsed.from.email.toLowerCase();\n if (await isRateLimited(senderEmail)) {\n console.warn(\n `[email] Rate limited sender: ${senderEmail} (>${RATE_LIMIT_MAX}/hr)`,\n );\n return null;\n }\n\n // Check allowed domains\n const config = await getIntegrationConfig(\"email\");\n if (config?.configData?.allowedDomains) {\n const allowed = config.configData.allowedDomains as string[];\n if (allowed.length > 0) {\n const senderDomain = senderEmail.split(\"@\")[1];\n if (!senderDomain || !allowed.includes(senderDomain)) {\n console.warn(\n `[email] Rejected email from ${senderEmail}: domain not in allowedDomains`,\n );\n return null;\n }\n }\n }\n\n // Determine if agent was CC'd (not in To, but in CC)\n const toAddresses = parsed.to.map((a) => a.toLowerCase());\n const ccAddresses = (parsed.cc ?? []).map((a) => a.toLowerCase());\n const isCC =\n !toAddresses.includes(agentAddress) &&\n ccAddresses.includes(agentAddress);\n\n // Build thread ID from References chain (Gmail-style: oldest Message-ID is thread root).\n // Scope the thread root by sender so an attacker who can forge a `References:`\n // header pointing at someone else's thread root can't graft into that thread.\n // Without this scoping, a third party could craft an inbound email whose\n // References chain matches a known victim's Message-ID and inject messages into\n // the victim's existing conversation — leaking prior content via the agent's\n // reply (M1 in the webhooks security audit).\n const threadRootId = scopeThreadIdToSender(\n getThreadRootId(parsed.messageId, parsed.references),\n senderEmail,\n );\n\n // Build body text\n let bodyText = parsed.text || stripHtmlForPlainText(parsed.html || \"\");\n\n // Truncate if needed\n if (bodyText.length > EMAIL_MAX_BODY_LENGTH) {\n bodyText =\n bodyText.slice(0, EMAIL_MAX_BODY_LENGTH) + \"\\n[Message truncated]\";\n }\n\n // Prefix CC'd emails with context\n if (isCC) {\n const otherRecipients = toAddresses\n .filter((a) => a !== agentAddress)\n .join(\", \");\n bodyText =\n `[CC'd on email between ${senderEmail} and ${otherRecipients || \"others\"}]\\n` +\n `Subject: ${parsed.subject}\\n\\n` +\n bodyText;\n }\n\n return {\n platform: \"email\",\n externalThreadId: threadRootId,\n text: bodyText,\n senderName: parsed.from.name,\n senderId: senderEmail,\n // Carry the message-authentication verdict downstream. Owner\n // resolution (dispatch) must NOT grant a real user's identity /\n // credentials unless the sender is verified — an unverified or\n // spoofed `From:` falls back to a synthetic, credential-less owner.\n senderVerified: parsed.senderVerified,\n platformContext: {\n messageId: parsed.messageId,\n subject: parsed.subject,\n from: senderEmail,\n to: parsed.to,\n cc: parsed.cc,\n inReplyTo: parsed.inReplyTo,\n references: parsed.references,\n isCC,\n senderVerified: parsed.senderVerified,\n },\n timestamp: parsed.date ? new Date(parsed.date).getTime() : Date.now(),\n };\n },\n\n async sendResponse(\n message: OutgoingMessage,\n context: IncomingMessage,\n ): Promise<void> {\n const agentAddress = await resolveSecret(\"EMAIL_AGENT_ADDRESS\");\n if (!agentAddress) {\n console.error(\"[email] EMAIL_AGENT_ADDRESS not configured\");\n return;\n }\n\n const config = await getIntegrationConfig(\"email\");\n const displayName =\n (config?.configData?.displayName as string) || \"Dispatch Agent\";\n\n // EMAIL_FROM overrides the from-address — required when the receiving\n // address is on a sub-domain that can't be a verified sender (e.g.\n // *.resend.app). Inbound and outbound addresses can differ.\n const emailFrom = await resolveSecret(\"EMAIL_FROM\");\n const fromAddress = emailFrom || `${displayName} <${agentAddress}>`;\n\n const subject = context.platformContext.subject as string;\n const reSubject = subject.startsWith(\"Re: \") ? subject : `Re: ${subject}`;\n\n try {\n await sendEmail({\n to: context.senderId!,\n from: fromAddress,\n subject: reSubject,\n html: message.text,\n text: stripHtmlForPlainText(message.text),\n inReplyTo: context.platformContext.messageId as string,\n references: buildReferencesHeader(context.platformContext),\n cc: context.platformContext.isCC\n ? buildReplyAllCc(context, agentAddress)\n : undefined,\n });\n } catch (err) {\n console.error(\"[email] Failed to send response:\", err);\n }\n },\n\n async sendMessageToTarget(\n message: OutgoingMessage,\n target: OutboundTarget,\n ): Promise<void> {\n const agentAddress = await resolveSecret(\"EMAIL_AGENT_ADDRESS\");\n if (!agentAddress) {\n console.error(\"[email] EMAIL_AGENT_ADDRESS not configured\");\n return;\n }\n\n const config = await getIntegrationConfig(\"email\");\n const displayName =\n (config?.configData?.displayName as string) || \"Dispatch Agent\";\n\n try {\n await sendEmail({\n to: target.destination,\n from: `${displayName} <${agentAddress}>`,\n subject: target.label || \"Message from Dispatch Agent\",\n html: message.text,\n text: stripHtmlForPlainText(message.text),\n ...(target.threadRef\n ? {\n inReplyTo: target.threadRef,\n references: target.threadRef,\n }\n : {}),\n });\n } catch (err) {\n console.error(\"[email] Failed to send proactive message:\", err);\n throw err;\n }\n },\n\n formatAgentResponse(text: string): OutgoingMessage {\n const bodyHtml = markdownToHtml(text);\n const html = wrapInEmailTemplate(bodyHtml);\n return { text: html, platformContext: {} };\n },\n\n async getStatus(_baseUrl?: string): Promise<IntegrationStatus> {\n const hasAgentAddress = !!(await resolveSecret(\"EMAIL_AGENT_ADDRESS\"));\n const hasEmailProvider = await isEmailConfigured();\n const hasWebhookSecret = !!(await resolveSecret(\n \"EMAIL_INBOUND_WEBHOOK_SECRET\",\n ));\n const configured = hasAgentAddress && hasEmailProvider;\n\n return {\n platform: \"email\",\n label: \"Email\",\n enabled: false, // overridden by plugin\n configured,\n details: {\n hasAgentAddress,\n hasEmailProvider,\n hasWebhookSecret,\n provider: await getEmailProvider(),\n },\n error: !configured\n ? \"Save EMAIL_AGENT_ADDRESS and either RESEND_API_KEY or SENDGRID_API_KEY in settings\"\n : undefined,\n };\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Parsed email shape\n// ---------------------------------------------------------------------------\n\ninterface ParsedEmail {\n messageId: string;\n subject: string;\n from: { name?: string; email: string };\n to: string[];\n cc?: string[];\n text?: string;\n html?: string;\n inReplyTo?: string;\n references?: string[];\n date?: string;\n /**\n * True when the provider's message-authentication results show that the\n * mail genuinely originated from the From domain: DKIM `pass` aligned with\n * the From domain, or an aligned SPF `pass`. False when results are absent\n * or fail — we fail closed so a spoofed `From:` can never be treated as\n * verified. See FINDING 3 (inbound-email impersonation) in the webhook\n * security audit.\n */\n senderVerified: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Webhook verification\n// ---------------------------------------------------------------------------\n\nasync function verifyResendWebhook(\n event: H3Event,\n secret?: string,\n): Promise<boolean> {\n if (!secret) {\n if (shouldRefuseWhenSecretMissing()) {\n if (!_resendUnverifiedWarned) {\n _resendUnverifiedWarned = true;\n console.error(\n \"[email] EMAIL_INBOUND_WEBHOOK_SECRET not set — refusing Resend webhook in production. \" +\n \"Set EMAIL_INBOUND_WEBHOOK_SECRET, or set AGENT_NATIVE_ALLOW_UNVERIFIED_WEBHOOKS=1 for local testing only.\",\n );\n }\n return false;\n }\n if (!_resendUnverifiedWarned) {\n _resendUnverifiedWarned = true;\n console.warn(\n \"[email] EMAIL_INBOUND_WEBHOOK_SECRET not set — accepting Resend webhook without verification (dev mode)\",\n );\n }\n return true;\n }\n\n const svixId = getHeader(event, \"svix-id\");\n const svixTimestamp = getHeader(event, \"svix-timestamp\");\n const svixSignature = getHeader(event, \"svix-signature\");\n\n if (!svixId || !svixTimestamp || !svixSignature) {\n console.warn(\"[email] Missing Svix signature headers\");\n return false;\n }\n\n // Reject requests older than 5 minutes (replay protection)\n const ts = parseInt(svixTimestamp, 10);\n if (Math.abs(Date.now() / 1000 - ts) > 300) {\n console.warn(\"[email] Svix timestamp too old, rejecting\");\n return false;\n }\n\n const body = await readRawBody(event);\n const crypto = await import(\"node:crypto\");\n\n // Svix signing secret may be prefixed with \"whsec_\"\n const rawSecret = secret.startsWith(\"whsec_\") ? secret.slice(6) : secret;\n const secretBytes = Buffer.from(rawSecret, \"base64\");\n\n const signedContent = `${svixId}.${svixTimestamp}.${body}`;\n const expectedSignature = crypto\n .createHmac(\"sha256\", secretBytes)\n .update(signedContent)\n .digest(\"base64\");\n\n // Svix sends multiple signatures separated by spaces, each prefixed with \"v1,\"\n const signatures = svixSignature.split(\" \");\n for (const sig of signatures) {\n const sigValue = sig.startsWith(\"v1,\") ? sig.slice(3) : sig;\n try {\n if (\n crypto.timingSafeEqual(\n Buffer.from(expectedSignature),\n Buffer.from(sigValue),\n )\n ) {\n return true;\n }\n } catch {\n // Length mismatch — try next signature\n }\n }\n\n console.warn(\"[email] Svix signature verification failed\");\n return false;\n}\n\nfunction safeEq(a: string, b: string): boolean {\n const aBuf = Buffer.from(a);\n const bBuf = Buffer.from(b);\n if (aBuf.length !== bBuf.length) return false;\n return timingSafeEqual(aBuf, bBuf);\n}\n\nasync function verifySendGridWebhook(\n event: H3Event,\n secret?: string,\n): Promise<boolean> {\n if (!secret) {\n if (shouldRefuseWhenSecretMissing()) {\n if (!_sendgridUnverifiedWarned) {\n _sendgridUnverifiedWarned = true;\n console.error(\n \"[email] EMAIL_INBOUND_WEBHOOK_SECRET not set — refusing SendGrid webhook in production. \" +\n \"Set EMAIL_INBOUND_WEBHOOK_SECRET, or set AGENT_NATIVE_ALLOW_UNVERIFIED_WEBHOOKS=1 for local testing only.\",\n );\n }\n return false;\n }\n if (!_sendgridUnverifiedWarned) {\n _sendgridUnverifiedWarned = true;\n console.warn(\n \"[email] EMAIL_INBOUND_WEBHOOK_SECRET not set — accepting SendGrid webhook without verification (dev mode)\",\n );\n }\n return true;\n }\n\n // Check for the secret in a custom header or basic auth\n const authHeader = getHeader(event, \"authorization\");\n if (authHeader) {\n // Basic auth: \"Basic base64(user:pass)\" — secret is the password\n if (authHeader.startsWith(\"Basic \")) {\n const decoded = Buffer.from(authHeader.slice(6), \"base64\").toString();\n const password = decoded.split(\":\")[1];\n if (password !== undefined && safeEq(password, secret)) return true;\n }\n }\n\n // Also check a custom header (common SendGrid Inbound Parse pattern)\n const customSecret = getHeader(event, \"x-webhook-secret\");\n if (customSecret !== undefined && safeEq(customSecret, secret)) return true;\n\n console.warn(\"[email] SendGrid webhook secret verification failed\");\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Inbound email parsing\n// ---------------------------------------------------------------------------\n\nasync function parseResendWebhook(event: H3Event): Promise<ParsedEmail | null> {\n const raw = await readRawBody(event);\n const body = JSON.parse(raw);\n if (!body || body.type !== \"email.received\") return null;\n\n const data = body.data;\n if (!data) return null;\n\n // Resend webhook payload provides email metadata directly in data\n // Fields: from, to, cc, subject, text, html, headers, created_at\n const fromRaw = data.from as string | undefined;\n const from = fromRaw ? parseEmailAddress(fromRaw) : null;\n if (!from) return null;\n\n const toRaw = data.to as string | string[] | undefined;\n const to = normalizeAddressList(toRaw);\n const ccRaw = data.cc as string | string[] | undefined;\n const cc = normalizeAddressList(ccRaw);\n\n // Parse headers for Message-ID, In-Reply-To, References\n const headers = parseHeadersObject(data.headers);\n const messageId =\n headers[\"message-id\"] || data.email_id || `resend-${Date.now()}`;\n\n // Resend forwards the raw `Authentication-Results` header (and may also\n // surface explicit `dkim`/`spf` fields). Derive a verified verdict from\n // whichever is present; absent results fail closed (unverified).\n const senderVerified = computeSenderVerified({\n fromEmail: from.email,\n authResults: headers[\"authentication-results\"],\n dkim: typeof data.dkim === \"string\" ? data.dkim : undefined,\n spf: typeof data.spf === \"string\" ? data.spf : undefined,\n });\n\n return {\n messageId,\n subject: (data.subject as string) || \"(no subject)\",\n from,\n to,\n cc: cc.length > 0 ? cc : undefined,\n text: data.text as string | undefined,\n html: data.html as string | undefined,\n inReplyTo: headers[\"in-reply-to\"] || undefined,\n references: parseReferencesHeader(headers[\"references\"]),\n date: (data.created_at as string) || undefined,\n senderVerified,\n };\n}\n\nasync function parseSendGridWebhook(\n event: H3Event,\n): Promise<ParsedEmail | null> {\n const raw = await readRawBody(event);\n const body = JSON.parse(raw);\n if (!body) return null;\n\n // SendGrid Inbound Parse sends form data with fields:\n // from, to, cc, subject, text, html, headers, envelope\n const fromRaw = body.from as string | undefined;\n const from = fromRaw ? parseEmailAddress(fromRaw) : null;\n if (!from) return null;\n\n const toRaw = body.to as string | undefined;\n const to = toRaw ? toRaw.split(\",\").map((a: string) => a.trim()) : [];\n const ccRaw = body.cc as string | undefined;\n const cc = ccRaw ? ccRaw.split(\",\").map((a: string) => a.trim()) : [];\n\n // Parse raw headers string\n const headersStr = body.headers as string | undefined;\n const headers = parseHeadersString(headersStr);\n const messageId = headers[\"message-id\"] || `sendgrid-${Date.now()}`;\n\n // SendGrid Inbound Parse posts explicit `dkim` (e.g. `{@example.com : pass}`)\n // and `SPF` (e.g. `pass`) form fields, and also carries\n // `Authentication-Results` inside the raw headers blob. Use all available\n // signals; absent results fail closed (unverified).\n const senderVerified = computeSenderVerified({\n fromEmail: from.email,\n authResults: headers[\"authentication-results\"],\n dkim: typeof body.dkim === \"string\" ? body.dkim : undefined,\n spf: typeof body.SPF === \"string\" ? body.SPF : undefined,\n });\n\n return {\n messageId,\n subject: (body.subject as string) || \"(no subject)\",\n from,\n to,\n cc: cc.length > 0 ? cc : undefined,\n text: body.text as string | undefined,\n html: body.html as string | undefined,\n inReplyTo: headers[\"in-reply-to\"] || undefined,\n references: parseReferencesHeader(headers[\"references\"]),\n date: headers[\"date\"] || undefined,\n senderVerified,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Helpers — sender authentication (DKIM / SPF)\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the registrable-ish domain from an email address (lowercased).\n * We keep the full host rather than collapsing to an eTLD+1 — exact-domain\n * alignment is the conservative choice here, and avoids bundling a public\n * suffix list. Subdomain senders that legitimately DKIM-sign with the parent\n * domain are handled by the suffix check in `domainsAlign`.\n */\nfunction emailDomain(email: string): string {\n const at = email.lastIndexOf(\"@\");\n return at >= 0\n ? email\n .slice(at + 1)\n .trim()\n .toLowerCase()\n : \"\";\n}\n\n/**\n * True when `signingDomain` is the From domain or a parent of it (e.g.\n * From `user@mail.example.com` aligned with a `d=example.com` signature).\n * Both directions of subdomain nesting are accepted because senders sign\n * with either the exact From host or the organizational parent.\n */\nfunction domainsAlign(fromDomain: string, signingDomain: string): boolean {\n if (!fromDomain || !signingDomain) return false;\n if (fromDomain === signingDomain) return true;\n return (\n fromDomain.endsWith(`.${signingDomain}`) ||\n signingDomain.endsWith(`.${fromDomain}`)\n );\n}\n\n/**\n * Compute whether an inbound email is authenticated as genuinely coming from\n * its `From:` domain. Returns true only when DKIM passes for an aligned\n * domain, or SPF passes for an aligned domain. Anything else — missing\n * results, `fail`, `softfail`, `none`, `neutral`, `temperror`, `permerror`\n * — returns false (fail closed).\n *\n * Inputs may come from provider-specific fields (`dkim`, `spf`) and/or the\n * RFC 8601 `Authentication-Results` header, in any combination. We treat the\n * union: if ANY source shows an aligned pass, the sender is verified.\n */\nfunction computeSenderVerified(input: {\n fromEmail: string;\n authResults?: string;\n dkim?: string;\n spf?: string;\n}): boolean {\n const fromDomain = emailDomain(input.fromEmail);\n if (!fromDomain) return false;\n\n // 1. Provider DKIM field, e.g. SendGrid `{@example.com : pass}` or\n // `{@example.com : pass; @other.com : fail}`.\n if (input.dkim) {\n const dkimEntries = input.dkim.matchAll(\n /@([a-z0-9.-]+)\\s*:\\s*(pass|fail|none|neutral|softfail|temperror|permerror)/gi,\n );\n for (const m of dkimEntries) {\n const domain = m[1].toLowerCase();\n const verdict = m[2].toLowerCase();\n if (verdict === \"pass\" && domainsAlign(fromDomain, domain)) return true;\n }\n }\n\n // 2. Provider SPF field. SendGrid posts a bare verdict (e.g. `pass`); since\n // SPF authenticates the envelope/MailFrom rather than the header From,\n // a bare `pass` with no domain only counts when we can't tell it's\n // misaligned. We accept a bare `pass` as an aligned SPF pass — this is\n // the same trust level Gmail-style routing assigns to a plain SPF pass.\n if (input.spf) {\n const spfVerdict = input.spf.trim().toLowerCase();\n if (spfVerdict === \"pass\") return true;\n }\n\n // 3. RFC 8601 `Authentication-Results` header (may list multiple methods).\n if (input.authResults) {\n const ar = input.authResults.toLowerCase();\n // DKIM with an aligned domain.\n const dkimRe = /dkim=pass[^;]*?(?:header\\.(?:d|i)=|@)([a-z0-9.-]+)/g;\n for (const m of ar.matchAll(dkimRe)) {\n const domain = m[1].replace(/^@/, \"\");\n if (domainsAlign(fromDomain, domain)) return true;\n }\n // SPF pass (envelope auth) — accept as an aligned pass.\n if (/spf=pass\\b/.test(ar)) return true;\n }\n\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Rate limiting\n// ---------------------------------------------------------------------------\n\n/**\n * Rate-limit heuristic backed by the `integration_pending_tasks` queue.\n *\n * Counts how many tasks this sender has produced in the last hour. The count\n * INCLUDES tasks already processed (status = completed/failed) because the\n * rows aren't deleted on completion — that's enough signal to throttle a\n * single noisy/abusive sender without needing a dedicated counter table.\n *\n * Two trade-offs worth knowing:\n * - This is a coarse heuristic, not exact metering. Within one hour the\n * count is correct; rows produced more than an hour ago naturally drop\n * off. We don't try to be precise, only to raise the bar past the\n * \"send 10K emails through one Lambda burst\" failure mode.\n * - The query relies on the `idx_pending_tasks_status_created` index plus\n * a sender substring match. A targeted attacker could amortise the cost\n * by reusing one sender address — that's fine, the goal here is to bound\n * the attack within a single attacker identity, not to detect spoofing.\n *\n * If the table doesn't yet exist on this deployment (no inbound webhook has\n * been processed before), we silently allow the message — the schema is\n * provisioned on first task insert. See H4 in the webhook security audit.\n */\nasync function isRateLimited(senderEmail: string): Promise<boolean> {\n const cutoff = Date.now() - RATE_LIMIT_WINDOW_MS;\n try {\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `\n SELECT COUNT(*) AS c\n FROM integration_pending_tasks\n WHERE platform = ?\n AND created_at >= ?\n AND payload LIKE ? ESCAPE '!'\n `,\n args: [\"email\", cutoff, `%\"senderId\":\"${escapeLike(senderEmail)}\"%`],\n });\n const count = Number(\n (rows[0] as Record<string, unknown> | undefined)?.c ?? 0,\n );\n return count >= RATE_LIMIT_MAX;\n } catch {\n // Table doesn't exist yet (first webhook on a fresh deployment) — allow.\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers — email address parsing\n// ---------------------------------------------------------------------------\n\n/** Parse \"Name <addr@example.com>\" or plain \"addr@example.com\" */\nfunction parseEmailAddress(raw: string): { name?: string; email: string } {\n const match = raw.match(/^\\s*(.*?)\\s*<([^>]+)>\\s*$/);\n if (match && match[2]) {\n return {\n name: match[1].replace(/^[\"']|[\"']$/g, \"\").trim() || undefined,\n email: match[2].trim(),\n };\n }\n return { email: raw.trim() };\n}\n\n/** Normalize a to/cc field that may be a string, array, or undefined into a string[] of addresses */\nfunction normalizeAddressList(raw: string | string[] | undefined): string[] {\n if (!raw) return [];\n if (Array.isArray(raw)) return raw.map((a) => a.trim());\n return raw.split(\",\").map((a) => a.trim());\n}\n\n// ---------------------------------------------------------------------------\n// Helpers — header parsing\n// ---------------------------------------------------------------------------\n\n/** Parse a headers object (Resend format: array of {name, value} or Record) */\nfunction parseHeadersObject(headers: unknown): Record<string, string> {\n const result: Record<string, string> = {};\n if (!headers) return result;\n\n if (Array.isArray(headers)) {\n for (const h of headers) {\n if (h && typeof h === \"object\" && \"name\" in h && \"value\" in h) {\n result[(h.name as string).toLowerCase()] = h.value as string;\n }\n }\n } else if (typeof headers === \"object\") {\n for (const [key, value] of Object.entries(\n headers as Record<string, unknown>,\n )) {\n result[key.toLowerCase()] = String(value);\n }\n }\n return result;\n}\n\n/** Parse a raw headers string (SendGrid format: \"Key: Value\\nKey: Value\\n...\") */\nfunction parseHeadersString(raw: string | undefined): Record<string, string> {\n const result: Record<string, string> = {};\n if (!raw) return result;\n\n const lines = raw.split(/\\r?\\n/);\n let currentKey = \"\";\n let currentValue = \"\";\n\n for (const line of lines) {\n // Continuation line (starts with whitespace)\n if (/^\\s/.test(line) && currentKey) {\n currentValue += \" \" + line.trim();\n continue;\n }\n // Save previous header\n if (currentKey) {\n result[currentKey.toLowerCase()] = currentValue;\n }\n const colonIdx = line.indexOf(\":\");\n if (colonIdx > 0) {\n currentKey = line.slice(0, colonIdx).trim();\n currentValue = line.slice(colonIdx + 1).trim();\n } else {\n currentKey = \"\";\n currentValue = \"\";\n }\n }\n // Save last header\n if (currentKey) {\n result[currentKey.toLowerCase()] = currentValue;\n }\n return result;\n}\n\n/** Parse a References header value into an array of Message-IDs */\nfunction parseReferencesHeader(\n references: string | undefined,\n): string[] | undefined {\n if (!references) return undefined;\n const ids = references.match(/<[^>]+>/g);\n return ids && ids.length > 0 ? ids : undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers — threading\n// ---------------------------------------------------------------------------\n\n/**\n * Get the thread root ID using a Gmail-style approach:\n * the oldest Message-ID from the References chain is the thread root.\n * If no References, use the current Message-ID.\n */\nfunction getThreadRootId(messageId: string, references?: string[]): string {\n if (references && references.length > 0) {\n return references[0];\n }\n return messageId;\n}\n\n/**\n * Scope a raw thread root id by the sender's email address. Two different\n * senders crafting the same `References:` header value should NOT collide\n * onto the same internal thread mapping — that's the email-side fix for the\n * thread-injection finding (M1 in the webhook security audit).\n *\n * The returned id is opaque to callers and stays stable across messages\n * from the same sender on the same conversation thread, so reply behaviour\n * is unchanged.\n */\nfunction scopeThreadIdToSender(\n rawThreadId: string,\n senderEmail: string,\n): string {\n return `${senderEmail.toLowerCase()}::${rawThreadId}`;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers — reply building\n// ---------------------------------------------------------------------------\n\n/** Build a References header from the platform context */\nfunction buildReferencesHeader(ctx: Record<string, unknown>): string {\n const parts: string[] = [];\n\n // Include existing references\n const refs = ctx.references as string[] | undefined;\n if (refs) {\n parts.push(...refs);\n }\n\n // Append the current message ID\n const messageId = ctx.messageId as string | undefined;\n if (messageId) {\n // Avoid duplicates\n if (!parts.includes(messageId)) {\n parts.push(messageId);\n }\n }\n\n return parts.join(\" \");\n}\n\n/**\n * Build CC list for reply-all when agent was CC'd.\n * Include original To addresses and other CC addresses, excluding the agent and the original sender.\n */\nfunction buildReplyAllCc(\n context: IncomingMessage,\n agentAddress: string,\n): string[] | undefined {\n const normalizedAgentAddress = agentAddress.toLowerCase();\n const senderEmail = context.senderId?.toLowerCase();\n const toAddresses = (context.platformContext.to as string[]) || [];\n const ccAddresses = (context.platformContext.cc as string[]) || [];\n\n const allRecipients = new Set<string>();\n for (const addr of [...toAddresses, ...ccAddresses]) {\n const normalized = addr.toLowerCase().trim();\n // Exclude agent address and original sender (sender goes in To)\n if (normalized !== normalizedAgentAddress && normalized !== senderEmail) {\n allRecipients.add(normalized);\n }\n }\n\n return allRecipients.size > 0 ? Array.from(allRecipients) : undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers — text conversion\n// ---------------------------------------------------------------------------\n\n/** Strip HTML tags for a plain-text version of the email */\nfunction stripHtmlForPlainText(html: string): string {\n return html\n .replace(/<br\\s*\\/?>/gi, \"\\n\")\n .replace(/<\\/p>/gi, \"\\n\\n\")\n .replace(/<\\/div>/gi, \"\\n\")\n .replace(/<\\/li>/gi, \"\\n\")\n .replace(/<li[^>]*>/gi, \"- \")\n .replace(/<\\/h[1-6]>/gi, \"\\n\\n\")\n .replace(/<[^>]+>/g, \"\")\n .replace(/&nbsp;/g, \" \")\n .replace(/&amp;/g, \"&\")\n .replace(/&lt;/g, \"<\")\n .replace(/&gt;/g, \">\")\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n}\n\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#39;\");\n}\n\nfunction decodeBasicHtmlEntities(s: string): string {\n return s\n .replace(/&amp;/g, \"&\")\n .replace(/&lt;/g, \"<\")\n .replace(/&gt;/g, \">\")\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\");\n}\n\nfunction splitTrailingUrlPunctuation(raw: string): {\n url: string;\n trailing: string;\n} {\n let url = raw;\n let trailing = \"\";\n const trailingEntities = [\"&quot;\", \"&#39;\"];\n\n for (;;) {\n const entity = trailingEntities.find((candidate) =>\n url.endsWith(candidate),\n );\n if (!entity) break;\n url = url.slice(0, -entity.length);\n trailing = entity + trailing;\n }\n\n while (/[.,!?;:]$/.test(url)) {\n trailing = url.slice(-1) + trailing;\n url = url.slice(0, -1);\n }\n\n while (url.endsWith(\")\") && !url.includes(\"(\")) {\n trailing = \")\" + trailing;\n url = url.slice(0, -1);\n }\n\n return { url, trailing };\n}\n\nfunction labelForUrl(rawUrl: string): string {\n try {\n const parsed = new URL(decodeBasicHtmlEntities(rawUrl));\n const host = parsed.hostname.replace(/^www\\./, \"\");\n return host ? `Open ${host}` : \"Open link\";\n } catch {\n return \"Open link\";\n }\n}\n\nfunction linkifyTextSegment(segment: string): string {\n return segment.replace(/\\bhttps?:\\/\\/[^\\s<>\"']+/gi, (raw) => {\n const { url, trailing } = splitTrailingUrlPunctuation(raw);\n const href = decodeBasicHtmlEntities(url);\n return `<a href=\"${escapeHtml(href)}\" style=\"color:#2563eb;text-decoration:underline;\">${escapeHtml(\n labelForUrl(url),\n )}</a>${trailing}`;\n });\n}\n\nfunction linkifyBareUrlsInHtml(html: string): string {\n const parts = html.split(/(<\\/?[^>]+>)/g);\n let skipDepth = 0;\n\n return parts\n .map((part) => {\n if (part.startsWith(\"<\") && part.endsWith(\">\")) {\n if (/^<\\/\\s*(a|code)\\b/i.test(part)) {\n skipDepth = Math.max(0, skipDepth - 1);\n } else if (/^<\\s*(a|code)\\b/i.test(part)) {\n skipDepth += 1;\n }\n return part;\n }\n return skipDepth > 0 ? part : linkifyTextSegment(part);\n })\n .join(\"\");\n}\n\n/** Convert basic markdown to HTML for email rendering */\nfunction markdownToHtml(md: string): string {\n let html = md;\n\n // Escape HTML entities in the source (but not our generated tags)\n html = escapeHtml(html);\n\n // Bold: **text** or __text__\n html = html.replace(/\\*\\*(.+?)\\*\\*/g, \"<strong>$1</strong>\");\n html = html.replace(/__(.+?)__/g, \"<strong>$1</strong>\");\n\n // Italic: *text* or _text_ (but not inside words with underscores)\n html = html.replace(/(?<!\\w)\\*([^*]+?)\\*(?!\\w)/g, \"<em>$1</em>\");\n html = html.replace(/(?<!\\w)_([^_]+?)_(?!\\w)/g, \"<em>$1</em>\");\n\n // Links: [text](url)\n html = html.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, (_match, label, url) => {\n const visibleLabel = /^https?:\\/\\//i.test(decodeBasicHtmlEntities(label))\n ? escapeHtml(labelForUrl(label))\n : label;\n return `<a href=\"${escapeHtml(\n decodeBasicHtmlEntities(url),\n )}\" style=\"color:#2563eb;text-decoration:underline;\">${visibleLabel}</a>`;\n });\n\n // Inline code: `code`\n html = html.replace(\n /`([^`]+)`/g,\n '<code style=\"background:#f1f5f9;padding:1px 4px;border-radius:3px;font-size:0.9em;\">$1</code>',\n );\n\n // Bare URLs: keep the destination in href but avoid spelling long URLs out.\n html = linkifyBareUrlsInHtml(html);\n\n // Unordered lists: lines starting with \"- \" or \"* \"\n html = html.replace(/^([*-]) (.+)$/gm, \"<li>$2</li>\");\n // Wrap consecutive <li> in <ul>\n html = html.replace(\n /(<li>.*?<\\/li>\\n?)+/g,\n '<ul style=\"margin:8px 0;padding-left:20px;\">$&</ul>',\n );\n\n // Ordered lists: lines starting with \"1. \", \"2. \" etc.\n html = html.replace(/^\\d+\\. (.+)$/gm, \"<li>$1</li>\");\n // Wrap consecutive <li> that aren't in <ul> in <ol>\n html = html.replace(/(?<!<\\/ul>)(<li>.*?<\\/li>\\n?)+/g, (match) => {\n if (match.includes(\"<ul\")) return match;\n return `<ol style=\"margin:8px 0;padding-left:20px;\">${match}</ol>`;\n });\n\n // Headings: # through ###\n html = html.replace(\n /^### (.+)$/gm,\n '<h3 style=\"margin:16px 0 8px;font-size:1.1em;\">$1</h3>',\n );\n html = html.replace(\n /^## (.+)$/gm,\n '<h2 style=\"margin:16px 0 8px;font-size:1.25em;\">$1</h2>',\n );\n html = html.replace(\n /^# (.+)$/gm,\n '<h1 style=\"margin:16px 0 8px;font-size:1.4em;\">$1</h1>',\n );\n\n // Horizontal rules: --- or ***\n html = html.replace(\n /^(-{3,}|\\*{3,})$/gm,\n '<hr style=\"border:none;border-top:1px solid #e2e8f0;margin:16px 0;\">',\n );\n\n // Paragraphs: double newlines\n html = html.replace(/\\n\\n/g, \"</p><p>\");\n // Single newlines → <br>\n html = html.replace(/\\n/g, \"<br>\");\n\n // Wrap in paragraph tags\n html = `<p>${html}</p>`;\n // Clean up empty paragraphs\n html = html.replace(/<p>\\s*<\\/p>/g, \"\");\n\n return html;\n}\n\n/** Wrap body HTML in a minimal email template with inline styles */\nfunction wrapInEmailTemplate(bodyHtml: string): string {\n return `<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n</head>\n<body style=\"margin:0;padding:0;background-color:#ffffff;\">\n<div style=\"max-width:600px;margin:0 auto;padding:20px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;font-size:14px;line-height:1.6;color:#1a1a1a;\">\n${bodyHtml}\n</div>\n</body>\n</html>`;\n}\n\n// ---------------------------------------------------------------------------\n// Raw body reader (matches Slack adapter pattern)\n// ---------------------------------------------------------------------------\n\n/**\n * Read the raw request body as a string and cache on the event context.\n * Reads raw bytes from the request stream — never re-stringifies a parsed\n * body, since the Resend / Svix HMAC is computed over the exact bytes sent\n * (M2 in the webhook security audit).\n */\nasync function readRawBody(event: H3Event): Promise<string> {\n const cached = event.context.__rawBody;\n if (typeof cached === \"string\") return cached;\n const raw = (await h3ReadRawBody(event)) ?? \"\";\n event.context.__rawBody = raw;\n return raw;\n}\n"]}
1
+ {"version":3,"file":"email.js","sourceRoot":"","sources":["../../../src/integrations/adapters/email.ts"],"names":[],"mappings":"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","sourcesContent":["import { timingSafeEqual } from \"node:crypto\";\n\nimport type { H3Event } from \"h3\";\nimport { getHeader, readRawBody as h3ReadRawBody } from \"h3\";\n\nimport { getDbExec } from \"../../db/client.js\";\nimport type { EnvKeyConfig } from \"../../server/create-server.js\";\nimport { resolveSecret } from \"../../server/credential-provider.js\";\nimport {\n sendEmail,\n isEmailConfigured,\n getEmailProvider,\n} from \"../../server/email.js\";\nimport { getIntegrationConfig } from \"../config-store.js\";\nimport type {\n PlatformAdapter,\n IncomingMessage,\n OutgoingMessage,\n IntegrationStatus,\n OutboundTarget,\n PlatformDeliveryReceipt,\n} from \"../types.js\";\n\n/** Max body length before truncation */\nconst EMAIL_MAX_BODY_LENGTH = 15000;\n\n/** Rate limit: max emails per sender within the window */\nconst RATE_LIMIT_MAX = 20;\n/** Rate limit window in ms (1 hour) */\nconst RATE_LIMIT_WINDOW_MS = 60 * 60 * 1000;\n\n/**\n * One-shot warning flags so we don't spam logs on every webhook.\n * Cleared per process — one warning per cold start is enough to surface\n * a misconfiguration without leaking config status to anyone with log access\n * (M6 in the webhook security audit).\n */\nlet _resendUnverifiedWarned = false;\nlet _sendgridUnverifiedWarned = false;\n\nfunction escapeLike(value: string): string {\n return value.replace(/[!%_]/g, (match) => `!${match}`);\n}\n\n/**\n * Returns true when the deployment is running in production mode and the\n * operator has NOT explicitly opted into accepting unverified webhooks for\n * local testing. In production we MUST refuse webhooks whose signature can't\n * be verified — accepting them with attacker-controlled `from:` addresses\n * lets the dispatch owner-resolution path run as the victim (C1 in the\n * webhook security audit).\n */\nfunction shouldRefuseWhenSecretMissing(): boolean {\n if (process.env.AGENT_NATIVE_ALLOW_UNVERIFIED_WEBHOOKS === \"1\") return false;\n return process.env.NODE_ENV === \"production\";\n}\n\n/**\n * Create an Email platform adapter for inbound/outbound email via\n * Resend or SendGrid webhooks.\n *\n * Required env vars:\n * - EMAIL_AGENT_ADDRESS — The email address the agent receives mail at\n *\n * One of these must also be set (checked via isEmailConfigured()):\n * - RESEND_API_KEY — For sending/receiving via Resend\n * - SENDGRID_API_KEY — For sending/receiving via SendGrid\n *\n * Optional:\n * - EMAIL_INBOUND_WEBHOOK_SECRET — Webhook signature verification secret\n */\nexport function emailAdapter(): PlatformAdapter {\n return {\n platform: \"email\",\n label: \"Email\",\n\n getRequiredEnvKeys(): EnvKeyConfig[] {\n return [\n {\n key: \"EMAIL_AGENT_ADDRESS\",\n label: \"Agent Email Address\",\n required: true,\n helpText:\n \"The email address people will use to message your agent (e.g. `agent@yourcompany.com`, or pick from your `<slug>.resend.app` sandbox).\",\n },\n {\n key: \"RESEND_API_KEY\",\n label: \"Resend API Key\",\n required: false,\n helpText:\n \"From resend.com → API keys (starts with `re_`). Either Resend or SendGrid is required for sending and receiving mail.\",\n },\n {\n key: \"SENDGRID_API_KEY\",\n label: \"SendGrid API Key\",\n required: false,\n helpText:\n \"From sendgrid.com → Settings → API Keys (starts with `SG.`). Either Resend or SendGrid is required.\",\n },\n {\n key: \"EMAIL_INBOUND_WEBHOOK_SECRET\",\n label: \"Inbound Webhook Secret\",\n required: false,\n helpText:\n \"Optional. From Resend (Webhooks → Signing Secret, starts with `whsec_`) or your SendGrid Inbound Parse basic-auth password. Used to verify inbound webhooks are real.\",\n },\n ];\n },\n\n async handleVerification(\n _event: H3Event,\n ): Promise<{ handled: boolean; response?: unknown }> {\n // Email webhooks don't need challenge handshakes\n return { handled: false };\n },\n\n async verifyWebhook(event: H3Event): Promise<boolean> {\n const secret =\n (await resolveSecret(\"EMAIL_INBOUND_WEBHOOK_SECRET\")) ?? undefined;\n const provider = await getEmailProvider();\n\n if (provider === \"resend\") {\n return verifyResendWebhook(event, secret);\n }\n\n if (provider === \"sendgrid\") {\n return verifySendGridWebhook(event, secret);\n }\n\n // No provider configured — reject\n console.warn(\"[email] No email provider configured, rejecting webhook\");\n return false;\n },\n\n async parseIncomingMessage(\n event: H3Event,\n ): Promise<IncomingMessage | null> {\n const provider = await getEmailProvider();\n const agentAddress = (\n await resolveSecret(\"EMAIL_AGENT_ADDRESS\")\n )?.toLowerCase();\n if (!agentAddress) {\n console.warn(\"[email] EMAIL_AGENT_ADDRESS not configured\");\n return null;\n }\n\n let parsed: ParsedEmail | null = null;\n\n if (provider === \"resend\") {\n parsed = await parseResendWebhook(event);\n } else if (provider === \"sendgrid\") {\n parsed = await parseSendGridWebhook(event);\n }\n\n if (!parsed) return null;\n\n // Rate limiting (SQL-backed heuristic — counts the sender's already-queued\n // tasks within the last hour). The previous in-memory map reset on every\n // serverless cold start, so the actual ceiling per attacker was\n // RATE_LIMIT_MAX × number_of_active_instances. SQL-backed counting holds\n // across instances. See H4 in the webhook security audit.\n const senderEmail = parsed.from.email.toLowerCase();\n if (await isRateLimited(senderEmail)) {\n console.warn(\n `[email] Rate limited sender: ${senderEmail} (>${RATE_LIMIT_MAX}/hr)`,\n );\n return null;\n }\n\n // Check allowed domains\n const config = await getIntegrationConfig(\"email\");\n if (config?.configData?.allowedDomains) {\n const allowed = config.configData.allowedDomains as string[];\n if (allowed.length > 0) {\n const senderDomain = senderEmail.split(\"@\")[1];\n if (!senderDomain || !allowed.includes(senderDomain)) {\n console.warn(\n `[email] Rejected email from ${senderEmail}: domain not in allowedDomains`,\n );\n return null;\n }\n }\n }\n\n // Determine if agent was CC'd (not in To, but in CC)\n const toAddresses = parsed.to.map((a) => a.toLowerCase());\n const ccAddresses = (parsed.cc ?? []).map((a) => a.toLowerCase());\n const isCC =\n !toAddresses.includes(agentAddress) &&\n ccAddresses.includes(agentAddress);\n\n // Build thread ID from References chain (Gmail-style: oldest Message-ID is thread root).\n // Scope the thread root by sender so an attacker who can forge a `References:`\n // header pointing at someone else's thread root can't graft into that thread.\n // Without this scoping, a third party could craft an inbound email whose\n // References chain matches a known victim's Message-ID and inject messages into\n // the victim's existing conversation — leaking prior content via the agent's\n // reply (M1 in the webhooks security audit).\n const threadRootId = scopeThreadIdToSender(\n getThreadRootId(parsed.messageId, parsed.references),\n senderEmail,\n );\n\n // Build body text\n let bodyText = parsed.text || stripHtmlForPlainText(parsed.html || \"\");\n\n // Truncate if needed\n if (bodyText.length > EMAIL_MAX_BODY_LENGTH) {\n bodyText =\n bodyText.slice(0, EMAIL_MAX_BODY_LENGTH) + \"\\n[Message truncated]\";\n }\n\n // Prefix CC'd emails with context\n if (isCC) {\n const otherRecipients = toAddresses\n .filter((a) => a !== agentAddress)\n .join(\", \");\n bodyText =\n `[CC'd on email between ${senderEmail} and ${otherRecipients || \"others\"}]\\n` +\n `Subject: ${parsed.subject}\\n\\n` +\n bodyText;\n }\n\n return {\n platform: \"email\",\n externalThreadId: threadRootId,\n text: bodyText,\n senderName: parsed.from.name,\n senderId: senderEmail,\n // Carry the message-authentication verdict downstream. Owner\n // resolution (dispatch) must NOT grant a real user's identity /\n // credentials unless the sender is verified — an unverified or\n // spoofed `From:` falls back to a synthetic, credential-less owner.\n senderVerified: parsed.senderVerified,\n platformContext: {\n messageId: parsed.messageId,\n subject: parsed.subject,\n from: senderEmail,\n to: parsed.to,\n cc: parsed.cc,\n inReplyTo: parsed.inReplyTo,\n references: parsed.references,\n isCC,\n senderVerified: parsed.senderVerified,\n },\n timestamp: parsed.date ? new Date(parsed.date).getTime() : Date.now(),\n };\n },\n\n async sendResponse(\n message: OutgoingMessage,\n context: IncomingMessage,\n ): Promise<void | PlatformDeliveryReceipt> {\n const agentAddress = await resolveSecret(\"EMAIL_AGENT_ADDRESS\");\n if (!agentAddress) {\n console.error(\"[email] EMAIL_AGENT_ADDRESS not configured\");\n return;\n }\n\n const config = await getIntegrationConfig(\"email\");\n const displayName =\n (config?.configData?.displayName as string) || \"Dispatch Agent\";\n\n // EMAIL_FROM overrides the from-address — required when the receiving\n // address is on a sub-domain that can't be a verified sender (e.g.\n // *.resend.app). Inbound and outbound addresses can differ.\n const emailFrom = await resolveSecret(\"EMAIL_FROM\");\n const fromAddress = emailFrom || `${displayName} <${agentAddress}>`;\n\n const subject = context.platformContext.subject as string;\n const reSubject = subject.startsWith(\"Re: \") ? subject : `Re: ${subject}`;\n\n try {\n await sendEmail({\n to: context.senderId!,\n from: fromAddress,\n subject: reSubject,\n html: message.text,\n text: stripHtmlForPlainText(message.text),\n inReplyTo: context.platformContext.messageId as string,\n references: buildReferencesHeader(context.platformContext),\n cc: context.platformContext.isCC\n ? buildReplyAllCc(context, agentAddress)\n : undefined,\n });\n } catch (err) {\n console.error(\"[email] Failed to send response:\", err);\n throw err;\n }\n return { status: \"delivered\" };\n },\n\n async sendMessageToTarget(\n message: OutgoingMessage,\n target: OutboundTarget,\n ): Promise<void> {\n const agentAddress = await resolveSecret(\"EMAIL_AGENT_ADDRESS\");\n if (!agentAddress) {\n console.error(\"[email] EMAIL_AGENT_ADDRESS not configured\");\n return;\n }\n\n const config = await getIntegrationConfig(\"email\");\n const displayName =\n (config?.configData?.displayName as string) || \"Dispatch Agent\";\n\n try {\n await sendEmail({\n to: target.destination,\n from: `${displayName} <${agentAddress}>`,\n subject: target.label || \"Message from Dispatch Agent\",\n html: message.text,\n text: stripHtmlForPlainText(message.text),\n ...(target.threadRef\n ? {\n inReplyTo: target.threadRef,\n references: target.threadRef,\n }\n : {}),\n });\n } catch (err) {\n console.error(\"[email] Failed to send proactive message:\", err);\n throw err;\n }\n },\n\n formatAgentResponse(text: string): OutgoingMessage {\n const bodyHtml = markdownToHtml(text);\n const html = wrapInEmailTemplate(bodyHtml);\n return { text: html, platformContext: {} };\n },\n\n async getStatus(_baseUrl?: string): Promise<IntegrationStatus> {\n const hasAgentAddress = !!(await resolveSecret(\"EMAIL_AGENT_ADDRESS\"));\n const hasEmailProvider = await isEmailConfigured();\n const hasWebhookSecret = !!(await resolveSecret(\n \"EMAIL_INBOUND_WEBHOOK_SECRET\",\n ));\n const configured = hasAgentAddress && hasEmailProvider;\n\n return {\n platform: \"email\",\n label: \"Email\",\n enabled: false, // overridden by plugin\n configured,\n details: {\n hasAgentAddress,\n hasEmailProvider,\n hasWebhookSecret,\n provider: await getEmailProvider(),\n },\n error: !configured\n ? \"Save EMAIL_AGENT_ADDRESS and either RESEND_API_KEY or SENDGRID_API_KEY in settings\"\n : undefined,\n };\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Parsed email shape\n// ---------------------------------------------------------------------------\n\ninterface ParsedEmail {\n messageId: string;\n subject: string;\n from: { name?: string; email: string };\n to: string[];\n cc?: string[];\n text?: string;\n html?: string;\n inReplyTo?: string;\n references?: string[];\n date?: string;\n /**\n * True when the provider's message-authentication results show that the\n * mail genuinely originated from the From domain: DKIM `pass` aligned with\n * the From domain, or an aligned SPF `pass`. False when results are absent\n * or fail — we fail closed so a spoofed `From:` can never be treated as\n * verified. See FINDING 3 (inbound-email impersonation) in the webhook\n * security audit.\n */\n senderVerified: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Webhook verification\n// ---------------------------------------------------------------------------\n\nasync function verifyResendWebhook(\n event: H3Event,\n secret?: string,\n): Promise<boolean> {\n if (!secret) {\n if (shouldRefuseWhenSecretMissing()) {\n if (!_resendUnverifiedWarned) {\n _resendUnverifiedWarned = true;\n console.error(\n \"[email] EMAIL_INBOUND_WEBHOOK_SECRET not set — refusing Resend webhook in production. \" +\n \"Set EMAIL_INBOUND_WEBHOOK_SECRET, or set AGENT_NATIVE_ALLOW_UNVERIFIED_WEBHOOKS=1 for local testing only.\",\n );\n }\n return false;\n }\n if (!_resendUnverifiedWarned) {\n _resendUnverifiedWarned = true;\n console.warn(\n \"[email] EMAIL_INBOUND_WEBHOOK_SECRET not set — accepting Resend webhook without verification (dev mode)\",\n );\n }\n return true;\n }\n\n const svixId = getHeader(event, \"svix-id\");\n const svixTimestamp = getHeader(event, \"svix-timestamp\");\n const svixSignature = getHeader(event, \"svix-signature\");\n\n if (!svixId || !svixTimestamp || !svixSignature) {\n console.warn(\"[email] Missing Svix signature headers\");\n return false;\n }\n\n // Reject requests older than 5 minutes (replay protection)\n const ts = parseInt(svixTimestamp, 10);\n if (Math.abs(Date.now() / 1000 - ts) > 300) {\n console.warn(\"[email] Svix timestamp too old, rejecting\");\n return false;\n }\n\n const body = await readRawBody(event);\n const crypto = await import(\"node:crypto\");\n\n // Svix signing secret may be prefixed with \"whsec_\"\n const rawSecret = secret.startsWith(\"whsec_\") ? secret.slice(6) : secret;\n const secretBytes = Buffer.from(rawSecret, \"base64\");\n\n const signedContent = `${svixId}.${svixTimestamp}.${body}`;\n const expectedSignature = crypto\n .createHmac(\"sha256\", secretBytes)\n .update(signedContent)\n .digest(\"base64\");\n\n // Svix sends multiple signatures separated by spaces, each prefixed with \"v1,\"\n const signatures = svixSignature.split(\" \");\n for (const sig of signatures) {\n const sigValue = sig.startsWith(\"v1,\") ? sig.slice(3) : sig;\n try {\n if (\n crypto.timingSafeEqual(\n Buffer.from(expectedSignature),\n Buffer.from(sigValue),\n )\n ) {\n return true;\n }\n } catch {\n // Length mismatch — try next signature\n }\n }\n\n console.warn(\"[email] Svix signature verification failed\");\n return false;\n}\n\nfunction safeEq(a: string, b: string): boolean {\n const aBuf = Buffer.from(a);\n const bBuf = Buffer.from(b);\n if (aBuf.length !== bBuf.length) return false;\n return timingSafeEqual(aBuf, bBuf);\n}\n\nasync function verifySendGridWebhook(\n event: H3Event,\n secret?: string,\n): Promise<boolean> {\n if (!secret) {\n if (shouldRefuseWhenSecretMissing()) {\n if (!_sendgridUnverifiedWarned) {\n _sendgridUnverifiedWarned = true;\n console.error(\n \"[email] EMAIL_INBOUND_WEBHOOK_SECRET not set — refusing SendGrid webhook in production. \" +\n \"Set EMAIL_INBOUND_WEBHOOK_SECRET, or set AGENT_NATIVE_ALLOW_UNVERIFIED_WEBHOOKS=1 for local testing only.\",\n );\n }\n return false;\n }\n if (!_sendgridUnverifiedWarned) {\n _sendgridUnverifiedWarned = true;\n console.warn(\n \"[email] EMAIL_INBOUND_WEBHOOK_SECRET not set — accepting SendGrid webhook without verification (dev mode)\",\n );\n }\n return true;\n }\n\n // Check for the secret in a custom header or basic auth\n const authHeader = getHeader(event, \"authorization\");\n if (authHeader) {\n // Basic auth: \"Basic base64(user:pass)\" — secret is the password\n if (authHeader.startsWith(\"Basic \")) {\n const decoded = Buffer.from(authHeader.slice(6), \"base64\").toString();\n const password = decoded.split(\":\")[1];\n if (password !== undefined && safeEq(password, secret)) return true;\n }\n }\n\n // Also check a custom header (common SendGrid Inbound Parse pattern)\n const customSecret = getHeader(event, \"x-webhook-secret\");\n if (customSecret !== undefined && safeEq(customSecret, secret)) return true;\n\n console.warn(\"[email] SendGrid webhook secret verification failed\");\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Inbound email parsing\n// ---------------------------------------------------------------------------\n\nasync function parseResendWebhook(event: H3Event): Promise<ParsedEmail | null> {\n const raw = await readRawBody(event);\n const body = JSON.parse(raw);\n if (!body || body.type !== \"email.received\") return null;\n\n const data = body.data;\n if (!data) return null;\n\n // Resend webhook payload provides email metadata directly in data\n // Fields: from, to, cc, subject, text, html, headers, created_at\n const fromRaw = data.from as string | undefined;\n const from = fromRaw ? parseEmailAddress(fromRaw) : null;\n if (!from) return null;\n\n const toRaw = data.to as string | string[] | undefined;\n const to = normalizeAddressList(toRaw);\n const ccRaw = data.cc as string | string[] | undefined;\n const cc = normalizeAddressList(ccRaw);\n\n // Parse headers for Message-ID, In-Reply-To, References\n const headers = parseHeadersObject(data.headers);\n const messageId =\n headers[\"message-id\"] || data.email_id || `resend-${Date.now()}`;\n\n // Resend forwards the raw `Authentication-Results` header (and may also\n // surface explicit `dkim`/`spf` fields). Derive a verified verdict from\n // whichever is present; absent results fail closed (unverified).\n const senderVerified = computeSenderVerified({\n fromEmail: from.email,\n authResults: headers[\"authentication-results\"],\n dkim: typeof data.dkim === \"string\" ? data.dkim : undefined,\n spf: typeof data.spf === \"string\" ? data.spf : undefined,\n });\n\n return {\n messageId,\n subject: (data.subject as string) || \"(no subject)\",\n from,\n to,\n cc: cc.length > 0 ? cc : undefined,\n text: data.text as string | undefined,\n html: data.html as string | undefined,\n inReplyTo: headers[\"in-reply-to\"] || undefined,\n references: parseReferencesHeader(headers[\"references\"]),\n date: (data.created_at as string) || undefined,\n senderVerified,\n };\n}\n\nasync function parseSendGridWebhook(\n event: H3Event,\n): Promise<ParsedEmail | null> {\n const raw = await readRawBody(event);\n const body = JSON.parse(raw);\n if (!body) return null;\n\n // SendGrid Inbound Parse sends form data with fields:\n // from, to, cc, subject, text, html, headers, envelope\n const fromRaw = body.from as string | undefined;\n const from = fromRaw ? parseEmailAddress(fromRaw) : null;\n if (!from) return null;\n\n const toRaw = body.to as string | undefined;\n const to = toRaw ? toRaw.split(\",\").map((a: string) => a.trim()) : [];\n const ccRaw = body.cc as string | undefined;\n const cc = ccRaw ? ccRaw.split(\",\").map((a: string) => a.trim()) : [];\n\n // Parse raw headers string\n const headersStr = body.headers as string | undefined;\n const headers = parseHeadersString(headersStr);\n const messageId = headers[\"message-id\"] || `sendgrid-${Date.now()}`;\n\n // SendGrid Inbound Parse posts explicit `dkim` (e.g. `{@example.com : pass}`)\n // and `SPF` (e.g. `pass`) form fields, and also carries\n // `Authentication-Results` inside the raw headers blob. Use all available\n // signals; absent results fail closed (unverified).\n const senderVerified = computeSenderVerified({\n fromEmail: from.email,\n authResults: headers[\"authentication-results\"],\n dkim: typeof body.dkim === \"string\" ? body.dkim : undefined,\n spf: typeof body.SPF === \"string\" ? body.SPF : undefined,\n });\n\n return {\n messageId,\n subject: (body.subject as string) || \"(no subject)\",\n from,\n to,\n cc: cc.length > 0 ? cc : undefined,\n text: body.text as string | undefined,\n html: body.html as string | undefined,\n inReplyTo: headers[\"in-reply-to\"] || undefined,\n references: parseReferencesHeader(headers[\"references\"]),\n date: headers[\"date\"] || undefined,\n senderVerified,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Helpers — sender authentication (DKIM / SPF)\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the registrable-ish domain from an email address (lowercased).\n * We keep the full host rather than collapsing to an eTLD+1 — exact-domain\n * alignment is the conservative choice here, and avoids bundling a public\n * suffix list. Subdomain senders that legitimately DKIM-sign with the parent\n * domain are handled by the suffix check in `domainsAlign`.\n */\nfunction emailDomain(email: string): string {\n const at = email.lastIndexOf(\"@\");\n return at >= 0\n ? email\n .slice(at + 1)\n .trim()\n .toLowerCase()\n : \"\";\n}\n\n/**\n * True when `signingDomain` is the From domain or a parent of it (e.g.\n * From `user@mail.example.com` aligned with a `d=example.com` signature).\n * Both directions of subdomain nesting are accepted because senders sign\n * with either the exact From host or the organizational parent.\n */\nfunction domainsAlign(fromDomain: string, signingDomain: string): boolean {\n if (!fromDomain || !signingDomain) return false;\n if (fromDomain === signingDomain) return true;\n return (\n fromDomain.endsWith(`.${signingDomain}`) ||\n signingDomain.endsWith(`.${fromDomain}`)\n );\n}\n\n/**\n * Compute whether an inbound email is authenticated as genuinely coming from\n * its `From:` domain. Returns true only when DKIM passes for an aligned\n * domain, or SPF passes for an aligned domain. Anything else — missing\n * results, `fail`, `softfail`, `none`, `neutral`, `temperror`, `permerror`\n * — returns false (fail closed).\n *\n * Inputs may come from provider-specific fields (`dkim`, `spf`) and/or the\n * RFC 8601 `Authentication-Results` header, in any combination. We treat the\n * union: if ANY source shows an aligned pass, the sender is verified.\n */\nfunction computeSenderVerified(input: {\n fromEmail: string;\n authResults?: string;\n dkim?: string;\n spf?: string;\n}): boolean {\n const fromDomain = emailDomain(input.fromEmail);\n if (!fromDomain) return false;\n\n // 1. Provider DKIM field, e.g. SendGrid `{@example.com : pass}` or\n // `{@example.com : pass; @other.com : fail}`.\n if (input.dkim) {\n const dkimEntries = input.dkim.matchAll(\n /@([a-z0-9.-]+)\\s*:\\s*(pass|fail|none|neutral|softfail|temperror|permerror)/gi,\n );\n for (const m of dkimEntries) {\n const domain = m[1].toLowerCase();\n const verdict = m[2].toLowerCase();\n if (verdict === \"pass\" && domainsAlign(fromDomain, domain)) return true;\n }\n }\n\n // 2. Provider SPF field. SendGrid posts a bare verdict (e.g. `pass`); since\n // SPF authenticates the envelope/MailFrom rather than the header From,\n // a bare `pass` with no domain only counts when we can't tell it's\n // misaligned. We accept a bare `pass` as an aligned SPF pass — this is\n // the same trust level Gmail-style routing assigns to a plain SPF pass.\n if (input.spf) {\n const spfVerdict = input.spf.trim().toLowerCase();\n if (spfVerdict === \"pass\") return true;\n }\n\n // 3. RFC 8601 `Authentication-Results` header (may list multiple methods).\n if (input.authResults) {\n const ar = input.authResults.toLowerCase();\n // DKIM with an aligned domain.\n const dkimRe = /dkim=pass[^;]*?(?:header\\.(?:d|i)=|@)([a-z0-9.-]+)/g;\n for (const m of ar.matchAll(dkimRe)) {\n const domain = m[1].replace(/^@/, \"\");\n if (domainsAlign(fromDomain, domain)) return true;\n }\n // SPF pass (envelope auth) — accept as an aligned pass.\n if (/spf=pass\\b/.test(ar)) return true;\n }\n\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Rate limiting\n// ---------------------------------------------------------------------------\n\n/**\n * Rate-limit heuristic backed by the `integration_pending_tasks` queue.\n *\n * Counts how many tasks this sender has produced in the last hour. The count\n * INCLUDES tasks already processed (status = completed/failed) because the\n * rows aren't deleted on completion — that's enough signal to throttle a\n * single noisy/abusive sender without needing a dedicated counter table.\n *\n * Two trade-offs worth knowing:\n * - This is a coarse heuristic, not exact metering. Within one hour the\n * count is correct; rows produced more than an hour ago naturally drop\n * off. We don't try to be precise, only to raise the bar past the\n * \"send 10K emails through one Lambda burst\" failure mode.\n * - The query relies on the `idx_pending_tasks_status_created` index plus\n * a sender substring match. A targeted attacker could amortise the cost\n * by reusing one sender address — that's fine, the goal here is to bound\n * the attack within a single attacker identity, not to detect spoofing.\n *\n * If the table doesn't yet exist on this deployment (no inbound webhook has\n * been processed before), we silently allow the message — the schema is\n * provisioned on first task insert. See H4 in the webhook security audit.\n */\nasync function isRateLimited(senderEmail: string): Promise<boolean> {\n const cutoff = Date.now() - RATE_LIMIT_WINDOW_MS;\n try {\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `\n SELECT COUNT(*) AS c\n FROM integration_pending_tasks\n WHERE platform = ?\n AND created_at >= ?\n AND payload LIKE ? ESCAPE '!'\n `,\n args: [\"email\", cutoff, `%\"senderId\":\"${escapeLike(senderEmail)}\"%`],\n });\n const count = Number(\n (rows[0] as Record<string, unknown> | undefined)?.c ?? 0,\n );\n return count >= RATE_LIMIT_MAX;\n } catch {\n // Table doesn't exist yet (first webhook on a fresh deployment) — allow.\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers — email address parsing\n// ---------------------------------------------------------------------------\n\n/** Parse \"Name <addr@example.com>\" or plain \"addr@example.com\" */\nfunction parseEmailAddress(raw: string): { name?: string; email: string } {\n const match = raw.match(/^\\s*(.*?)\\s*<([^>]+)>\\s*$/);\n if (match && match[2]) {\n return {\n name: match[1].replace(/^[\"']|[\"']$/g, \"\").trim() || undefined,\n email: match[2].trim(),\n };\n }\n return { email: raw.trim() };\n}\n\n/** Normalize a to/cc field that may be a string, array, or undefined into a string[] of addresses */\nfunction normalizeAddressList(raw: string | string[] | undefined): string[] {\n if (!raw) return [];\n if (Array.isArray(raw)) return raw.map((a) => a.trim());\n return raw.split(\",\").map((a) => a.trim());\n}\n\n// ---------------------------------------------------------------------------\n// Helpers — header parsing\n// ---------------------------------------------------------------------------\n\n/** Parse a headers object (Resend format: array of {name, value} or Record) */\nfunction parseHeadersObject(headers: unknown): Record<string, string> {\n const result: Record<string, string> = {};\n if (!headers) return result;\n\n if (Array.isArray(headers)) {\n for (const h of headers) {\n if (h && typeof h === \"object\" && \"name\" in h && \"value\" in h) {\n result[(h.name as string).toLowerCase()] = h.value as string;\n }\n }\n } else if (typeof headers === \"object\") {\n for (const [key, value] of Object.entries(\n headers as Record<string, unknown>,\n )) {\n result[key.toLowerCase()] = String(value);\n }\n }\n return result;\n}\n\n/** Parse a raw headers string (SendGrid format: \"Key: Value\\nKey: Value\\n...\") */\nfunction parseHeadersString(raw: string | undefined): Record<string, string> {\n const result: Record<string, string> = {};\n if (!raw) return result;\n\n const lines = raw.split(/\\r?\\n/);\n let currentKey = \"\";\n let currentValue = \"\";\n\n for (const line of lines) {\n // Continuation line (starts with whitespace)\n if (/^\\s/.test(line) && currentKey) {\n currentValue += \" \" + line.trim();\n continue;\n }\n // Save previous header\n if (currentKey) {\n result[currentKey.toLowerCase()] = currentValue;\n }\n const colonIdx = line.indexOf(\":\");\n if (colonIdx > 0) {\n currentKey = line.slice(0, colonIdx).trim();\n currentValue = line.slice(colonIdx + 1).trim();\n } else {\n currentKey = \"\";\n currentValue = \"\";\n }\n }\n // Save last header\n if (currentKey) {\n result[currentKey.toLowerCase()] = currentValue;\n }\n return result;\n}\n\n/** Parse a References header value into an array of Message-IDs */\nfunction parseReferencesHeader(\n references: string | undefined,\n): string[] | undefined {\n if (!references) return undefined;\n const ids = references.match(/<[^>]+>/g);\n return ids && ids.length > 0 ? ids : undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers — threading\n// ---------------------------------------------------------------------------\n\n/**\n * Get the thread root ID using a Gmail-style approach:\n * the oldest Message-ID from the References chain is the thread root.\n * If no References, use the current Message-ID.\n */\nfunction getThreadRootId(messageId: string, references?: string[]): string {\n if (references && references.length > 0) {\n return references[0];\n }\n return messageId;\n}\n\n/**\n * Scope a raw thread root id by the sender's email address. Two different\n * senders crafting the same `References:` header value should NOT collide\n * onto the same internal thread mapping — that's the email-side fix for the\n * thread-injection finding (M1 in the webhook security audit).\n *\n * The returned id is opaque to callers and stays stable across messages\n * from the same sender on the same conversation thread, so reply behaviour\n * is unchanged.\n */\nfunction scopeThreadIdToSender(\n rawThreadId: string,\n senderEmail: string,\n): string {\n return `${senderEmail.toLowerCase()}::${rawThreadId}`;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers — reply building\n// ---------------------------------------------------------------------------\n\n/** Build a References header from the platform context */\nfunction buildReferencesHeader(ctx: Record<string, unknown>): string {\n const parts: string[] = [];\n\n // Include existing references\n const refs = ctx.references as string[] | undefined;\n if (refs) {\n parts.push(...refs);\n }\n\n // Append the current message ID\n const messageId = ctx.messageId as string | undefined;\n if (messageId) {\n // Avoid duplicates\n if (!parts.includes(messageId)) {\n parts.push(messageId);\n }\n }\n\n return parts.join(\" \");\n}\n\n/**\n * Build CC list for reply-all when agent was CC'd.\n * Include original To addresses and other CC addresses, excluding the agent and the original sender.\n */\nfunction buildReplyAllCc(\n context: IncomingMessage,\n agentAddress: string,\n): string[] | undefined {\n const normalizedAgentAddress = agentAddress.toLowerCase();\n const senderEmail = context.senderId?.toLowerCase();\n const toAddresses = (context.platformContext.to as string[]) || [];\n const ccAddresses = (context.platformContext.cc as string[]) || [];\n\n const allRecipients = new Set<string>();\n for (const addr of [...toAddresses, ...ccAddresses]) {\n const normalized = addr.toLowerCase().trim();\n // Exclude agent address and original sender (sender goes in To)\n if (normalized !== normalizedAgentAddress && normalized !== senderEmail) {\n allRecipients.add(normalized);\n }\n }\n\n return allRecipients.size > 0 ? Array.from(allRecipients) : undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers — text conversion\n// ---------------------------------------------------------------------------\n\n/** Strip HTML tags for a plain-text version of the email */\nfunction stripHtmlForPlainText(html: string): string {\n return html\n .replace(/<br\\s*\\/?>/gi, \"\\n\")\n .replace(/<\\/p>/gi, \"\\n\\n\")\n .replace(/<\\/div>/gi, \"\\n\")\n .replace(/<\\/li>/gi, \"\\n\")\n .replace(/<li[^>]*>/gi, \"- \")\n .replace(/<\\/h[1-6]>/gi, \"\\n\\n\")\n .replace(/<[^>]+>/g, \"\")\n .replace(/&nbsp;/g, \" \")\n .replace(/&amp;/g, \"&\")\n .replace(/&lt;/g, \"<\")\n .replace(/&gt;/g, \">\")\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n}\n\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#39;\");\n}\n\nfunction decodeBasicHtmlEntities(s: string): string {\n return s\n .replace(/&amp;/g, \"&\")\n .replace(/&lt;/g, \"<\")\n .replace(/&gt;/g, \">\")\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\");\n}\n\nfunction splitTrailingUrlPunctuation(raw: string): {\n url: string;\n trailing: string;\n} {\n let url = raw;\n let trailing = \"\";\n const trailingEntities = [\"&quot;\", \"&#39;\"];\n\n for (;;) {\n const entity = trailingEntities.find((candidate) =>\n url.endsWith(candidate),\n );\n if (!entity) break;\n url = url.slice(0, -entity.length);\n trailing = entity + trailing;\n }\n\n while (/[.,!?;:]$/.test(url)) {\n trailing = url.slice(-1) + trailing;\n url = url.slice(0, -1);\n }\n\n while (url.endsWith(\")\") && !url.includes(\"(\")) {\n trailing = \")\" + trailing;\n url = url.slice(0, -1);\n }\n\n return { url, trailing };\n}\n\nfunction labelForUrl(rawUrl: string): string {\n try {\n const parsed = new URL(decodeBasicHtmlEntities(rawUrl));\n const host = parsed.hostname.replace(/^www\\./, \"\");\n return host ? `Open ${host}` : \"Open link\";\n } catch {\n return \"Open link\";\n }\n}\n\nfunction linkifyTextSegment(segment: string): string {\n return segment.replace(/\\bhttps?:\\/\\/[^\\s<>\"']+/gi, (raw) => {\n const { url, trailing } = splitTrailingUrlPunctuation(raw);\n const href = decodeBasicHtmlEntities(url);\n return `<a href=\"${escapeHtml(href)}\" style=\"color:#2563eb;text-decoration:underline;\">${escapeHtml(\n labelForUrl(url),\n )}</a>${trailing}`;\n });\n}\n\nfunction linkifyBareUrlsInHtml(html: string): string {\n const parts = html.split(/(<\\/?[^>]+>)/g);\n let skipDepth = 0;\n\n return parts\n .map((part) => {\n if (part.startsWith(\"<\") && part.endsWith(\">\")) {\n if (/^<\\/\\s*(a|code)\\b/i.test(part)) {\n skipDepth = Math.max(0, skipDepth - 1);\n } else if (/^<\\s*(a|code)\\b/i.test(part)) {\n skipDepth += 1;\n }\n return part;\n }\n return skipDepth > 0 ? part : linkifyTextSegment(part);\n })\n .join(\"\");\n}\n\n/** Convert basic markdown to HTML for email rendering */\nfunction markdownToHtml(md: string): string {\n let html = md;\n\n // Escape HTML entities in the source (but not our generated tags)\n html = escapeHtml(html);\n\n // Bold: **text** or __text__\n html = html.replace(/\\*\\*(.+?)\\*\\*/g, \"<strong>$1</strong>\");\n html = html.replace(/__(.+?)__/g, \"<strong>$1</strong>\");\n\n // Italic: *text* or _text_ (but not inside words with underscores)\n html = html.replace(/(?<!\\w)\\*([^*]+?)\\*(?!\\w)/g, \"<em>$1</em>\");\n html = html.replace(/(?<!\\w)_([^_]+?)_(?!\\w)/g, \"<em>$1</em>\");\n\n // Links: [text](url)\n html = html.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, (_match, label, url) => {\n const visibleLabel = /^https?:\\/\\//i.test(decodeBasicHtmlEntities(label))\n ? escapeHtml(labelForUrl(label))\n : label;\n return `<a href=\"${escapeHtml(\n decodeBasicHtmlEntities(url),\n )}\" style=\"color:#2563eb;text-decoration:underline;\">${visibleLabel}</a>`;\n });\n\n // Inline code: `code`\n html = html.replace(\n /`([^`]+)`/g,\n '<code style=\"background:#f1f5f9;padding:1px 4px;border-radius:3px;font-size:0.9em;\">$1</code>',\n );\n\n // Bare URLs: keep the destination in href but avoid spelling long URLs out.\n html = linkifyBareUrlsInHtml(html);\n\n // Unordered lists: lines starting with \"- \" or \"* \"\n html = html.replace(/^([*-]) (.+)$/gm, \"<li>$2</li>\");\n // Wrap consecutive <li> in <ul>\n html = html.replace(\n /(<li>.*?<\\/li>\\n?)+/g,\n '<ul style=\"margin:8px 0;padding-left:20px;\">$&</ul>',\n );\n\n // Ordered lists: lines starting with \"1. \", \"2. \" etc.\n html = html.replace(/^\\d+\\. (.+)$/gm, \"<li>$1</li>\");\n // Wrap consecutive <li> that aren't in <ul> in <ol>\n html = html.replace(/(?<!<\\/ul>)(<li>.*?<\\/li>\\n?)+/g, (match) => {\n if (match.includes(\"<ul\")) return match;\n return `<ol style=\"margin:8px 0;padding-left:20px;\">${match}</ol>`;\n });\n\n // Headings: # through ###\n html = html.replace(\n /^### (.+)$/gm,\n '<h3 style=\"margin:16px 0 8px;font-size:1.1em;\">$1</h3>',\n );\n html = html.replace(\n /^## (.+)$/gm,\n '<h2 style=\"margin:16px 0 8px;font-size:1.25em;\">$1</h2>',\n );\n html = html.replace(\n /^# (.+)$/gm,\n '<h1 style=\"margin:16px 0 8px;font-size:1.4em;\">$1</h1>',\n );\n\n // Horizontal rules: --- or ***\n html = html.replace(\n /^(-{3,}|\\*{3,})$/gm,\n '<hr style=\"border:none;border-top:1px solid #e2e8f0;margin:16px 0;\">',\n );\n\n // Paragraphs: double newlines\n html = html.replace(/\\n\\n/g, \"</p><p>\");\n // Single newlines → <br>\n html = html.replace(/\\n/g, \"<br>\");\n\n // Wrap in paragraph tags\n html = `<p>${html}</p>`;\n // Clean up empty paragraphs\n html = html.replace(/<p>\\s*<\\/p>/g, \"\");\n\n return html;\n}\n\n/** Wrap body HTML in a minimal email template with inline styles */\nfunction wrapInEmailTemplate(bodyHtml: string): string {\n return `<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n</head>\n<body style=\"margin:0;padding:0;background-color:#ffffff;\">\n<div style=\"max-width:600px;margin:0 auto;padding:20px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;font-size:14px;line-height:1.6;color:#1a1a1a;\">\n${bodyHtml}\n</div>\n</body>\n</html>`;\n}\n\n// ---------------------------------------------------------------------------\n// Raw body reader (matches Slack adapter pattern)\n// ---------------------------------------------------------------------------\n\n/**\n * Read the raw request body as a string and cache on the event context.\n * Reads raw bytes from the request stream — never re-stringifies a parsed\n * body, since the Resend / Svix HMAC is computed over the exact bytes sent\n * (M2 in the webhook security audit).\n */\nasync function readRawBody(event: H3Event): Promise<string> {\n const cached = event.context.__rawBody;\n if (typeof cached === \"string\") return cached;\n const raw = (await h3ReadRawBody(event)) ?? \"\";\n event.context.__rawBody = raw;\n return raw;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"google-docs.d.ts","sourceRoot":"","sources":["../../../src/integrations/adapters/google-docs.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,eAAe,EAIhB,MAAM,aAAa,CAAC;AAOrB,UAAU,iBAAiB;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAID;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,iBAAiB,GAAG,IAAI,CAe/D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,GAAG,IAAI,CAGtD;AAED;;GAEG;AACH,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA2D3E;AAID;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,iBAAiB,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,OAAO,CAAC,EAAE,KAAK,CAAC;QACd,EAAE,EAAE,MAAM,CAAC;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE;YAAE,WAAW,EAAE,MAAM,CAAC;YAAC,YAAY,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACvD,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC,CAAC;CACJ;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,iBAAiB,CAAC,EAAE,MAAM,GACzB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAoB7B;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAmBf;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ5E;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE;QACL,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;IAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC,CA0B5D;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,IAAI,eAAe,CA6EnD"}
1
+ {"version":3,"file":"google-docs.d.ts","sourceRoot":"","sources":["../../../src/integrations/adapters/google-docs.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,eAAe,EAKhB,MAAM,aAAa,CAAC;AAOrB,UAAU,iBAAiB;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAID;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,iBAAiB,GAAG,IAAI,CAe/D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,GAAG,IAAI,CAGtD;AAED;;GAEG;AACH,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA2D3E;AAID;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,iBAAiB,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,OAAO,CAAC,EAAE,KAAK,CAAC;QACd,EAAE,EAAE,MAAM,CAAC;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE;YAAE,WAAW,EAAE,MAAM,CAAC;YAAC,YAAY,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACvD,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC,CAAC;CACJ;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,iBAAiB,CAAC,EAAE,MAAM,GACzB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAoB7B;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAmBf;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ5E;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE;QACL,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;IAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC,CA0B5D;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,IAAI,eAAe,CA+EnD"}
@@ -212,8 +212,10 @@ export function googleDocsAdapter() {
212
212
  }
213
213
  catch (err) {
214
214
  console.error("[google-docs] Failed to send reply:", err);
215
+ throw err;
215
216
  }
216
217
  }
218
+ return { status: "delivered" };
217
219
  },
218
220
  formatAgentResponse(text) {
219
221
  return { text, platformContext: {} };
@@ -1 +1 @@
1
- {"version":3,"file":"google-docs.js","sourceRoot":"","sources":["../../../src/integrations/adapters/google-docs.ts"],"names":[],"mappings":"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","sourcesContent":["import type { H3Event } from \"h3\";\n\nimport type { EnvKeyConfig } from \"../../server/create-server.js\";\nimport type {\n PlatformAdapter,\n IncomingMessage,\n OutgoingMessage,\n IntegrationStatus,\n} from \"../types.js\";\n\n/** Google Docs comment replies have no formal length limit but keep it reasonable */\nconst GDOCS_MAX_LENGTH = 4000;\n\n// ─── Service Account Auth ───────────────────────────────────────────────────\n\ninterface ServiceAccountKey {\n client_email: string;\n private_key: string;\n token_uri?: string;\n}\n\nlet cachedToken: { token: string; expiresAt: number } | null = null;\n\n/**\n * Parse the service account key from env.\n * Supports both a JSON string and a file path.\n */\nexport function getServiceAccountKey(): ServiceAccountKey | null {\n const raw = process.env.GOOGLE_SERVICE_ACCOUNT_KEY;\n if (!raw) return null;\n try {\n return JSON.parse(raw) as ServiceAccountKey;\n } catch {\n // Could be a file path — try reading it\n try {\n const fs = require(\"node:fs\");\n const content = fs.readFileSync(raw, \"utf-8\");\n return JSON.parse(content) as ServiceAccountKey;\n } catch {\n return null;\n }\n }\n}\n\n/**\n * Get the service account email for display (users share docs with this).\n */\nexport function getServiceAccountEmail(): string | null {\n const key = getServiceAccountKey();\n return key?.client_email ?? null;\n}\n\n/**\n * Create a signed JWT and exchange it for an access token.\n */\nexport async function getServiceAccountAccessToken(): Promise<string | null> {\n // Return cached token if still valid (with 60s buffer)\n if (cachedToken && Date.now() < cachedToken.expiresAt - 60_000) {\n return cachedToken.token;\n }\n\n const key = getServiceAccountKey();\n if (!key) return null;\n\n try {\n const crypto = await import(\"node:crypto\");\n\n const now = Math.floor(Date.now() / 1000);\n const header = { alg: \"RS256\", typ: \"JWT\" };\n const payload = {\n iss: key.client_email,\n scope: \"https://www.googleapis.com/auth/drive\",\n aud: key.token_uri || \"https://oauth2.googleapis.com/token\",\n iat: now,\n exp: now + 3600,\n };\n\n const encode = (obj: unknown) =>\n Buffer.from(JSON.stringify(obj)).toString(\"base64url\");\n\n const unsigned = `${encode(header)}.${encode(payload)}`;\n const signer = crypto.createSign(\"RSA-SHA256\");\n signer.update(unsigned);\n const signature = signer.sign(key.private_key, \"base64url\");\n const jwt = `${unsigned}.${signature}`;\n\n const res = await fetch(\"https://oauth2.googleapis.com/token\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n grant_type: \"urn:ietf:params:oauth:grant-type:jwt-bearer\",\n assertion: jwt,\n }),\n });\n\n if (!res.ok) {\n const err = await res.text();\n console.error(\"[google-docs] Token exchange failed:\", err);\n return null;\n }\n\n const data = (await res.json()) as {\n access_token: string;\n expires_in: number;\n };\n cachedToken = {\n token: data.access_token,\n expiresAt: Date.now() + data.expires_in * 1000,\n };\n return data.access_token;\n } catch (err) {\n console.error(\"[google-docs] Failed to get service account token:\", err);\n return null;\n }\n}\n\n// ─── Google Drive API Helpers ───────────────────────────────────────────────\n\n/**\n * Extract a Google Doc file ID from a URL or return the string as-is.\n */\nexport function extractFileId(urlOrId: string): string {\n const match = urlOrId.match(/\\/d\\/([a-zA-Z0-9_-]+)/);\n return match ? match[1] : urlOrId;\n}\n\nexport interface GoogleDocComment {\n id: string;\n content: string;\n author: { displayName: string; emailAddress?: string };\n createdTime: string;\n modifiedTime: string;\n resolved: boolean;\n quotedFileContent?: { value: string };\n replies?: Array<{\n id: string;\n content: string;\n author: { displayName: string; emailAddress?: string };\n createdTime: string;\n }>;\n}\n\n/**\n * List comments on a Google Doc, optionally filtering by modified time.\n */\nexport async function listDocComments(\n fileId: string,\n accessToken: string,\n startModifiedTime?: string,\n): Promise<GoogleDocComment[]> {\n const params = new URLSearchParams({\n fields:\n \"comments(id,content,author,createdTime,modifiedTime,resolved,quotedFileContent,replies(id,content,author,createdTime))\",\n pageSize: \"100\",\n });\n if (startModifiedTime) {\n params.set(\"startModifiedTime\", startModifiedTime);\n }\n\n const res = await fetch(\n `https://www.googleapis.com/drive/v3/files/${fileId}/comments?${params}`,\n { headers: { Authorization: `Bearer ${accessToken}` } },\n );\n if (!res.ok) {\n const err = await res.text();\n throw new Error(`Failed to list comments: ${err}`);\n }\n const data = (await res.json()) as { comments?: GoogleDocComment[] };\n return data.comments ?? [];\n}\n\n/**\n * Reply to a comment on a Google Doc.\n */\nexport async function replyToComment(\n fileId: string,\n commentId: string,\n content: string,\n accessToken: string,\n): Promise<void> {\n const res = await fetch(\n `https://www.googleapis.com/drive/v3/files/${fileId}/comments/${commentId}/replies`,\n {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${accessToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n content,\n fields: \"id\",\n }),\n },\n );\n if (!res.ok) {\n const err = await res.text();\n throw new Error(`Failed to reply to comment: ${err}`);\n }\n}\n\n/**\n * Get the start page token for changes.list (initial sync point).\n */\nexport async function getStartPageToken(accessToken: string): Promise<string> {\n const res = await fetch(\n \"https://www.googleapis.com/drive/v3/changes/startPageToken\",\n { headers: { Authorization: `Bearer ${accessToken}` } },\n );\n if (!res.ok) throw new Error(\"Failed to get start page token\");\n const data = (await res.json()) as { startPageToken: string };\n return data.startPageToken;\n}\n\nexport interface DriveChange {\n fileId: string;\n removed: boolean;\n file?: {\n id: string;\n name: string;\n mimeType: string;\n };\n}\n\n/**\n * List changes since a page token. Returns changed file IDs and the next token.\n */\nexport async function listChanges(\n pageToken: string,\n accessToken: string,\n): Promise<{ changes: DriveChange[]; nextPageToken: string }> {\n const params = new URLSearchParams({\n pageToken,\n fields:\n \"nextPageToken,newStartPageToken,changes(fileId,removed,file(id,name,mimeType))\",\n pageSize: \"100\",\n includeRemoved: \"false\",\n });\n\n const res = await fetch(\n `https://www.googleapis.com/drive/v3/changes?${params}`,\n { headers: { Authorization: `Bearer ${accessToken}` } },\n );\n if (!res.ok) {\n const err = await res.text();\n throw new Error(`Failed to list changes: ${err}`);\n }\n const data = (await res.json()) as {\n changes?: DriveChange[];\n nextPageToken?: string;\n newStartPageToken?: string;\n };\n return {\n changes: data.changes ?? [],\n nextPageToken: data.nextPageToken || data.newStartPageToken || pageToken,\n };\n}\n\n// ─── Platform Adapter ───────────────────────────────────────────────────────\n\n/**\n * Create a Google Docs platform adapter.\n *\n * Unlike Slack/Telegram, this adapter is poll-driven — the poller\n * constructs IncomingMessage objects and feeds them through the\n * webhook handler. The adapter handles formatting and sending replies.\n *\n * Setup:\n * - Set GOOGLE_SERVICE_ACCOUNT_KEY (JSON string or file path) in env\n * - Users share their Google Docs with the service account email\n * - Comments containing the trigger keyword (default: \"@agent\") are processed\n */\nexport function googleDocsAdapter(): PlatformAdapter {\n return {\n platform: \"google-docs\",\n label: \"Google Docs\",\n\n getRequiredEnvKeys(): EnvKeyConfig[] {\n return [\n {\n key: \"GOOGLE_SERVICE_ACCOUNT_KEY\",\n label: \"Google Service Account Key (JSON)\",\n required: true,\n },\n ];\n },\n\n async handleVerification(\n _event: H3Event,\n ): Promise<{ handled: boolean; response?: unknown }> {\n return { handled: false };\n },\n\n async verifyWebhook(_event: H3Event): Promise<boolean> {\n return true;\n },\n\n async parseIncomingMessage(\n _event: H3Event,\n ): Promise<IncomingMessage | null> {\n return null;\n },\n\n async sendResponse(\n message: OutgoingMessage,\n context: IncomingMessage,\n ): Promise<void> {\n const fileId = context.platformContext.fileId as string;\n const commentId = context.platformContext.commentId as string;\n\n const accessToken = await getServiceAccountAccessToken();\n if (!accessToken) {\n console.error(\"[google-docs] No access token available to send reply\");\n return;\n }\n\n const chunks = splitMessage(message.text, GDOCS_MAX_LENGTH);\n for (const chunk of chunks) {\n try {\n await replyToComment(fileId, commentId, chunk, accessToken);\n } catch (err) {\n console.error(\"[google-docs] Failed to send reply:\", err);\n }\n }\n },\n\n formatAgentResponse(text: string): OutgoingMessage {\n return { text, platformContext: {} };\n },\n\n async getStatus(_baseUrl?: string): Promise<IntegrationStatus> {\n const key = getServiceAccountKey();\n const configured = !!key;\n const email = key?.client_email;\n\n return {\n platform: \"google-docs\",\n label: \"Google Docs\",\n enabled: false,\n configured,\n details: {\n serviceAccountEmail: email,\n },\n error: !configured\n ? \"Set GOOGLE_SERVICE_ACCOUNT_KEY in your environment (JSON string or file path to the key file)\"\n : undefined,\n };\n },\n };\n}\n\n/** Split a message into chunks that fit within the platform's limit */\nfunction splitMessage(text: string, maxLength: number): string[] {\n if (text.length <= maxLength) return [text];\n const chunks: string[] = [];\n let remaining = text;\n while (remaining.length > 0) {\n if (remaining.length <= maxLength) {\n chunks.push(remaining);\n break;\n }\n let splitIdx = remaining.lastIndexOf(\"\\n\", maxLength);\n if (splitIdx <= 0) splitIdx = remaining.lastIndexOf(\" \", maxLength);\n if (splitIdx <= 0) splitIdx = maxLength;\n chunks.push(remaining.slice(0, splitIdx));\n remaining = remaining.slice(splitIdx).trimStart();\n }\n return chunks;\n}\n"]}
1
+ {"version":3,"file":"google-docs.js","sourceRoot":"","sources":["../../../src/integrations/adapters/google-docs.ts"],"names":[],"mappings":"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","sourcesContent":["import type { H3Event } from \"h3\";\n\nimport type { EnvKeyConfig } from \"../../server/create-server.js\";\nimport type {\n PlatformAdapter,\n IncomingMessage,\n OutgoingMessage,\n IntegrationStatus,\n PlatformDeliveryReceipt,\n} from \"../types.js\";\n\n/** Google Docs comment replies have no formal length limit but keep it reasonable */\nconst GDOCS_MAX_LENGTH = 4000;\n\n// ─── Service Account Auth ───────────────────────────────────────────────────\n\ninterface ServiceAccountKey {\n client_email: string;\n private_key: string;\n token_uri?: string;\n}\n\nlet cachedToken: { token: string; expiresAt: number } | null = null;\n\n/**\n * Parse the service account key from env.\n * Supports both a JSON string and a file path.\n */\nexport function getServiceAccountKey(): ServiceAccountKey | null {\n const raw = process.env.GOOGLE_SERVICE_ACCOUNT_KEY;\n if (!raw) return null;\n try {\n return JSON.parse(raw) as ServiceAccountKey;\n } catch {\n // Could be a file path — try reading it\n try {\n const fs = require(\"node:fs\");\n const content = fs.readFileSync(raw, \"utf-8\");\n return JSON.parse(content) as ServiceAccountKey;\n } catch {\n return null;\n }\n }\n}\n\n/**\n * Get the service account email for display (users share docs with this).\n */\nexport function getServiceAccountEmail(): string | null {\n const key = getServiceAccountKey();\n return key?.client_email ?? null;\n}\n\n/**\n * Create a signed JWT and exchange it for an access token.\n */\nexport async function getServiceAccountAccessToken(): Promise<string | null> {\n // Return cached token if still valid (with 60s buffer)\n if (cachedToken && Date.now() < cachedToken.expiresAt - 60_000) {\n return cachedToken.token;\n }\n\n const key = getServiceAccountKey();\n if (!key) return null;\n\n try {\n const crypto = await import(\"node:crypto\");\n\n const now = Math.floor(Date.now() / 1000);\n const header = { alg: \"RS256\", typ: \"JWT\" };\n const payload = {\n iss: key.client_email,\n scope: \"https://www.googleapis.com/auth/drive\",\n aud: key.token_uri || \"https://oauth2.googleapis.com/token\",\n iat: now,\n exp: now + 3600,\n };\n\n const encode = (obj: unknown) =>\n Buffer.from(JSON.stringify(obj)).toString(\"base64url\");\n\n const unsigned = `${encode(header)}.${encode(payload)}`;\n const signer = crypto.createSign(\"RSA-SHA256\");\n signer.update(unsigned);\n const signature = signer.sign(key.private_key, \"base64url\");\n const jwt = `${unsigned}.${signature}`;\n\n const res = await fetch(\"https://oauth2.googleapis.com/token\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n grant_type: \"urn:ietf:params:oauth:grant-type:jwt-bearer\",\n assertion: jwt,\n }),\n });\n\n if (!res.ok) {\n const err = await res.text();\n console.error(\"[google-docs] Token exchange failed:\", err);\n return null;\n }\n\n const data = (await res.json()) as {\n access_token: string;\n expires_in: number;\n };\n cachedToken = {\n token: data.access_token,\n expiresAt: Date.now() + data.expires_in * 1000,\n };\n return data.access_token;\n } catch (err) {\n console.error(\"[google-docs] Failed to get service account token:\", err);\n return null;\n }\n}\n\n// ─── Google Drive API Helpers ───────────────────────────────────────────────\n\n/**\n * Extract a Google Doc file ID from a URL or return the string as-is.\n */\nexport function extractFileId(urlOrId: string): string {\n const match = urlOrId.match(/\\/d\\/([a-zA-Z0-9_-]+)/);\n return match ? match[1] : urlOrId;\n}\n\nexport interface GoogleDocComment {\n id: string;\n content: string;\n author: { displayName: string; emailAddress?: string };\n createdTime: string;\n modifiedTime: string;\n resolved: boolean;\n quotedFileContent?: { value: string };\n replies?: Array<{\n id: string;\n content: string;\n author: { displayName: string; emailAddress?: string };\n createdTime: string;\n }>;\n}\n\n/**\n * List comments on a Google Doc, optionally filtering by modified time.\n */\nexport async function listDocComments(\n fileId: string,\n accessToken: string,\n startModifiedTime?: string,\n): Promise<GoogleDocComment[]> {\n const params = new URLSearchParams({\n fields:\n \"comments(id,content,author,createdTime,modifiedTime,resolved,quotedFileContent,replies(id,content,author,createdTime))\",\n pageSize: \"100\",\n });\n if (startModifiedTime) {\n params.set(\"startModifiedTime\", startModifiedTime);\n }\n\n const res = await fetch(\n `https://www.googleapis.com/drive/v3/files/${fileId}/comments?${params}`,\n { headers: { Authorization: `Bearer ${accessToken}` } },\n );\n if (!res.ok) {\n const err = await res.text();\n throw new Error(`Failed to list comments: ${err}`);\n }\n const data = (await res.json()) as { comments?: GoogleDocComment[] };\n return data.comments ?? [];\n}\n\n/**\n * Reply to a comment on a Google Doc.\n */\nexport async function replyToComment(\n fileId: string,\n commentId: string,\n content: string,\n accessToken: string,\n): Promise<void> {\n const res = await fetch(\n `https://www.googleapis.com/drive/v3/files/${fileId}/comments/${commentId}/replies`,\n {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${accessToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n content,\n fields: \"id\",\n }),\n },\n );\n if (!res.ok) {\n const err = await res.text();\n throw new Error(`Failed to reply to comment: ${err}`);\n }\n}\n\n/**\n * Get the start page token for changes.list (initial sync point).\n */\nexport async function getStartPageToken(accessToken: string): Promise<string> {\n const res = await fetch(\n \"https://www.googleapis.com/drive/v3/changes/startPageToken\",\n { headers: { Authorization: `Bearer ${accessToken}` } },\n );\n if (!res.ok) throw new Error(\"Failed to get start page token\");\n const data = (await res.json()) as { startPageToken: string };\n return data.startPageToken;\n}\n\nexport interface DriveChange {\n fileId: string;\n removed: boolean;\n file?: {\n id: string;\n name: string;\n mimeType: string;\n };\n}\n\n/**\n * List changes since a page token. Returns changed file IDs and the next token.\n */\nexport async function listChanges(\n pageToken: string,\n accessToken: string,\n): Promise<{ changes: DriveChange[]; nextPageToken: string }> {\n const params = new URLSearchParams({\n pageToken,\n fields:\n \"nextPageToken,newStartPageToken,changes(fileId,removed,file(id,name,mimeType))\",\n pageSize: \"100\",\n includeRemoved: \"false\",\n });\n\n const res = await fetch(\n `https://www.googleapis.com/drive/v3/changes?${params}`,\n { headers: { Authorization: `Bearer ${accessToken}` } },\n );\n if (!res.ok) {\n const err = await res.text();\n throw new Error(`Failed to list changes: ${err}`);\n }\n const data = (await res.json()) as {\n changes?: DriveChange[];\n nextPageToken?: string;\n newStartPageToken?: string;\n };\n return {\n changes: data.changes ?? [],\n nextPageToken: data.nextPageToken || data.newStartPageToken || pageToken,\n };\n}\n\n// ─── Platform Adapter ───────────────────────────────────────────────────────\n\n/**\n * Create a Google Docs platform adapter.\n *\n * Unlike Slack/Telegram, this adapter is poll-driven — the poller\n * constructs IncomingMessage objects and feeds them through the\n * webhook handler. The adapter handles formatting and sending replies.\n *\n * Setup:\n * - Set GOOGLE_SERVICE_ACCOUNT_KEY (JSON string or file path) in env\n * - Users share their Google Docs with the service account email\n * - Comments containing the trigger keyword (default: \"@agent\") are processed\n */\nexport function googleDocsAdapter(): PlatformAdapter {\n return {\n platform: \"google-docs\",\n label: \"Google Docs\",\n\n getRequiredEnvKeys(): EnvKeyConfig[] {\n return [\n {\n key: \"GOOGLE_SERVICE_ACCOUNT_KEY\",\n label: \"Google Service Account Key (JSON)\",\n required: true,\n },\n ];\n },\n\n async handleVerification(\n _event: H3Event,\n ): Promise<{ handled: boolean; response?: unknown }> {\n return { handled: false };\n },\n\n async verifyWebhook(_event: H3Event): Promise<boolean> {\n return true;\n },\n\n async parseIncomingMessage(\n _event: H3Event,\n ): Promise<IncomingMessage | null> {\n return null;\n },\n\n async sendResponse(\n message: OutgoingMessage,\n context: IncomingMessage,\n ): Promise<void | PlatformDeliveryReceipt> {\n const fileId = context.platformContext.fileId as string;\n const commentId = context.platformContext.commentId as string;\n\n const accessToken = await getServiceAccountAccessToken();\n if (!accessToken) {\n console.error(\"[google-docs] No access token available to send reply\");\n return;\n }\n\n const chunks = splitMessage(message.text, GDOCS_MAX_LENGTH);\n for (const chunk of chunks) {\n try {\n await replyToComment(fileId, commentId, chunk, accessToken);\n } catch (err) {\n console.error(\"[google-docs] Failed to send reply:\", err);\n throw err;\n }\n }\n return { status: \"delivered\" };\n },\n\n formatAgentResponse(text: string): OutgoingMessage {\n return { text, platformContext: {} };\n },\n\n async getStatus(_baseUrl?: string): Promise<IntegrationStatus> {\n const key = getServiceAccountKey();\n const configured = !!key;\n const email = key?.client_email;\n\n return {\n platform: \"google-docs\",\n label: \"Google Docs\",\n enabled: false,\n configured,\n details: {\n serviceAccountEmail: email,\n },\n error: !configured\n ? \"Set GOOGLE_SERVICE_ACCOUNT_KEY in your environment (JSON string or file path to the key file)\"\n : undefined,\n };\n },\n };\n}\n\n/** Split a message into chunks that fit within the platform's limit */\nfunction splitMessage(text: string, maxLength: number): string[] {\n if (text.length <= maxLength) return [text];\n const chunks: string[] = [];\n let remaining = text;\n while (remaining.length > 0) {\n if (remaining.length <= maxLength) {\n chunks.push(remaining);\n break;\n }\n let splitIdx = remaining.lastIndexOf(\"\\n\", maxLength);\n if (splitIdx <= 0) splitIdx = remaining.lastIndexOf(\" \", maxLength);\n if (splitIdx <= 0) splitIdx = maxLength;\n chunks.push(remaining.slice(0, splitIdx));\n remaining = remaining.slice(splitIdx).trimStart();\n }\n return chunks;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"microsoft-teams.d.ts","sourceRoot":"","sources":["../../../src/integrations/adapters/microsoft-teams.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAIV,eAAe,EAChB,MAAM,aAAa,CAAC;AAkCrB,wBAAgB,qBAAqB,IAAI,eAAe,CA4OvD;AAED,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,MAAM,CAAC,CA8CpE;AAED,wBAAgB,mCAAmC,IAAI,IAAI,CAE1D"}
1
+ {"version":3,"file":"microsoft-teams.d.ts","sourceRoot":"","sources":["../../../src/integrations/adapters/microsoft-teams.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAIV,eAAe,EAEhB,MAAM,aAAa,CAAC;AAkCrB,wBAAgB,qBAAqB,IAAI,eAAe,CA6OvD;AAED,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,MAAM,CAAC,CA8CpE;AAED,wBAAgB,mCAAmC,IAAI,IAAI,CAE1D"}
@@ -158,6 +158,7 @@ export function microsoftTeamsAdapter() {
158
158
  if (!response.ok) {
159
159
  throw new Error(`Microsoft Teams reply failed (HTTP ${response.status})`);
160
160
  }
161
+ return { status: "delivered" };
161
162
  },
162
163
  formatAgentResponse(text) {
163
164
  return { text, platformContext: {} };
@@ -1 +1 @@
1
- {"version":3,"file":"microsoft-teams.js","sourceRoot":"","sources":["../../../src/integrations/adapters/microsoft-teams.ts"],"names":[],"mappings":"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","sourcesContent":["import type { H3Event } from \"h3\";\nimport { getHeader, readRawBody } from \"h3\";\n\nimport type { EnvKeyConfig } from \"../../server/create-server.js\";\nimport { resolveSecret } from \"../../server/credential-provider.js\";\nimport type {\n IncomingMessage,\n IntegrationStatus,\n OutgoingMessage,\n PlatformAdapter,\n} from \"../types.js\";\n\nconst BOT_FRAMEWORK_SCOPE = \"https://api.botframework.com/.default\";\nconst TOKEN_REFRESH_SKEW_MS = 5 * 60 * 1_000;\n\ntype TeamsActivity = {\n id?: unknown;\n type?: unknown;\n timestamp?: unknown;\n serviceUrl?: unknown;\n channelId?: unknown;\n text?: unknown;\n replyToId?: unknown;\n from?: { id?: unknown; name?: unknown; aadObjectId?: unknown };\n recipient?: { id?: unknown; name?: unknown };\n conversation?: {\n id?: unknown;\n tenantId?: unknown;\n conversationType?: unknown;\n };\n channelData?: {\n tenant?: { id?: unknown };\n team?: { id?: unknown; name?: unknown };\n channel?: { id?: unknown; name?: unknown };\n };\n};\n\ntype TokenCacheEntry = {\n accessToken: string;\n expiresAt: number;\n};\n\nconst accessTokenCache = new Map<string, TokenCacheEntry>();\n\nexport function microsoftTeamsAdapter(): PlatformAdapter {\n return {\n platform: \"microsoft-teams\",\n label: \"Microsoft Teams\",\n capabilities: {\n replyText: true,\n proactiveMessages: false,\n nativeThreads: true,\n contextualReplies: true,\n deferredWebhookResponse: false,\n interactionOnly: false,\n },\n\n getRequiredEnvKeys(): EnvKeyConfig[] {\n return [\n {\n key: \"MICROSOFT_TEAMS_APP_ID\",\n label: \"Microsoft Bot App ID\",\n required: true,\n helpText:\n \"The Microsoft App ID associated with the Azure Bot resource.\",\n },\n {\n key: \"MICROSOFT_TEAMS_APP_PASSWORD\",\n label: \"Microsoft Bot Client Secret\",\n required: true,\n helpText:\n \"A current client secret for the bot app registration. Store the secret value, not its secret ID.\",\n },\n {\n key: \"MICROSOFT_TEAMS_APP_TENANT_ID\",\n label: \"Microsoft Bot Tenant ID\",\n required: false,\n helpText:\n \"Required for single-tenant bot registrations. Multi-tenant Bot Framework apps use botframework.com.\",\n },\n {\n key: \"MICROSOFT_TEAMS_ALLOWED_TENANT_IDS\",\n label: \"Allowed Microsoft Teams Tenant IDs\",\n required: true,\n helpText:\n \"Comma-separated tenant IDs allowed to invoke this deployment. Production webhooks fail closed when unset.\",\n },\n ];\n },\n\n async handleVerification(event: H3Event) {\n await readRawBodyCached(event);\n return { handled: false };\n },\n\n async verifyWebhook(event: H3Event): Promise<boolean> {\n const appId = await resolveSecret(\"MICROSOFT_TEAMS_APP_ID\");\n const appPassword = await resolveSecret(\"MICROSOFT_TEAMS_APP_PASSWORD\");\n const authorization = getHeader(event, \"authorization\") ?? \"\";\n const activity = parseActivity(await readRawBodyCached(event));\n if (\n !appId ||\n !appPassword ||\n !authorization.startsWith(\"Bearer \") ||\n !activity ||\n activity.channelId !== \"msteams\" ||\n !readString(activity.serviceUrl)\n ) {\n return false;\n }\n\n try {\n const { JwtTokenValidation, SimpleCredentialProvider } =\n await import(\"botframework-connector\");\n await JwtTokenValidation.authenticateRequest(\n activity as never,\n authorization,\n new SimpleCredentialProvider(appId, appPassword),\n \"\",\n );\n } catch {\n return false;\n }\n\n const tenantId = teamsTenantId(activity);\n return isAllowedTenant(\n tenantId,\n await resolveSecret(\"MICROSOFT_TEAMS_ALLOWED_TENANT_IDS\"),\n );\n },\n\n async parseIncomingMessage(\n event: H3Event,\n ): Promise<IncomingMessage | null> {\n const activity = parseActivity(await readRawBodyCached(event));\n if (\n !activity ||\n activity.channelId !== \"msteams\" ||\n activity.type !== \"message\"\n ) {\n return null;\n }\n\n const activityId = readString(activity.id);\n const conversationId = readString(activity.conversation?.id);\n const serviceUrl = normalizeServiceUrl(activity.serviceUrl);\n const tenantId = teamsTenantId(activity);\n const teamId = readString(activity.channelData?.team?.id);\n const channelId = readString(activity.channelData?.channel?.id);\n const senderId = readString(\n activity.from?.aadObjectId ?? activity.from?.id,\n );\n const text = stripTeamsMentions(readString(activity.text) ?? \"\");\n if (\n !activityId ||\n !conversationId ||\n !serviceUrl ||\n !tenantId ||\n !senderId ||\n !text\n ) {\n return null;\n }\n\n const location = teamId\n ? `:team:${teamId}:channel:${channelId ?? conversationId}`\n : \"\";\n const replyRef = readString(activity.replyToId) ?? activityId;\n\n return {\n platform: \"microsoft-teams\",\n externalThreadId: `tenant:${tenantId}${location}:conversation:${conversationId}`,\n text,\n senderName: readString(activity.from?.name),\n senderId,\n threadRef: conversationId,\n replyRef,\n platformContext: {\n tenantId,\n teamId,\n channelId,\n conversationId,\n conversationType: readString(activity.conversation?.conversationType),\n botFrameworkChannelId: readString(activity.channelId),\n activityId,\n replyToId: replyRef,\n serviceUrl,\n from: {\n id: readString(activity.from?.id),\n name: readString(activity.from?.name),\n },\n recipient: {\n id: readString(activity.recipient?.id),\n name: readString(activity.recipient?.name),\n },\n },\n timestamp: parseTimestamp(activity.timestamp),\n };\n },\n\n async sendResponse(\n message: OutgoingMessage,\n context: IncomingMessage,\n ): Promise<void> {\n const serviceUrl = normalizeServiceUrl(\n context.platformContext.serviceUrl,\n );\n const conversationId = readString(context.platformContext.conversationId);\n const activityId =\n context.replyRef ??\n readString(context.platformContext.activityId) ??\n readString(context.platformContext.replyToId);\n if (!serviceUrl || !conversationId || !activityId) {\n throw new Error(\"Microsoft Teams conversation reference is incomplete\");\n }\n\n const accessToken = await getMicrosoftTeamsAccessToken();\n const response = await fetch(\n `${serviceUrl}/v3/conversations/${encodeURIComponent(conversationId)}/activities/${encodeURIComponent(activityId)}`,\n {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${accessToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n type: \"message\",\n text: message.text,\n replyToId: activityId,\n channelId:\n readString(context.platformContext.botFrameworkChannelId) ??\n \"msteams\",\n serviceUrl,\n conversation: { id: conversationId },\n from: context.platformContext.recipient,\n recipient: context.platformContext.from,\n }),\n },\n );\n if (!response.ok) {\n throw new Error(\n `Microsoft Teams reply failed (HTTP ${response.status})`,\n );\n }\n },\n\n formatAgentResponse(text: string): OutgoingMessage {\n return { text, platformContext: {} };\n },\n\n async getStatus(): Promise<IntegrationStatus> {\n const hasAppId = !!(await resolveSecret(\"MICROSOFT_TEAMS_APP_ID\"));\n const hasAppPassword = !!(await resolveSecret(\n \"MICROSOFT_TEAMS_APP_PASSWORD\",\n ));\n const allowedTenants = await resolveSecret(\n \"MICROSOFT_TEAMS_ALLOWED_TENANT_IDS\",\n );\n const hasAllowedTenants = parseCsv(allowedTenants).size > 0;\n const configured =\n hasAppId &&\n hasAppPassword &&\n (process.env.NODE_ENV !== \"production\" || hasAllowedTenants);\n return {\n platform: \"microsoft-teams\",\n label: \"Microsoft Teams\",\n enabled: false,\n configured,\n details: {\n hasAppId,\n hasAppPassword,\n hasAllowedTenants,\n validation: \"botframework-connector\",\n },\n error: !configured\n ? \"Save the Microsoft Bot App ID, client secret, and production tenant allowlist in settings\"\n : undefined,\n };\n },\n };\n}\n\nexport async function getMicrosoftTeamsAccessToken(): Promise<string> {\n const appId = await resolveSecret(\"MICROSOFT_TEAMS_APP_ID\");\n const appPassword = await resolveSecret(\"MICROSOFT_TEAMS_APP_PASSWORD\");\n const tenantId =\n (await resolveSecret(\"MICROSOFT_TEAMS_APP_TENANT_ID\")) ??\n \"botframework.com\";\n if (!appId || !appPassword) {\n throw new Error(\"Microsoft Teams bot credentials are not configured\");\n }\n\n const cacheKey = `${tenantId}:${appId}`;\n const cached = accessTokenCache.get(cacheKey);\n if (cached && cached.expiresAt - TOKEN_REFRESH_SKEW_MS > Date.now()) {\n return cached.accessToken;\n }\n\n const response = await fetch(\n `https://login.microsoftonline.com/${encodeURIComponent(tenantId)}/oauth2/v2.0/token`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n grant_type: \"client_credentials\",\n client_id: appId,\n client_secret: appPassword,\n scope: BOT_FRAMEWORK_SCOPE,\n }),\n },\n );\n const body = (await response.json().catch(() => null)) as {\n access_token?: unknown;\n expires_in?: unknown;\n } | null;\n const accessToken = readString(body?.access_token);\n const expiresIn = Number(body?.expires_in);\n if (!response.ok || !accessToken || !Number.isFinite(expiresIn)) {\n throw new Error(\n `Microsoft Teams token request failed (HTTP ${response.status})`,\n );\n }\n\n accessTokenCache.set(cacheKey, {\n accessToken,\n expiresAt: Date.now() + Math.max(60, expiresIn) * 1_000,\n });\n return accessToken;\n}\n\nexport function clearMicrosoftTeamsAccessTokenCache(): void {\n accessTokenCache.clear();\n}\n\nasync function readRawBodyCached(event: H3Event): Promise<string> {\n const cached = event.context.__rawBody;\n if (typeof cached === \"string\") return cached;\n const raw = (await readRawBody(event)) ?? \"\";\n event.context.__rawBody = raw;\n return raw;\n}\n\nfunction parseActivity(raw: string): TeamsActivity | null {\n try {\n const value = JSON.parse(raw);\n return value && typeof value === \"object\" ? (value as TeamsActivity) : null;\n } catch {\n return null;\n }\n}\n\nfunction teamsTenantId(activity: TeamsActivity): string | undefined {\n return readString(\n activity.channelData?.tenant?.id ?? activity.conversation?.tenantId,\n );\n}\n\nfunction isAllowedTenant(\n tenantId: string | undefined,\n allowlist: string | null | undefined,\n): boolean {\n if (!tenantId) return false;\n const allowed = parseCsv(allowlist);\n if (allowed.size === 0) return process.env.NODE_ENV !== \"production\";\n return allowed.has(tenantId);\n}\n\nfunction parseCsv(value: string | null | undefined): Set<string> {\n return new Set(\n (value ?? \"\")\n .split(\",\")\n .map((entry) => entry.trim())\n .filter(Boolean),\n );\n}\n\nfunction normalizeServiceUrl(value: unknown): string | null {\n const raw = readString(value);\n if (!raw) return null;\n try {\n const url = new URL(raw);\n if (\n url.protocol !== \"https:\" ||\n url.username ||\n url.password ||\n !url.hostname\n ) {\n return null;\n }\n return url.toString().replace(/\\/+$/, \"\");\n } catch {\n return null;\n }\n}\n\nfunction stripTeamsMentions(text: string): string {\n return text\n .replace(/<at\\b[^>]*>.*?<\\/at>/gis, \"\")\n .replace(/<[^>]+>/g, \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\nfunction parseTimestamp(value: unknown): number {\n const parsed = typeof value === \"string\" ? Date.parse(value) : Number.NaN;\n return Number.isFinite(parsed) ? parsed : Date.now();\n}\n\nfunction readString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value.trim() : undefined;\n}\n"]}
1
+ {"version":3,"file":"microsoft-teams.js","sourceRoot":"","sources":["../../../src/integrations/adapters/microsoft-teams.ts"],"names":[],"mappings":"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","sourcesContent":["import type { H3Event } from \"h3\";\nimport { getHeader, readRawBody } from \"h3\";\n\nimport type { EnvKeyConfig } from \"../../server/create-server.js\";\nimport { resolveSecret } from \"../../server/credential-provider.js\";\nimport type {\n IncomingMessage,\n IntegrationStatus,\n OutgoingMessage,\n PlatformAdapter,\n PlatformDeliveryReceipt,\n} from \"../types.js\";\n\nconst BOT_FRAMEWORK_SCOPE = \"https://api.botframework.com/.default\";\nconst TOKEN_REFRESH_SKEW_MS = 5 * 60 * 1_000;\n\ntype TeamsActivity = {\n id?: unknown;\n type?: unknown;\n timestamp?: unknown;\n serviceUrl?: unknown;\n channelId?: unknown;\n text?: unknown;\n replyToId?: unknown;\n from?: { id?: unknown; name?: unknown; aadObjectId?: unknown };\n recipient?: { id?: unknown; name?: unknown };\n conversation?: {\n id?: unknown;\n tenantId?: unknown;\n conversationType?: unknown;\n };\n channelData?: {\n tenant?: { id?: unknown };\n team?: { id?: unknown; name?: unknown };\n channel?: { id?: unknown; name?: unknown };\n };\n};\n\ntype TokenCacheEntry = {\n accessToken: string;\n expiresAt: number;\n};\n\nconst accessTokenCache = new Map<string, TokenCacheEntry>();\n\nexport function microsoftTeamsAdapter(): PlatformAdapter {\n return {\n platform: \"microsoft-teams\",\n label: \"Microsoft Teams\",\n capabilities: {\n replyText: true,\n proactiveMessages: false,\n nativeThreads: true,\n contextualReplies: true,\n deferredWebhookResponse: false,\n interactionOnly: false,\n },\n\n getRequiredEnvKeys(): EnvKeyConfig[] {\n return [\n {\n key: \"MICROSOFT_TEAMS_APP_ID\",\n label: \"Microsoft Bot App ID\",\n required: true,\n helpText:\n \"The Microsoft App ID associated with the Azure Bot resource.\",\n },\n {\n key: \"MICROSOFT_TEAMS_APP_PASSWORD\",\n label: \"Microsoft Bot Client Secret\",\n required: true,\n helpText:\n \"A current client secret for the bot app registration. Store the secret value, not its secret ID.\",\n },\n {\n key: \"MICROSOFT_TEAMS_APP_TENANT_ID\",\n label: \"Microsoft Bot Tenant ID\",\n required: false,\n helpText:\n \"Required for single-tenant bot registrations. Multi-tenant Bot Framework apps use botframework.com.\",\n },\n {\n key: \"MICROSOFT_TEAMS_ALLOWED_TENANT_IDS\",\n label: \"Allowed Microsoft Teams Tenant IDs\",\n required: true,\n helpText:\n \"Comma-separated tenant IDs allowed to invoke this deployment. Production webhooks fail closed when unset.\",\n },\n ];\n },\n\n async handleVerification(event: H3Event) {\n await readRawBodyCached(event);\n return { handled: false };\n },\n\n async verifyWebhook(event: H3Event): Promise<boolean> {\n const appId = await resolveSecret(\"MICROSOFT_TEAMS_APP_ID\");\n const appPassword = await resolveSecret(\"MICROSOFT_TEAMS_APP_PASSWORD\");\n const authorization = getHeader(event, \"authorization\") ?? \"\";\n const activity = parseActivity(await readRawBodyCached(event));\n if (\n !appId ||\n !appPassword ||\n !authorization.startsWith(\"Bearer \") ||\n !activity ||\n activity.channelId !== \"msteams\" ||\n !readString(activity.serviceUrl)\n ) {\n return false;\n }\n\n try {\n const { JwtTokenValidation, SimpleCredentialProvider } =\n await import(\"botframework-connector\");\n await JwtTokenValidation.authenticateRequest(\n activity as never,\n authorization,\n new SimpleCredentialProvider(appId, appPassword),\n \"\",\n );\n } catch {\n return false;\n }\n\n const tenantId = teamsTenantId(activity);\n return isAllowedTenant(\n tenantId,\n await resolveSecret(\"MICROSOFT_TEAMS_ALLOWED_TENANT_IDS\"),\n );\n },\n\n async parseIncomingMessage(\n event: H3Event,\n ): Promise<IncomingMessage | null> {\n const activity = parseActivity(await readRawBodyCached(event));\n if (\n !activity ||\n activity.channelId !== \"msteams\" ||\n activity.type !== \"message\"\n ) {\n return null;\n }\n\n const activityId = readString(activity.id);\n const conversationId = readString(activity.conversation?.id);\n const serviceUrl = normalizeServiceUrl(activity.serviceUrl);\n const tenantId = teamsTenantId(activity);\n const teamId = readString(activity.channelData?.team?.id);\n const channelId = readString(activity.channelData?.channel?.id);\n const senderId = readString(\n activity.from?.aadObjectId ?? activity.from?.id,\n );\n const text = stripTeamsMentions(readString(activity.text) ?? \"\");\n if (\n !activityId ||\n !conversationId ||\n !serviceUrl ||\n !tenantId ||\n !senderId ||\n !text\n ) {\n return null;\n }\n\n const location = teamId\n ? `:team:${teamId}:channel:${channelId ?? conversationId}`\n : \"\";\n const replyRef = readString(activity.replyToId) ?? activityId;\n\n return {\n platform: \"microsoft-teams\",\n externalThreadId: `tenant:${tenantId}${location}:conversation:${conversationId}`,\n text,\n senderName: readString(activity.from?.name),\n senderId,\n threadRef: conversationId,\n replyRef,\n platformContext: {\n tenantId,\n teamId,\n channelId,\n conversationId,\n conversationType: readString(activity.conversation?.conversationType),\n botFrameworkChannelId: readString(activity.channelId),\n activityId,\n replyToId: replyRef,\n serviceUrl,\n from: {\n id: readString(activity.from?.id),\n name: readString(activity.from?.name),\n },\n recipient: {\n id: readString(activity.recipient?.id),\n name: readString(activity.recipient?.name),\n },\n },\n timestamp: parseTimestamp(activity.timestamp),\n };\n },\n\n async sendResponse(\n message: OutgoingMessage,\n context: IncomingMessage,\n ): Promise<void | PlatformDeliveryReceipt> {\n const serviceUrl = normalizeServiceUrl(\n context.platformContext.serviceUrl,\n );\n const conversationId = readString(context.platformContext.conversationId);\n const activityId =\n context.replyRef ??\n readString(context.platformContext.activityId) ??\n readString(context.platformContext.replyToId);\n if (!serviceUrl || !conversationId || !activityId) {\n throw new Error(\"Microsoft Teams conversation reference is incomplete\");\n }\n\n const accessToken = await getMicrosoftTeamsAccessToken();\n const response = await fetch(\n `${serviceUrl}/v3/conversations/${encodeURIComponent(conversationId)}/activities/${encodeURIComponent(activityId)}`,\n {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${accessToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n type: \"message\",\n text: message.text,\n replyToId: activityId,\n channelId:\n readString(context.platformContext.botFrameworkChannelId) ??\n \"msteams\",\n serviceUrl,\n conversation: { id: conversationId },\n from: context.platformContext.recipient,\n recipient: context.platformContext.from,\n }),\n },\n );\n if (!response.ok) {\n throw new Error(\n `Microsoft Teams reply failed (HTTP ${response.status})`,\n );\n }\n return { status: \"delivered\" };\n },\n\n formatAgentResponse(text: string): OutgoingMessage {\n return { text, platformContext: {} };\n },\n\n async getStatus(): Promise<IntegrationStatus> {\n const hasAppId = !!(await resolveSecret(\"MICROSOFT_TEAMS_APP_ID\"));\n const hasAppPassword = !!(await resolveSecret(\n \"MICROSOFT_TEAMS_APP_PASSWORD\",\n ));\n const allowedTenants = await resolveSecret(\n \"MICROSOFT_TEAMS_ALLOWED_TENANT_IDS\",\n );\n const hasAllowedTenants = parseCsv(allowedTenants).size > 0;\n const configured =\n hasAppId &&\n hasAppPassword &&\n (process.env.NODE_ENV !== \"production\" || hasAllowedTenants);\n return {\n platform: \"microsoft-teams\",\n label: \"Microsoft Teams\",\n enabled: false,\n configured,\n details: {\n hasAppId,\n hasAppPassword,\n hasAllowedTenants,\n validation: \"botframework-connector\",\n },\n error: !configured\n ? \"Save the Microsoft Bot App ID, client secret, and production tenant allowlist in settings\"\n : undefined,\n };\n },\n };\n}\n\nexport async function getMicrosoftTeamsAccessToken(): Promise<string> {\n const appId = await resolveSecret(\"MICROSOFT_TEAMS_APP_ID\");\n const appPassword = await resolveSecret(\"MICROSOFT_TEAMS_APP_PASSWORD\");\n const tenantId =\n (await resolveSecret(\"MICROSOFT_TEAMS_APP_TENANT_ID\")) ??\n \"botframework.com\";\n if (!appId || !appPassword) {\n throw new Error(\"Microsoft Teams bot credentials are not configured\");\n }\n\n const cacheKey = `${tenantId}:${appId}`;\n const cached = accessTokenCache.get(cacheKey);\n if (cached && cached.expiresAt - TOKEN_REFRESH_SKEW_MS > Date.now()) {\n return cached.accessToken;\n }\n\n const response = await fetch(\n `https://login.microsoftonline.com/${encodeURIComponent(tenantId)}/oauth2/v2.0/token`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n grant_type: \"client_credentials\",\n client_id: appId,\n client_secret: appPassword,\n scope: BOT_FRAMEWORK_SCOPE,\n }),\n },\n );\n const body = (await response.json().catch(() => null)) as {\n access_token?: unknown;\n expires_in?: unknown;\n } | null;\n const accessToken = readString(body?.access_token);\n const expiresIn = Number(body?.expires_in);\n if (!response.ok || !accessToken || !Number.isFinite(expiresIn)) {\n throw new Error(\n `Microsoft Teams token request failed (HTTP ${response.status})`,\n );\n }\n\n accessTokenCache.set(cacheKey, {\n accessToken,\n expiresAt: Date.now() + Math.max(60, expiresIn) * 1_000,\n });\n return accessToken;\n}\n\nexport function clearMicrosoftTeamsAccessTokenCache(): void {\n accessTokenCache.clear();\n}\n\nasync function readRawBodyCached(event: H3Event): Promise<string> {\n const cached = event.context.__rawBody;\n if (typeof cached === \"string\") return cached;\n const raw = (await readRawBody(event)) ?? \"\";\n event.context.__rawBody = raw;\n return raw;\n}\n\nfunction parseActivity(raw: string): TeamsActivity | null {\n try {\n const value = JSON.parse(raw);\n return value && typeof value === \"object\" ? (value as TeamsActivity) : null;\n } catch {\n return null;\n }\n}\n\nfunction teamsTenantId(activity: TeamsActivity): string | undefined {\n return readString(\n activity.channelData?.tenant?.id ?? activity.conversation?.tenantId,\n );\n}\n\nfunction isAllowedTenant(\n tenantId: string | undefined,\n allowlist: string | null | undefined,\n): boolean {\n if (!tenantId) return false;\n const allowed = parseCsv(allowlist);\n if (allowed.size === 0) return process.env.NODE_ENV !== \"production\";\n return allowed.has(tenantId);\n}\n\nfunction parseCsv(value: string | null | undefined): Set<string> {\n return new Set(\n (value ?? \"\")\n .split(\",\")\n .map((entry) => entry.trim())\n .filter(Boolean),\n );\n}\n\nfunction normalizeServiceUrl(value: unknown): string | null {\n const raw = readString(value);\n if (!raw) return null;\n try {\n const url = new URL(raw);\n if (\n url.protocol !== \"https:\" ||\n url.username ||\n url.password ||\n !url.hostname\n ) {\n return null;\n }\n return url.toString().replace(/\\/+$/, \"\");\n } catch {\n return null;\n }\n}\n\nfunction stripTeamsMentions(text: string): string {\n return text\n .replace(/<at\\b[^>]*>.*?<\\/at>/gis, \"\")\n .replace(/<[^>]+>/g, \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\nfunction parseTimestamp(value: unknown): number {\n const parsed = typeof value === \"string\" ? Date.parse(value) : Number.NaN;\n return Number.isFinite(parsed) ? parsed : Date.now();\n}\n\nfunction readString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value.trim() : undefined;\n}\n"]}