@agent-native/core 0.7.21 → 0.7.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.js +43 -1
- package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
- package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
- package/dist/agent/engine/anthropic-engine.js +8 -0
- package/dist/agent/engine/anthropic-engine.js.map +1 -1
- package/dist/agent/engine/builder-engine.d.ts +1 -1
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +9 -4
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/agent/engine/translate-ai-sdk.d.ts.map +1 -1
- package/dist/agent/engine/translate-ai-sdk.js +31 -1
- package/dist/agent/engine/translate-ai-sdk.js.map +1 -1
- package/dist/agent/engine/translate-anthropic.d.ts.map +1 -1
- package/dist/agent/engine/translate-anthropic.js +16 -0
- package/dist/agent/engine/translate-anthropic.js.map +1 -1
- package/dist/agent/engine/types.d.ts +16 -1
- package/dist/agent/engine/types.d.ts.map +1 -1
- package/dist/agent/engine/types.js.map +1 -1
- package/dist/agent/production-agent.d.ts +4 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +96 -4
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/types.d.ts +3 -0
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/cli/create.d.ts +2 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +21 -14
- package/dist/cli/create.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +5 -5
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +5 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +54 -2
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +33 -2
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/NewWorkspaceAppFlow.d.ts.map +1 -1
- package/dist/client/NewWorkspaceAppFlow.js +10 -1
- package/dist/client/NewWorkspaceAppFlow.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts +4 -0
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +5 -1
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts +6 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +25 -17
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/composer/useVoiceDictation.d.ts +6 -5
- package/dist/client/composer/useVoiceDictation.d.ts.map +1 -1
- package/dist/client/composer/useVoiceDictation.js +54 -21
- package/dist/client/composer/useVoiceDictation.js.map +1 -1
- package/dist/client/org/OrgSwitcher.d.ts +3 -1
- package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
- package/dist/client/org/OrgSwitcher.js +12 -7
- package/dist/client/org/OrgSwitcher.js.map +1 -1
- package/dist/client/settings/AutomationsSection.d.ts.map +1 -1
- package/dist/client/settings/AutomationsSection.js +2 -2
- package/dist/client/settings/AutomationsSection.js.map +1 -1
- package/dist/client/settings/VoiceTranscriptionSection.d.ts.map +1 -1
- package/dist/client/settings/VoiceTranscriptionSection.js +46 -15
- package/dist/client/settings/VoiceTranscriptionSection.js.map +1 -1
- package/dist/client/tools/ToolViewer.d.ts.map +1 -1
- package/dist/client/tools/ToolViewer.js +2 -2
- package/dist/client/tools/ToolViewer.js.map +1 -1
- package/dist/client/tools/ToolsListPage.d.ts.map +1 -1
- package/dist/client/tools/ToolsListPage.js +4 -4
- package/dist/client/tools/ToolsListPage.js.map +1 -1
- package/dist/client/tools/ToolsSidebarSection.d.ts.map +1 -1
- package/dist/client/tools/ToolsSidebarSection.js +2 -2
- package/dist/client/tools/ToolsSidebarSection.js.map +1 -1
- package/dist/client/transcription/use-live-transcription.d.ts +1 -0
- package/dist/client/transcription/use-live-transcription.d.ts.map +1 -1
- package/dist/client/transcription/use-live-transcription.js +41 -0
- package/dist/client/transcription/use-live-transcription.js.map +1 -1
- package/dist/integrations/adapters/email.js +81 -5
- package/dist/integrations/adapters/email.js.map +1 -1
- package/dist/integrations/adapters/slack.d.ts.map +1 -1
- package/dist/integrations/adapters/slack.js +4 -1
- package/dist/integrations/adapters/slack.js.map +1 -1
- package/dist/integrations/plugin.d.ts.map +1 -1
- package/dist/integrations/plugin.js +2 -1
- package/dist/integrations/plugin.js.map +1 -1
- package/dist/integrations/types.d.ts +2 -0
- package/dist/integrations/types.d.ts.map +1 -1
- package/dist/integrations/types.js.map +1 -1
- package/dist/integrations/webhook-handler.js +12 -2
- package/dist/integrations/webhook-handler.js.map +1 -1
- package/dist/oauth-tokens/store.d.ts.map +1 -1
- package/dist/oauth-tokens/store.js +34 -16
- package/dist/oauth-tokens/store.js.map +1 -1
- package/dist/scripts/db/exec.d.ts.map +1 -1
- package/dist/scripts/db/exec.js +32 -23
- package/dist/scripts/db/exec.js.map +1 -1
- package/dist/scripts/db/patch.d.ts.map +1 -1
- package/dist/scripts/db/patch.js +48 -35
- package/dist/scripts/db/patch.js.map +1 -1
- package/dist/scripts/db/query.d.ts.map +1 -1
- package/dist/scripts/db/query.js +22 -13
- package/dist/scripts/db/query.js.map +1 -1
- package/dist/scripts/db/safety.d.ts +2 -0
- package/dist/scripts/db/safety.d.ts.map +1 -0
- package/dist/scripts/db/safety.js +67 -0
- package/dist/scripts/db/safety.js.map +1 -0
- package/dist/scripts/db/scoping.js +4 -4
- package/dist/scripts/db/scoping.js.map +1 -1
- package/dist/server/email-template.d.ts +5 -0
- package/dist/server/email-template.d.ts.map +1 -1
- package/dist/server/email-template.js +7 -4
- package/dist/server/email-template.js.map +1 -1
- package/dist/server/google-auth-plugin.d.ts.map +1 -1
- package/dist/server/google-auth-plugin.js +1 -8
- package/dist/server/google-auth-plugin.js.map +1 -1
- package/dist/server/index.d.ts +3 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +3 -2
- package/dist/server/index.js.map +1 -1
- package/dist/server/onboarding-html.d.ts.map +1 -1
- package/dist/server/onboarding-html.js +3 -10
- package/dist/server/onboarding-html.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +9 -9
- package/dist/server/transcribe-voice.d.ts.map +1 -1
- package/dist/server/transcribe-voice.js +405 -51
- package/dist/server/transcribe-voice.js.map +1 -1
- package/dist/server/voice-providers-status.d.ts.map +1 -1
- package/dist/server/voice-providers-status.js +13 -1
- package/dist/server/voice-providers-status.js.map +1 -1
- package/dist/settings/store.d.ts.map +1 -1
- package/dist/settings/store.js +14 -6
- package/dist/settings/store.js.map +1 -1
- package/dist/shared/reasoning-effort.d.ts +8 -0
- package/dist/shared/reasoning-effort.d.ts.map +1 -0
- package/dist/shared/reasoning-effort.js +94 -0
- package/dist/shared/reasoning-effort.js.map +1 -0
- package/dist/templates/default/public/favicon.svg +1 -13
- package/dist/templates/default/public/icon-180.svg +1 -13
- package/dist/templates/default/public/icon-192.svg +1 -13
- package/dist/templates/default/public/icon-512.svg +1 -13
- package/dist/templates/workspace-root/.github/workflows/ci.yml +32 -0
- package/dist/templates/workspace-root/.prettierignore +19 -0
- package/dist/templates/workspace-root/_gitignore +5 -0
- package/dist/templates/workspace-root/package.json +13 -2
- package/dist/templates/workspace-root/pnpm-workspace.yaml +1 -0
- package/dist/templates/workspace-root/scripts/workspace-dev.ts +2 -0
- package/dist/transcription/builder-transcription.d.ts +2 -0
- package/dist/transcription/builder-transcription.d.ts.map +1 -1
- package/dist/transcription/builder-transcription.js +4 -0
- package/dist/transcription/builder-transcription.js.map +1 -1
- package/docs/content/voice-input.md +14 -13
- package/package.json +1 -1
- package/src/templates/default/public/favicon.svg +1 -13
- package/src/templates/default/public/icon-180.svg +1 -13
- package/src/templates/default/public/icon-192.svg +1 -13
- package/src/templates/default/public/icon-512.svg +1 -13
- package/src/templates/workspace-root/.github/workflows/ci.yml +32 -0
- package/src/templates/workspace-root/.prettierignore +19 -0
- package/src/templates/workspace-root/_gitignore +5 -0
- package/src/templates/workspace-root/package.json +13 -2
- package/src/templates/workspace-root/pnpm-workspace.yaml +1 -0
- package/src/templates/workspace-root/scripts/workspace-dev.ts +2 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email.js","sourceRoot":"","sources":["../../../src/integrations/adapters/email.ts"],"names":[],"mappings":"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","sourcesContent":["import type { H3Event } from \"h3\";\nimport { getHeader, readRawBody as h3ReadRawBody } from \"h3\";\nimport { timingSafeEqual } from \"node:crypto\";\nimport type {\n PlatformAdapter,\n IncomingMessage,\n OutgoingMessage,\n IntegrationStatus,\n OutboundTarget,\n} from \"../types.js\";\nimport type { EnvKeyConfig } from \"../../server/create-server.js\";\nimport { getIntegrationConfig } from \"../config-store.js\";\nimport { getDbExec } from \"../../db/client.js\";\nimport {\n sendEmail,\n isEmailConfigured,\n getEmailProvider,\n} from \"../../server/email.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\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 = process.env.EMAIL_INBOUND_WEBHOOK_SECRET;\n const provider = 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 = getEmailProvider();\n const agentAddress = process.env.EMAIL_AGENT_ADDRESS?.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 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 },\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 = process.env.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 fromAddress = process.env.EMAIL_FROM\n ? process.env.EMAIL_FROM\n : `${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)\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 = process.env.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 = !!process.env.EMAIL_AGENT_ADDRESS;\n const hasEmailProvider = isEmailConfigured();\n const hasWebhookSecret = !!process.env.EMAIL_INBOUND_WEBHOOK_SECRET;\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: getEmailProvider(),\n },\n error: !configured\n ? \"Set EMAIL_AGENT_ADDRESS and either RESEND_API_KEY or SENDGRID_API_KEY\"\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\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 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 };\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 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 };\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 ?\n `,\n args: [\"email\", cutoff, `%\"senderId\":\"${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(context: IncomingMessage): string[] | undefined {\n const agentAddress = process.env.EMAIL_AGENT_ADDRESS?.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 !== agentAddress && 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(/ /g, \" \")\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\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 = html\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\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(\n /\\[([^\\]]+)\\]\\(([^)]+)\\)/g,\n '<a href=\"$2\" style=\"color:#2563eb;text-decoration:underline;\">$1</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 // 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":"AACA,OAAO,EAAE,SAAS,EAAE,WAAW,IAAI,aAAa,EAAE,MAAM,IAAI,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAS9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EACL,SAAS,EACT,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,uBAAuB,CAAC;AAE/B,wCAAwC;AACxC,MAAM,qBAAqB,GAAG,KAAK,CAAC;AAEpC,0DAA0D;AAC1D,MAAM,cAAc,GAAG,EAAE,CAAC;AAC1B,uCAAuC;AACvC,MAAM,oBAAoB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAE5C;;;;;GAKG;AACH,IAAI,uBAAuB,GAAG,KAAK,CAAC;AACpC,IAAI,yBAAyB,GAAG,KAAK,CAAC;AAEtC;;;;;;;GAOG;AACH,SAAS,6BAA6B;IACpC,IAAI,OAAO,CAAC,GAAG,CAAC,sCAAsC,KAAK,GAAG;QAAE,OAAO,KAAK,CAAC;IAC7E,OAAO,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,CAAC;AAC/C,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,YAAY;IAC1B,OAAO;QACL,QAAQ,EAAE,OAAO;QACjB,KAAK,EAAE,OAAO;QAEd,kBAAkB;YAChB,OAAO;gBACL;oBACE,GAAG,EAAE,qBAAqB;oBAC1B,KAAK,EAAE,qBAAqB;oBAC5B,QAAQ,EAAE,IAAI;oBACd,QAAQ,EACN,wIAAwI;iBAC3I;gBACD;oBACE,GAAG,EAAE,gBAAgB;oBACrB,KAAK,EAAE,gBAAgB;oBACvB,QAAQ,EAAE,KAAK;oBACf,QAAQ,EACN,uHAAuH;iBAC1H;gBACD;oBACE,GAAG,EAAE,kBAAkB;oBACvB,KAAK,EAAE,kBAAkB;oBACzB,QAAQ,EAAE,KAAK;oBACf,QAAQ,EACN,qGAAqG;iBACxG;gBACD;oBACE,GAAG,EAAE,8BAA8B;oBACnC,KAAK,EAAE,wBAAwB;oBAC/B,QAAQ,EAAE,KAAK;oBACf,QAAQ,EACN,uKAAuK;iBAC1K;aACF,CAAC;QACJ,CAAC;QAED,KAAK,CAAC,kBAAkB,CACtB,MAAe;YAEf,iDAAiD;YACjD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAC5B,CAAC;QAED,KAAK,CAAC,aAAa,CAAC,KAAc;YAChC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC;YACxD,MAAM,QAAQ,GAAG,gBAAgB,EAAE,CAAC;YAEpC,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBAC1B,OAAO,mBAAmB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YAC5C,CAAC;YAED,IAAI,QAAQ,KAAK,UAAU,EAAE,CAAC;gBAC5B,OAAO,qBAAqB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YAC9C,CAAC;YAED,kCAAkC;YAClC,OAAO,CAAC,IAAI,CAAC,yDAAyD,CAAC,CAAC;YACxE,OAAO,KAAK,CAAC;QACf,CAAC;QAED,KAAK,CAAC,oBAAoB,CACxB,KAAc;YAEd,MAAM,QAAQ,GAAG,gBAAgB,EAAE,CAAC;YACpC,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,WAAW,EAAE,CAAC;YACpE,IAAI,CAAC,YAAY,EAAE,CAAC;gBAClB,OAAO,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;gBAC3D,OAAO,IAAI,CAAC;YACd,CAAC;YAED,IAAI,MAAM,GAAuB,IAAI,CAAC;YAEtC,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBAC1B,MAAM,GAAG,MAAM,kBAAkB,CAAC,KAAK,CAAC,CAAC;YAC3C,CAAC;iBAAM,IAAI,QAAQ,KAAK,UAAU,EAAE,CAAC;gBACnC,MAAM,GAAG,MAAM,oBAAoB,CAAC,KAAK,CAAC,CAAC;YAC7C,CAAC;YAED,IAAI,CAAC,MAAM;gBAAE,OAAO,IAAI,CAAC;YAEzB,2EAA2E;YAC3E,yEAAyE;YACzE,gEAAgE;YAChE,yEAAyE;YACzE,0DAA0D;YAC1D,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;YACpD,IAAI,MAAM,aAAa,CAAC,WAAW,CAAC,EAAE,CAAC;gBACrC,OAAO,CAAC,IAAI,CACV,gCAAgC,WAAW,MAAM,cAAc,MAAM,CACtE,CAAC;gBACF,OAAO,IAAI,CAAC;YACd,CAAC;YAED,wBAAwB;YACxB,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC,OAAO,CAAC,CAAC;YACnD,IAAI,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,CAAC;gBACvC,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,cAA0B,CAAC;gBAC7D,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACvB,MAAM,YAAY,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;oBAC/C,IAAI,CAAC,YAAY,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;wBACrD,OAAO,CAAC,IAAI,CACV,+BAA+B,WAAW,gCAAgC,CAC3E,CAAC;wBACF,OAAO,IAAI,CAAC;oBACd,CAAC;gBACH,CAAC;YACH,CAAC;YAED,qDAAqD;YACrD,MAAM,WAAW,GAAG,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;YAC1D,MAAM,WAAW,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;YAClE,MAAM,IAAI,GACR,CAAC,WAAW,CAAC,QAAQ,CAAC,YAAY,CAAC;gBACnC,WAAW,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;YAErC,yFAAyF;YACzF,+EAA+E;YAC/E,8EAA8E;YAC9E,yEAAyE;YACzE,gFAAgF;YAChF,6EAA6E;YAC7E,6CAA6C;YAC7C,MAAM,YAAY,GAAG,qBAAqB,CACxC,eAAe,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,UAAU,CAAC,EACpD,WAAW,CACZ,CAAC;YAEF,kBAAkB;YAClB,IAAI,QAAQ,GAAG,MAAM,CAAC,IAAI,IAAI,qBAAqB,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;YAEvE,qBAAqB;YACrB,IAAI,QAAQ,CAAC,MAAM,GAAG,qBAAqB,EAAE,CAAC;gBAC5C,QAAQ;oBACN,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,uBAAuB,CAAC;YACvE,CAAC;YAED,kCAAkC;YAClC,IAAI,IAAI,EAAE,CAAC;gBACT,MAAM,eAAe,GAAG,WAAW;qBAChC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,YAAY,CAAC;qBACjC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACd,QAAQ;oBACN,0BAA0B,WAAW,QAAQ,eAAe,IAAI,QAAQ,KAAK;wBAC7E,YAAY,MAAM,CAAC,OAAO,MAAM;wBAChC,QAAQ,CAAC;YACb,CAAC;YAED,OAAO;gBACL,QAAQ,EAAE,OAAO;gBACjB,gBAAgB,EAAE,YAAY;gBAC9B,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI;gBAC5B,QAAQ,EAAE,WAAW;gBACrB,eAAe,EAAE;oBACf,SAAS,EAAE,MAAM,CAAC,SAAS;oBAC3B,OAAO,EAAE,MAAM,CAAC,OAAO;oBACvB,IAAI,EAAE,WAAW;oBACjB,EAAE,EAAE,MAAM,CAAC,EAAE;oBACb,EAAE,EAAE,MAAM,CAAC,EAAE;oBACb,SAAS,EAAE,MAAM,CAAC,SAAS;oBAC3B,UAAU,EAAE,MAAM,CAAC,UAAU;oBAC7B,IAAI;iBACL;gBACD,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE;aACtE,CAAC;QACJ,CAAC;QAED,KAAK,CAAC,YAAY,CAChB,OAAwB,EACxB,OAAwB;YAExB,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;YACrD,IAAI,CAAC,YAAY,EAAE,CAAC;gBAClB,OAAO,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;gBAC5D,OAAO;YACT,CAAC;YAED,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC,OAAO,CAAC,CAAC;YACnD,MAAM,WAAW,GACd,MAAM,EAAE,UAAU,EAAE,WAAsB,IAAI,gBAAgB,CAAC;YAElE,sEAAsE;YACtE,mEAAmE;YACnE,4DAA4D;YAC5D,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU;gBACxC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU;gBACxB,CAAC,CAAC,GAAG,WAAW,KAAK,YAAY,GAAG,CAAC;YAEvC,MAAM,OAAO,GAAG,OAAO,CAAC,eAAe,CAAC,OAAiB,CAAC;YAC1D,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,OAAO,EAAE,CAAC;YAE1E,IAAI,CAAC;gBACH,MAAM,SAAS,CAAC;oBACd,EAAE,EAAE,OAAO,CAAC,QAAS;oBACrB,IAAI,EAAE,WAAW;oBACjB,OAAO,EAAE,SAAS;oBAClB,IAAI,EAAE,OAAO,CAAC,IAAI;oBAClB,IAAI,EAAE,qBAAqB,CAAC,OAAO,CAAC,IAAI,CAAC;oBACzC,SAAS,EAAE,OAAO,CAAC,eAAe,CAAC,SAAmB;oBACtD,UAAU,EAAE,qBAAqB,CAAC,OAAO,CAAC,eAAe,CAAC;oBAC1D,EAAE,EAAE,OAAO,CAAC,eAAe,CAAC,IAAI;wBAC9B,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC;wBAC1B,CAAC,CAAC,SAAS;iBACd,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,kCAAkC,EAAE,GAAG,CAAC,CAAC;YACzD,CAAC;QACH,CAAC;QAED,KAAK,CAAC,mBAAmB,CACvB,OAAwB,EACxB,MAAsB;YAEtB,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;YACrD,IAAI,CAAC,YAAY,EAAE,CAAC;gBAClB,OAAO,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;gBAC5D,OAAO;YACT,CAAC;YAED,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC,OAAO,CAAC,CAAC;YACnD,MAAM,WAAW,GACd,MAAM,EAAE,UAAU,EAAE,WAAsB,IAAI,gBAAgB,CAAC;YAElE,IAAI,CAAC;gBACH,MAAM,SAAS,CAAC;oBACd,EAAE,EAAE,MAAM,CAAC,WAAW;oBACtB,IAAI,EAAE,GAAG,WAAW,KAAK,YAAY,GAAG;oBACxC,OAAO,EAAE,MAAM,CAAC,KAAK,IAAI,6BAA6B;oBACtD,IAAI,EAAE,OAAO,CAAC,IAAI;oBAClB,IAAI,EAAE,qBAAqB,CAAC,OAAO,CAAC,IAAI,CAAC;oBACzC,GAAG,CAAC,MAAM,CAAC,SAAS;wBAClB,CAAC,CAAC;4BACE,SAAS,EAAE,MAAM,CAAC,SAAS;4BAC3B,UAAU,EAAE,MAAM,CAAC,SAAS;yBAC7B;wBACH,CAAC,CAAC,EAAE,CAAC;iBACR,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,2CAA2C,EAAE,GAAG,CAAC,CAAC;gBAChE,MAAM,GAAG,CAAC;YACZ,CAAC;QACH,CAAC;QAED,mBAAmB,CAAC,IAAY;YAC9B,MAAM,QAAQ,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;YACtC,MAAM,IAAI,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;YAC3C,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,eAAe,EAAE,EAAE,EAAE,CAAC;QAC7C,CAAC;QAED,KAAK,CAAC,SAAS,CAAC,QAAiB;YAC/B,MAAM,eAAe,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;YAC1D,MAAM,gBAAgB,GAAG,iBAAiB,EAAE,CAAC;YAC7C,MAAM,gBAAgB,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC;YACpE,MAAM,UAAU,GAAG,eAAe,IAAI,gBAAgB,CAAC;YAEvD,OAAO;gBACL,QAAQ,EAAE,OAAO;gBACjB,KAAK,EAAE,OAAO;gBACd,OAAO,EAAE,KAAK,EAAE,uBAAuB;gBACvC,UAAU;gBACV,OAAO,EAAE;oBACP,eAAe;oBACf,gBAAgB;oBAChB,gBAAgB;oBAChB,QAAQ,EAAE,gBAAgB,EAAE;iBAC7B;gBACD,KAAK,EAAE,CAAC,UAAU;oBAChB,CAAC,CAAC,uEAAuE;oBACzE,CAAC,CAAC,SAAS;aACd,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC;AAmBD,8EAA8E;AAC9E,uBAAuB;AACvB,8EAA8E;AAE9E,KAAK,UAAU,mBAAmB,CAChC,KAAc,EACd,MAAe;IAEf,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,IAAI,6BAA6B,EAAE,EAAE,CAAC;YACpC,IAAI,CAAC,uBAAuB,EAAE,CAAC;gBAC7B,uBAAuB,GAAG,IAAI,CAAC;gBAC/B,OAAO,CAAC,KAAK,CACX,wFAAwF;oBACtF,2GAA2G,CAC9G,CAAC;YACJ,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,CAAC,uBAAuB,EAAE,CAAC;YAC7B,uBAAuB,GAAG,IAAI,CAAC;YAC/B,OAAO,CAAC,IAAI,CACV,yGAAyG,CAC1G,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IAC3C,MAAM,aAAa,GAAG,SAAS,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC;IACzD,MAAM,aAAa,GAAG,SAAS,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC;IAEzD,IAAI,CAAC,MAAM,IAAI,CAAC,aAAa,IAAI,CAAC,aAAa,EAAE,CAAC;QAChD,OAAO,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;QACvD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,2DAA2D;IAC3D,MAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;IACvC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC;QAC3C,OAAO,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAC;QAC1D,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,KAAK,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;IAE3C,oDAAoD;IACpD,MAAM,SAAS,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACzE,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAErD,MAAM,aAAa,GAAG,GAAG,MAAM,IAAI,aAAa,IAAI,IAAI,EAAE,CAAC;IAC3D,MAAM,iBAAiB,GAAG,MAAM;SAC7B,UAAU,CAAC,QAAQ,EAAE,WAAW,CAAC;SACjC,MAAM,CAAC,aAAa,CAAC;SACrB,MAAM,CAAC,QAAQ,CAAC,CAAC;IAEpB,+EAA+E;IAC/E,MAAM,UAAU,GAAG,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5C,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;QAC7B,MAAM,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QAC5D,IAAI,CAAC;YACH,IACE,MAAM,CAAC,eAAe,CACpB,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,EAC9B,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CACtB,EACD,CAAC;gBACD,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,uCAAuC;QACzC,CAAC;IACH,CAAC;IAED,OAAO,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;IAC3D,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,MAAM,CAAC,CAAS,EAAE,CAAS;IAClC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5B,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC9C,OAAO,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AACrC,CAAC;AAED,KAAK,UAAU,qBAAqB,CAClC,KAAc,EACd,MAAe;IAEf,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,IAAI,6BAA6B,EAAE,EAAE,CAAC;YACpC,IAAI,CAAC,yBAAyB,EAAE,CAAC;gBAC/B,yBAAyB,GAAG,IAAI,CAAC;gBACjC,OAAO,CAAC,KAAK,CACX,0FAA0F;oBACxF,2GAA2G,CAC9G,CAAC;YACJ,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,CAAC,yBAAyB,EAAE,CAAC;YAC/B,yBAAyB,GAAG,IAAI,CAAC;YACjC,OAAO,CAAC,IAAI,CACV,2GAA2G,CAC5G,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,wDAAwD;IACxD,MAAM,UAAU,GAAG,SAAS,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC;IACrD,IAAI,UAAU,EAAE,CAAC;QACf,iEAAiE;QACjE,IAAI,UAAU,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YACpC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC;YACtE,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YACvC,IAAI,QAAQ,KAAK,SAAS,IAAI,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAAE,OAAO,IAAI,CAAC;QACtE,CAAC;IACH,CAAC;IAED,qEAAqE;IACrE,MAAM,YAAY,GAAG,SAAS,CAAC,KAAK,EAAE,kBAAkB,CAAC,CAAC;IAC1D,IAAI,YAAY,KAAK,SAAS,IAAI,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IAE5E,OAAO,CAAC,IAAI,CAAC,qDAAqD,CAAC,CAAC;IACpE,OAAO,KAAK,CAAC;AACf,CAAC;AAED,8EAA8E;AAC9E,wBAAwB;AACxB,8EAA8E;AAE9E,KAAK,UAAU,kBAAkB,CAAC,KAAc;IAC9C,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,KAAK,CAAC,CAAC;IACrC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,gBAAgB;QAAE,OAAO,IAAI,CAAC;IAEzD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IACvB,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAEvB,kEAAkE;IAClE,iEAAiE;IACjE,MAAM,OAAO,GAAG,IAAI,CAAC,IAA0B,CAAC;IAChD,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACzD,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAEvB,MAAM,KAAK,GAAG,IAAI,CAAC,EAAmC,CAAC;IACvD,MAAM,EAAE,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,IAAI,CAAC,EAAmC,CAAC;IACvD,MAAM,EAAE,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAEvC,wDAAwD;IACxD,MAAM,OAAO,GAAG,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACjD,MAAM,SAAS,GACb,OAAO,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC,QAAQ,IAAI,UAAU,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;IAEnE,OAAO;QACL,SAAS;QACT,OAAO,EAAG,IAAI,CAAC,OAAkB,IAAI,cAAc;QACnD,IAAI;QACJ,EAAE;QACF,EAAE,EAAE,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS;QAClC,IAAI,EAAE,IAAI,CAAC,IAA0B;QACrC,IAAI,EAAE,IAAI,CAAC,IAA0B;QACrC,SAAS,EAAE,OAAO,CAAC,aAAa,CAAC,IAAI,SAAS;QAC9C,UAAU,EAAE,qBAAqB,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QACxD,IAAI,EAAG,IAAI,CAAC,UAAqB,IAAI,SAAS;KAC/C,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,oBAAoB,CACjC,KAAc;IAEd,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,KAAK,CAAC,CAAC;IACrC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAEvB,sDAAsD;IACtD,uDAAuD;IACvD,MAAM,OAAO,GAAG,IAAI,CAAC,IAA0B,CAAC;IAChD,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACzD,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAEvB,MAAM,KAAK,GAAG,IAAI,CAAC,EAAwB,CAAC;IAC5C,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACtE,MAAM,KAAK,GAAG,IAAI,CAAC,EAAwB,CAAC;IAC5C,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAEtE,2BAA2B;IAC3B,MAAM,UAAU,GAAG,IAAI,CAAC,OAA6B,CAAC;IACtD,MAAM,OAAO,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,IAAI,YAAY,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;IAEpE,OAAO;QACL,SAAS;QACT,OAAO,EAAG,IAAI,CAAC,OAAkB,IAAI,cAAc;QACnD,IAAI;QACJ,EAAE;QACF,EAAE,EAAE,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS;QAClC,IAAI,EAAE,IAAI,CAAC,IAA0B;QACrC,IAAI,EAAE,IAAI,CAAC,IAA0B;QACrC,SAAS,EAAE,OAAO,CAAC,aAAa,CAAC,IAAI,SAAS;QAC9C,UAAU,EAAE,qBAAqB,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QACxD,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,SAAS;KACnC,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,gBAAgB;AAChB,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,KAAK,UAAU,aAAa,CAAC,WAAmB;IAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,oBAAoB,CAAC;IACjD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;YACpC,GAAG,EAAE;;;;;;OAMJ;YACD,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,gBAAgB,WAAW,IAAI,CAAC;SACzD,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,CACjB,IAAI,CAAC,CAAC,CAAyC,EAAE,CAAC,IAAI,CAAC,CACzD,CAAC;QACF,OAAO,KAAK,IAAI,cAAc,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,yEAAyE;QACzE,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,kCAAkC;AAClC,8EAA8E;AAE9E,kEAAkE;AAClE,SAAS,iBAAiB,CAAC,GAAW;IACpC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;IACrD,IAAI,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QACtB,OAAO;YACL,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,SAAS;YAC9D,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;SACvB,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;AAC/B,CAAC;AAED,qGAAqG;AACrG,SAAS,oBAAoB,CAAC,GAAkC;IAC9D,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAC;IACpB,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACxD,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED,8EAA8E;AAC9E,2BAA2B;AAC3B,8EAA8E;AAE9E,+EAA+E;AAC/E,SAAS,kBAAkB,CAAC,OAAgB;IAC1C,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,IAAI,CAAC,OAAO;QAAE,OAAO,MAAM,CAAC;IAE5B,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,EAAE,CAAC;gBAC9D,MAAM,CAAE,CAAC,CAAC,IAAe,CAAC,WAAW,EAAE,CAAC,GAAG,CAAC,CAAC,KAAe,CAAC;YAC/D,CAAC;QACH,CAAC;IACH,CAAC;SAAM,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QACvC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CACvC,OAAkC,CACnC,EAAE,CAAC;YACF,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,kFAAkF;AAClF,SAAS,kBAAkB,CAAC,GAAuB;IACjD,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,IAAI,CAAC,GAAG;QAAE,OAAO,MAAM,CAAC;IAExB,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACjC,IAAI,UAAU,GAAG,EAAE,CAAC;IACpB,IAAI,YAAY,GAAG,EAAE,CAAC;IAEtB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,6CAA6C;QAC7C,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,UAAU,EAAE,CAAC;YACnC,YAAY,IAAI,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAClC,SAAS;QACX,CAAC;QACD,uBAAuB;QACvB,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,GAAG,YAAY,CAAC;QAClD,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;YACjB,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;YAC5C,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACjD,CAAC;aAAM,CAAC;YACN,UAAU,GAAG,EAAE,CAAC;YAChB,YAAY,GAAG,EAAE,CAAC;QACpB,CAAC;IACH,CAAC;IACD,mBAAmB;IACnB,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,GAAG,YAAY,CAAC;IAClD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,mEAAmE;AACnE,SAAS,qBAAqB,CAC5B,UAA8B;IAE9B,IAAI,CAAC,UAAU;QAAE,OAAO,SAAS,CAAC;IAClC,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACzC,OAAO,GAAG,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AACjD,CAAC;AAED,8EAA8E;AAC9E,sBAAsB;AACtB,8EAA8E;AAE9E;;;;GAIG;AACH,SAAS,eAAe,CAAC,SAAiB,EAAE,UAAqB;IAC/D,IAAI,UAAU,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxC,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC;IACvB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,qBAAqB,CAC5B,WAAmB,EACnB,WAAmB;IAEnB,OAAO,GAAG,WAAW,CAAC,WAAW,EAAE,KAAK,WAAW,EAAE,CAAC;AACxD,CAAC;AAED,8EAA8E;AAC9E,2BAA2B;AAC3B,8EAA8E;AAE9E,0DAA0D;AAC1D,SAAS,qBAAqB,CAAC,GAA4B;IACzD,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,8BAA8B;IAC9B,MAAM,IAAI,GAAG,GAAG,CAAC,UAAkC,CAAC;IACpD,IAAI,IAAI,EAAE,CAAC;QACT,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;IACtB,CAAC;IAED,gCAAgC;IAChC,MAAM,SAAS,GAAG,GAAG,CAAC,SAA+B,CAAC;IACtD,IAAI,SAAS,EAAE,CAAC;QACd,mBAAmB;QACnB,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YAC/B,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC;AAED;;;GAGG;AACH,SAAS,eAAe,CAAC,OAAwB;IAC/C,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,WAAW,EAAE,CAAC;IACpE,MAAM,WAAW,GAAG,OAAO,CAAC,QAAQ,EAAE,WAAW,EAAE,CAAC;IACpD,MAAM,WAAW,GAAI,OAAO,CAAC,eAAe,CAAC,EAAe,IAAI,EAAE,CAAC;IACnE,MAAM,WAAW,GAAI,OAAO,CAAC,eAAe,CAAC,EAAe,IAAI,EAAE,CAAC;IAEnE,MAAM,aAAa,GAAG,IAAI,GAAG,EAAU,CAAC;IACxC,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,WAAW,EAAE,GAAG,WAAW,CAAC,EAAE,CAAC;QACpD,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;QAC7C,gEAAgE;QAChE,IAAI,UAAU,KAAK,YAAY,IAAI,UAAU,KAAK,WAAW,EAAE,CAAC;YAC9D,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED,OAAO,aAAa,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACxE,CAAC;AAED,8EAA8E;AAC9E,4BAA4B;AAC5B,8EAA8E;AAE9E,4DAA4D;AAC5D,SAAS,qBAAqB,CAAC,IAAY;IACzC,OAAO,IAAI;SACR,OAAO,CAAC,cAAc,EAAE,IAAI,CAAC;SAC7B,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC;SAC1B,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC;SAC1B,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC;SACzB,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC;SAC5B,OAAO,CAAC,cAAc,EAAE,MAAM,CAAC;SAC/B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC;SACtB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC;SACtB,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC;SAC1B,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC;SACL,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,uBAAuB,CAAC,CAAS;IACxC,OAAO,CAAC;SACL,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC;SACtB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,2BAA2B,CAAC,GAAW;IAI9C,IAAI,GAAG,GAAG,GAAG,CAAC;IACd,IAAI,QAAQ,GAAG,EAAE,CAAC;IAClB,MAAM,gBAAgB,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAE7C,SAAS,CAAC;QACR,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CACjD,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,CACxB,CAAC;QACF,IAAI,CAAC,MAAM;YAAE,MAAM;QACnB,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACnC,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC/B,CAAC;IAED,OAAO,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7B,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC;QACpC,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC;IAED,OAAO,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/C,QAAQ,GAAG,GAAG,GAAG,QAAQ,CAAC;QAC1B,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC;IAED,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC;AAC3B,CAAC;AAED,SAAS,WAAW,CAAC,MAAc;IACjC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,uBAAuB,CAAC,MAAM,CAAC,CAAC,CAAC;QACxD,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QACnD,OAAO,IAAI,CAAC,CAAC,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,WAAW,CAAC;IACrB,CAAC;AACH,CAAC;AAED,SAAS,kBAAkB,CAAC,OAAe;IACzC,OAAO,OAAO,CAAC,OAAO,CAAC,2BAA2B,EAAE,CAAC,GAAG,EAAE,EAAE;QAC1D,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,2BAA2B,CAAC,GAAG,CAAC,CAAC;QAC3D,MAAM,IAAI,GAAG,uBAAuB,CAAC,GAAG,CAAC,CAAC;QAC1C,OAAO,YAAY,UAAU,CAAC,IAAI,CAAC,sDAAsD,UAAU,CACjG,WAAW,CAAC,GAAG,CAAC,CACjB,OAAO,QAAQ,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,qBAAqB,CAAC,IAAY;IACzC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IAC1C,IAAI,SAAS,GAAG,CAAC,CAAC;IAElB,OAAO,KAAK;SACT,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACZ,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/C,IAAI,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACpC,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC;YACzC,CAAC;iBAAM,IAAI,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACzC,SAAS,IAAI,CAAC,CAAC;YACjB,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;IACzD,CAAC,CAAC;SACD,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC;AAED,yDAAyD;AACzD,SAAS,cAAc,CAAC,EAAU;IAChC,IAAI,IAAI,GAAG,EAAE,CAAC;IAEd,kEAAkE;IAClE,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAExB,6BAA6B;IAC7B,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,qBAAqB,CAAC,CAAC;IAC7D,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,qBAAqB,CAAC,CAAC;IAEzD,mEAAmE;IACnE,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,4BAA4B,EAAE,aAAa,CAAC,CAAC;IACjE,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,0BAA0B,EAAE,aAAa,CAAC,CAAC;IAE/D,qBAAqB;IACrB,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,0BAA0B,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACrE,MAAM,YAAY,GAAG,eAAe,CAAC,IAAI,CAAC,uBAAuB,CAAC,KAAK,CAAC,CAAC;YACvE,CAAC,CAAC,UAAU,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YAChC,CAAC,CAAC,KAAK,CAAC;QACV,OAAO,YAAY,UAAU,CAC3B,uBAAuB,CAAC,GAAG,CAAC,CAC7B,sDAAsD,YAAY,MAAM,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,sBAAsB;IACtB,IAAI,GAAG,IAAI,CAAC,OAAO,CACjB,YAAY,EACZ,+FAA+F,CAChG,CAAC;IAEF,4EAA4E;IAC5E,IAAI,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;IAEnC,oDAAoD;IACpD,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,aAAa,CAAC,CAAC;IACtD,gCAAgC;IAChC,IAAI,GAAG,IAAI,CAAC,OAAO,CACjB,sBAAsB,EACtB,qDAAqD,CACtD,CAAC;IAEF,uDAAuD;IACvD,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,aAAa,CAAC,CAAC;IACrD,oDAAoD;IACpD,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,iCAAiC,EAAE,CAAC,KAAK,EAAE,EAAE;QAC/D,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACxC,OAAO,+CAA+C,KAAK,OAAO,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,0BAA0B;IAC1B,IAAI,GAAG,IAAI,CAAC,OAAO,CACjB,cAAc,EACd,wDAAwD,CACzD,CAAC;IACF,IAAI,GAAG,IAAI,CAAC,OAAO,CACjB,aAAa,EACb,yDAAyD,CAC1D,CAAC;IACF,IAAI,GAAG,IAAI,CAAC,OAAO,CACjB,YAAY,EACZ,wDAAwD,CACzD,CAAC;IAEF,+BAA+B;IAC/B,IAAI,GAAG,IAAI,CAAC,OAAO,CACjB,oBAAoB,EACpB,sEAAsE,CACvE,CAAC;IAEF,8BAA8B;IAC9B,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACxC,yBAAyB;IACzB,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAEnC,yBAAyB;IACzB,IAAI,GAAG,MAAM,IAAI,MAAM,CAAC;IACxB,4BAA4B;IAC5B,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;IAExC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,oEAAoE;AACpE,SAAS,mBAAmB,CAAC,QAAgB;IAC3C,OAAO;;;;;;;;EAQP,QAAQ;;;QAGF,CAAC;AACT,CAAC;AAED,8EAA8E;AAC9E,kDAAkD;AAClD,8EAA8E;AAE9E;;;;;GAKG;AACH,KAAK,UAAU,WAAW,CAAC,KAAc;IACvC,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC;IACvC,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC;IAC9C,MAAM,GAAG,GAAG,CAAC,MAAM,aAAa,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/C,KAAK,CAAC,OAAO,CAAC,SAAS,GAAG,GAAG,CAAC;IAC9B,OAAO,GAAG,CAAC;AACb,CAAC","sourcesContent":["import type { H3Event } from \"h3\";\nimport { getHeader, readRawBody as h3ReadRawBody } from \"h3\";\nimport { timingSafeEqual } from \"node:crypto\";\nimport type {\n PlatformAdapter,\n IncomingMessage,\n OutgoingMessage,\n IntegrationStatus,\n OutboundTarget,\n} from \"../types.js\";\nimport type { EnvKeyConfig } from \"../../server/create-server.js\";\nimport { getIntegrationConfig } from \"../config-store.js\";\nimport { getDbExec } from \"../../db/client.js\";\nimport {\n sendEmail,\n isEmailConfigured,\n getEmailProvider,\n} from \"../../server/email.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\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 = process.env.EMAIL_INBOUND_WEBHOOK_SECRET;\n const provider = 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 = getEmailProvider();\n const agentAddress = process.env.EMAIL_AGENT_ADDRESS?.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 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 },\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 = process.env.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 fromAddress = process.env.EMAIL_FROM\n ? process.env.EMAIL_FROM\n : `${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)\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 = process.env.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 = !!process.env.EMAIL_AGENT_ADDRESS;\n const hasEmailProvider = isEmailConfigured();\n const hasWebhookSecret = !!process.env.EMAIL_INBOUND_WEBHOOK_SECRET;\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: getEmailProvider(),\n },\n error: !configured\n ? \"Set EMAIL_AGENT_ADDRESS and either RESEND_API_KEY or SENDGRID_API_KEY\"\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\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 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 };\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 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 };\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 ?\n `,\n args: [\"email\", cutoff, `%\"senderId\":\"${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(context: IncomingMessage): string[] | undefined {\n const agentAddress = process.env.EMAIL_AGENT_ADDRESS?.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 !== agentAddress && 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(/ /g, \" \")\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n}\n\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\nfunction decodeBasicHtmlEntities(s: string): string {\n return s\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\");\n}\n\nfunction splitTrailingUrlPunctuation(raw: string): {\n url: string;\n trailing: string;\n} {\n let url = raw;\n let trailing = \"\";\n const trailingEntities = [\""\", \"'\"];\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":"slack.d.ts","sourceRoot":"","sources":["../../../src/integrations/adapters/slack.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,eAAe,EAKhB,MAAM,aAAa,CAAC;AAOrB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,YAAY,IAAI,eAAe,
|
|
1
|
+
{"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../../src/integrations/adapters/slack.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,eAAe,EAKhB,MAAM,aAAa,CAAC;AAOrB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,YAAY,IAAI,eAAe,CAgU9C"}
|
|
@@ -50,7 +50,10 @@ export function slackAdapter() {
|
|
|
50
50
|
try {
|
|
51
51
|
const parsed = JSON.parse(body);
|
|
52
52
|
if (parsed.type === "url_verification") {
|
|
53
|
-
|
|
53
|
+
// Slack's URL verifier expects the raw challenge value in the
|
|
54
|
+
// response body. Returning JSON works for some clients but the app
|
|
55
|
+
// settings verifier rejects it as not matching the challenge.
|
|
56
|
+
return { handled: true, response: parsed.challenge };
|
|
54
57
|
}
|
|
55
58
|
}
|
|
56
59
|
catch { }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slack.js","sourceRoot":"","sources":["../../../src/integrations/adapters/slack.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,IAAI,CAAC;AAWzD,iCAAiC;AACjC,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,YAAY;IAC1B,OAAO;QACL,QAAQ,EAAE,OAAO;QACjB,KAAK,EAAE,OAAO;QAEd,kBAAkB;YAChB,OAAO;gBACL;oBACE,GAAG,EAAE,iBAAiB;oBACtB,KAAK,EAAE,iBAAiB;oBACxB,QAAQ,EAAE,IAAI;oBACd,QAAQ,EACN,iGAAiG;iBACpG;gBACD;oBACE,GAAG,EAAE,sBAAsB;oBAC3B,KAAK,EAAE,sBAAsB;oBAC7B,QAAQ,EAAE,IAAI;oBACd,QAAQ,EACN,qFAAqF;iBACxF;aACF,CAAC;QACJ,CAAC;QAED,KAAK,CAAC,kBAAkB,CACtB,KAAc;YAEd,kEAAkE;YAClE,uEAAuE;YACvE,oEAAoE;YACpE,2DAA2D;YAC3D,MAAM,IAAI,GAAG,MAAM,iBAAiB,CAAC,KAAK,CAAC,CAAC;YAC5C,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAChC,IAAI,MAAM,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;oBACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,EAAE,CAAC;gBACtE,CAAC;YACH,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;YACV,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAC5B,CAAC;QAED,KAAK,CAAC,aAAa,CAAC,KAAc;YAChC,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;YACvD,IAAI,CAAC,aAAa;gBAAE,OAAO,KAAK,CAAC;YAEjC,MAAM,SAAS,GAAG,SAAS,CAAC,KAAK,EAAE,mBAAmB,CAAC,CAAC;YACxD,MAAM,SAAS,GAAG,SAAS,CAAC,KAAK,EAAE,2BAA2B,CAAC,CAAC;YAChE,IAAI,CAAC,SAAS,IAAI,CAAC,SAAS;gBAAE,OAAO,KAAK,CAAC;YAE3C,2DAA2D;YAC3D,MAAM,EAAE,GAAG,QAAQ,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;YACnC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC,GAAG,GAAG;gBAAE,OAAO,KAAK,CAAC;YAEzD,MAAM,IAAI,GAAG,MAAM,iBAAiB,CAAC,KAAK,CAAC,CAAC;YAC5C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;YAC3C,MAAM,UAAU,GAAG,MAAM,SAAS,IAAI,IAAI,EAAE,CAAC;YAC7C,MAAM,iBAAiB,GACrB,KAAK;gBACL,MAAM;qBACH,UAAU,CAAC,QAAQ,EAAE,aAAa,CAAC;qBACnC,MAAM,CAAC,UAAU,CAAC;qBAClB,MAAM,CAAC,KAAK,CAAC,CAAC;YAEnB,yBAAyB;YACzB,IAAI,CAAC;gBACH,OAAO,MAAM,CAAC,eAAe,CAC3B,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EACtB,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAC/B,CAAC;YACJ,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;QAED,KAAK,CAAC,oBAAoB,CACxB,KAAc;YAEd,MAAM,GAAG,GAAG,MAAM,iBAAiB,CAAC,KAAK,CAAC,CAAC;YAC3C,IAAI,OAAY,CAAC;YACjB,IAAI,CAAC;gBACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC5B,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,CAAC;YACd,CAAC;YAED,kEAAkE;YAClE,sEAAsE;YACtE,qEAAqE;YACrE,mEAAmE;YACnE,oEAAoE;YACpE,oEAAoE;YACpE,mEAAmE;YACnE,iCAAiC;YACjC,yBAAyB,CAAC,OAAO,CAAC,CAAC;YAEnC,4BAA4B;YAC5B,IAAI,OAAO,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;gBACtC,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC;gBACxB,IAAI,CAAC,CAAC;oBAAE,OAAO,IAAI,CAAC;gBAEpB,sBAAsB;gBACtB,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,OAAO,KAAK,aAAa;oBAAE,OAAO,IAAI,CAAC;gBACzD,mCAAmC;gBACnC,IAAI,CAAC,CAAC,OAAO,KAAK,iBAAiB,IAAI,CAAC,CAAC,OAAO,KAAK,iBAAiB;oBACpE,OAAO,IAAI,CAAC;gBAEd,+CAA+C;gBAC/C,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC;gBAC5B,IAAI,CAAC,IAAI;oBAAE,OAAO,IAAI,CAAC;gBAEvB,+EAA+E;gBAC/E,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC3D,IAAI,CAAC,SAAS;oBAAE,OAAO,IAAI,CAAC;gBAE5B,gEAAgE;gBAChE,MAAM,QAAQ,GAAG,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,EAAE,CAAC;gBACrC,MAAM,gBAAgB,GAAG,GAAG,CAAC,CAAC,OAAO,IAAI,QAAQ,EAAE,CAAC;gBAEpD,OAAO;oBACL,QAAQ,EAAE,OAAO;oBACjB,gBAAgB;oBAChB,IAAI,EAAE,SAAS;oBACf,UAAU,EAAE,CAAC,CAAC,IAAI;oBAClB,QAAQ,EAAE,CAAC,CAAC,IAAI;oBAChB,eAAe,EAAE;wBACf,SAAS,EAAE,CAAC,CAAC,OAAO;wBACpB,QAAQ,EAAE,QAAQ;wBAClB,SAAS,EAAE,CAAC,CAAC,EAAE;wBACf,MAAM,EAAE,OAAO,CAAC,OAAO;wBACvB,OAAO,EAAE,OAAO,CAAC,QAAQ;qBAC1B;oBACD,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;iBAC/C,CAAC;YACJ,CAAC;YAED,OAAO,IAAI,CAAC;QACd,CAAC;QAED,KAAK,CAAC,yBAAyB,CAC7B,QAAyB;YAEzB,gEAAgE;YAChE,sEAAsE;YACtE,kEAAkE;YAClE,oEAAoE;YACpE,sEAAsE;YACtE,sCAAsC;YACtC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;YAC1C,IAAI,CAAC,KAAK;gBAAE,OAAO,IAAI,CAAC;YAExB,MAAM,SAAS,GAAG,QAAQ,CAAC,eAAe,CAAC,SAAmB,CAAC;YAC/D,MAAM,QAAQ,GAAG,QAAQ,CAAC,eAAe,CAAC,QAAkB,CAAC;YAC7D,IAAI,CAAC,SAAS,IAAI,CAAC,QAAQ;gBAAE,OAAO,IAAI,CAAC;YAEzC,sEAAsE;YACtE,qEAAqE;YACrE,mCAAmC;YACnC,uBAAuB,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC;YACpE,OAAO,IAAI,CAAC;QACd,CAAC;QAED,KAAK,CAAC,YAAY,CAChB,OAAwB,EACxB,OAAwB,EACxB,IAAkC;YAElC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;YAC1C,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC;gBACxD,OAAO;YACT,CAAC;YAED,MAAM,SAAS,GAAG,OAAO,CAAC,eAAe,CAAC,SAAmB,CAAC;YAC9D,MAAM,QAAQ,GAAG,OAAO,CAAC,eAAe,CAAC,QAAkB,CAAC;YAC5D,MAAM,MAAM,GAAI,OAAO,CAAC,eAAuB,EAAE,MAEpC,CAAC;YACd,MAAM,cAAc,GAAG,IAAI,EAAE,cAAc,CAAC;YAE5C,wEAAwE;YACxE,qEAAqE;YACrE,wEAAwE;YACxE,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;YAC5D,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YACnC,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAEnC,MAAM,WAAW,GACf,MAAM;gBACN,mBAAmB,CAAC,UAAU,EAAE;oBAC9B,iBAAiB,EAAG,OAAO,CAAC,eAAuB;wBACjD,EAAE,iBAAiB;iBACtB,CAAC,CAAC;YAEL,MAAM,QAAQ,GAA4B;gBACxC,OAAO,EAAE,SAAS;gBAClB,IAAI,EAAE,UAAU;gBAChB,MAAM,EAAE,WAAW;gBACnB,YAAY,EAAE,KAAK;gBACnB,YAAY,EAAE,KAAK;gBACnB,MAAM,EAAE,IAAI;aACb,CAAC;YAEF,IAAI,CAAC;gBACH,IAAI,cAAc,EAAE,CAAC;oBACnB,gDAAgD;oBAChD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,mCAAmC,EAAE;wBAC3D,MAAM,EAAE,MAAM;wBACd,OAAO,EAAE;4BACP,aAAa,EAAE,UAAU,KAAK,EAAE;4BAChC,cAAc,EAAE,kBAAkB;yBACnC;wBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,QAAQ,EAAE,EAAE,EAAE,cAAc,EAAE,CAAC;qBAC1D,CAAC,CAAC;oBACH,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAG7B,CAAC;oBACF,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;wBACb,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;wBACxD,2DAA2D;wBAC3D,MAAM,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;oBACxD,CAAC;gBACH,CAAC;qBAAM,CAAC;oBACN,MAAM,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;gBACxD,CAAC;gBAED,8DAA8D;gBAC9D,sDAAsD;gBACtD,IAAI,QAAQ,EAAE,CAAC;oBACb,uBAAuB,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;gBAC1D,CAAC;gBAED,uEAAuE;gBACvE,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;oBAC/B,MAAM,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE;wBAC1C,OAAO,EAAE,SAAS;wBAClB,IAAI,EAAE,KAAK;wBACX,YAAY,EAAE,KAAK;wBACnB,YAAY,EAAE,KAAK;wBACnB,MAAM,EAAE,IAAI;qBACb,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,GAAG,CAAC,CAAC;YACxD,CAAC;QACH,CAAC;QAED,KAAK,CAAC,mBAAmB,CACvB,OAAwB,EACxB,MAAsB;YAEtB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;YAC1C,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC;gBACxD,OAAO;YACT,CAAC;YAED,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;YAC5D,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBAC3B,MAAM,IAAI,GAA4B;oBACpC,OAAO,EAAE,MAAM,CAAC,WAAW;oBAC3B,IAAI,EAAE,KAAK;iBACZ,CAAC;gBACF,IAAI,MAAM,CAAC,SAAS;oBAAE,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;gBAExD,IAAI,CAAC;oBACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,wCAAwC,EAAE;wBAChE,MAAM,EAAE,MAAM;wBACd,OAAO,EAAE;4BACP,aAAa,EAAE,UAAU,KAAK,EAAE;4BAChC,cAAc,EAAE,kBAAkB;yBACnC;wBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;qBAC3B,CAAC,CAAC;oBACH,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAoC,CAAC;oBACnE,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;wBACb,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,yBAAyB,CAAC,CAAC;oBAC3D,CAAC;gBACH,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,OAAO,CAAC,KAAK,CAAC,2CAA2C,EAAE,GAAG,CAAC,CAAC;oBAChE,MAAM,GAAG,CAAC;gBACZ,CAAC;YACH,CAAC;QACH,CAAC;QAED,mBAAmB,CACjB,IAAY,EACZ,IAAqC;YAErC,OAAO;gBACL,IAAI,EAAE,qBAAqB,CAAC,IAAI,CAAC;gBACjC,eAAe,EAAE,IAAI,EAAE,iBAAiB;oBACtC,CAAC,CAAC,EAAE,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,EAAE;oBAC/C,CAAC,CAAC,EAAE;aACP,CAAC;QACJ,CAAC;QAED,KAAK,CAAC,SAAS,CAAC,OAAgB;YAC9B,MAAM,QAAQ,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;YAC/C,MAAM,SAAS,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;YACrD,MAAM,UAAU,GAAG,QAAQ,IAAI,SAAS,CAAC;YAEzC,OAAO;gBACL,QAAQ,EAAE,OAAO;gBACjB,KAAK,EAAE,OAAO;gBACd,OAAO,EAAE,KAAK,EAAE,uBAAuB;gBACvC,UAAU;gBACV,OAAO,EAAE;oBACP,QAAQ;oBACR,SAAS;iBACV;gBACD,KAAK,EAAE,CAAC,UAAU;oBAChB,CAAC,CAAC,kEAAkE;oBACpE,CAAC,CAAC,SAAS;aACd,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,iBAAiB,CAAC,IAAY;IACrC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,MAAM,MAAM,GAAG,GAAG;SACf,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC/B,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACrC,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;AACzB,CAAC;AAED,IAAI,uBAAuB,GAAG,KAAK,CAAC;AAEpC;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,SAAS,yBAAyB,CAAC,OAAY;IAC7C,MAAM,MAAM,GACV,OAAO,OAAO,EAAE,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IACrE,MAAM,QAAQ,GACZ,OAAO,OAAO,EAAE,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IAE3E,MAAM,cAAc,GAAG,iBAAiB,CAAC,wBAAwB,CAAC,CAAC;IACnE,MAAM,aAAa,GAAG,iBAAiB,CAAC,2BAA2B,CAAC,CAAC;IAErE,IAAI,cAAc,EAAE,CAAC;QACnB,IAAI,CAAC,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3C,MAAM,WAAW,CAAC;gBAChB,UAAU,EAAE,GAAG;gBACf,aAAa,EAAE,8BAA8B;aAC9C,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,IAAI,aAAa,EAAE,CAAC;QAClB,IAAI,CAAC,QAAQ,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9C,MAAM,WAAW,CAAC;gBAChB,UAAU,EAAE,GAAG;gBACf,aAAa,EAAE,8BAA8B;aAC9C,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,IACE,CAAC,cAAc;QACf,CAAC,aAAa;QACd,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY;QACrC,CAAC,uBAAuB,EACxB,CAAC;QACD,uBAAuB,GAAG,IAAI,CAAC;QAC/B,OAAO,CAAC,IAAI,CACV,2IAA2I;YACzI,sJAAsJ,CACzJ,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,KAAK,UAAU,iBAAiB,CAAC,KAAc;IAC7C,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC;IACvC,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC;IAC9C,wEAAwE;IACxE,wEAAwE;IACxE,gDAAgD;IAChD,MAAM,GAAG,GAAG,CAAC,MAAM,WAAW,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7C,KAAK,CAAC,OAAO,CAAC,SAAS,GAAG,GAAG,CAAC;IAC9B,OAAO,GAAG,CAAC;AACb,CAAC;AAED,uEAAuE;AACvE,SAAS,YAAY,CAAC,IAAY,EAAE,SAAiB;IACnD,IAAI,IAAI,CAAC,MAAM,IAAI,SAAS;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,SAAS,GAAG,IAAI,CAAC;IACrB,OAAO,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,IAAI,SAAS,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC;YAClC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACvB,MAAM;QACR,CAAC;QACD,4BAA4B;QAC5B,IAAI,QAAQ,GAAG,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACtD,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;YAClB,0BAA0B;YAC1B,QAAQ,GAAG,SAAS,CAAC,WAAW,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QACnD,CAAC;QACD,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;YAClB,QAAQ,GAAG,SAAS,CAAC;QACvB,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;QAC1C,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,SAAS,EAAE,CAAC;IACpD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;4EAK4E;AAC5E,MAAM,iBAAiB,GAAG,MAAM,CAAC;AAEjC;;;;;;;;GAQG;AACH,SAAS,qBAAqB,CAAC,IAAY;IACzC,MAAM,OAAO,GACX,IAAI,CAAC,MAAM,GAAG,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5E,OAAO,CACL,OAAO;SACJ,OAAO,CAAC,0BAA0B,EAAE,SAAS,CAAC;QAC/C,oEAAoE;QACpE,gEAAgE;QAChE,sEAAsE;QACtE,iEAAiE;QACjE,gDAAgD;SAC/C,OAAO,CAAC,yBAAyB,EAAE,MAAM,CAAC,CAC9C,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,uBAAuB,CAC9B,KAAa,EACb,SAAiB,EACjB,QAAgB,EAChB,MAAc;IAEd,KAAK,CAAC,mDAAmD,EAAE;QACzD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,aAAa,EAAE,UAAU,KAAK,EAAE;YAChC,cAAc,EAAE,kBAAkB;SACnC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,UAAU,EAAE,SAAS;YACrB,SAAS,EAAE,QAAQ;YACnB,MAAM;SACP,CAAC;KACH,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AACrB,CAAC;AAED;;;;;GAKG;AACH,SAAS,mBAAmB,CAC1B,IAAY,EACZ,IAAoC;IAEpC,MAAM,MAAM,GAAU;QACpB;YACE,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,IAAI,iBAAiB,EAAE;SAC1D;KACF,CAAC;IACF,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC3B,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,SAAS;YACf,QAAQ,EAAE;gBACR;oBACE,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,IAAI,EAAE;oBAC9D,GAAG,EAAE,IAAI,CAAC,iBAAiB;oBAC3B,SAAS,EAAE,sBAAsB;iBAClC;aACF;SACF,CAAC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,SAAS,CACtB,KAAa,EACb,SAAiB,EACjB,QAA4B,EAC5B,IAA6B;IAE7B,MAAM,OAAO,GAA4B;QACvC,GAAG,IAAI;QACP,OAAO,EAAE,SAAS;KACnB,CAAC;IACF,IAAI,QAAQ,IAAI,CAAC,OAAO,CAAC,SAAS;QAAE,OAAO,CAAC,SAAS,GAAG,QAAQ,CAAC;IACjE,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,wCAAwC,EAAE;QAChE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,aAAa,EAAE,UAAU,KAAK,EAAE;YAChC,cAAc,EAAE,kBAAkB;SACnC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;KAC9B,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAoC,CAAC;IACnE,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IAC/D,CAAC;AACH,CAAC","sourcesContent":["import type { H3Event } from \"h3\";\nimport { createError, getHeader, readRawBody } from \"h3\";\nimport type {\n PlatformAdapter,\n IncomingMessage,\n OutgoingMessage,\n IntegrationStatus,\n OutboundTarget,\n} from \"../types.js\";\nimport type { EnvKeyConfig } from \"../../server/create-server.js\";\nimport { getIntegrationConfig } from \"../config-store.js\";\n\n/** Slack's max message length */\nconst SLACK_MAX_LENGTH = 4000;\n\n/**\n * Create a Slack platform adapter.\n *\n * Required env vars:\n * - SLACK_BOT_TOKEN — Bot user OAuth token (xoxb-...)\n * - SLACK_SIGNING_SECRET — Used to verify webhook signatures\n *\n * Optional env vars:\n * - SLACK_ALLOWED_TEAM_IDS — Comma-separated list of Slack workspace\n * `team_id` values (e.g. \"T012ABCDEF,T034GHIJKL\") that this deployment\n * accepts events from. Strongly recommended in multi-tenant deployments\n * to prevent cross-workspace event injection (H1 in the webhook audit):\n * the global `SLACK_SIGNING_SECRET` is the same key for every workspace\n * the app is installed to, so without an allowlist any installed\n * workspace can drive the agent. When unset the adapter accepts events\n * from any workspace — fine for single-tenant dev, unsafe for prod.\n * - SLACK_ALLOWED_API_APP_IDS — Comma-separated list of Slack app IDs\n * (`api_app_id`) to additionally pin events to. Useful when the same\n * signing secret rotation surfaces multiple app installs.\n */\nexport function slackAdapter(): PlatformAdapter {\n return {\n platform: \"slack\",\n label: \"Slack\",\n\n getRequiredEnvKeys(): EnvKeyConfig[] {\n return [\n {\n key: \"SLACK_BOT_TOKEN\",\n label: \"Slack Bot Token\",\n required: true,\n helpText:\n \"In your Slack app's left nav: OAuth & Permissions → Bot User OAuth Token (starts with `xoxb-`).\",\n },\n {\n key: \"SLACK_SIGNING_SECRET\",\n label: \"Slack Signing Secret\",\n required: true,\n helpText:\n \"In your Slack app's left nav: Basic Information → App Credentials → Signing Secret.\",\n },\n ];\n },\n\n async handleVerification(\n event: H3Event,\n ): Promise<{ handled: boolean; response?: unknown }> {\n // Slack sends url_verification when first setting up the webhook.\n // readRawBodyCached caches the raw bytes on event.context.__rawBody so\n // subsequent verifyWebhook + parseIncomingMessage calls re-use them\n // without re-stringifying a parsed body (M2 in the audit).\n const body = await readRawBodyCached(event);\n try {\n const parsed = JSON.parse(body);\n if (parsed.type === \"url_verification\") {\n return { handled: true, response: { challenge: parsed.challenge } };\n }\n } catch {}\n return { handled: false };\n },\n\n async verifyWebhook(event: H3Event): Promise<boolean> {\n const signingSecret = process.env.SLACK_SIGNING_SECRET;\n if (!signingSecret) return false;\n\n const signature = getHeader(event, \"x-slack-signature\");\n const timestamp = getHeader(event, \"x-slack-request-timestamp\");\n if (!signature || !timestamp) return false;\n\n // Reject requests older than 5 minutes (replay protection)\n const ts = parseInt(timestamp, 10);\n if (Math.abs(Date.now() / 1000 - ts) > 300) return false;\n\n const body = await readRawBodyCached(event);\n const crypto = await import(\"node:crypto\");\n const basestring = `v0:${timestamp}:${body}`;\n const expectedSignature =\n \"v0=\" +\n crypto\n .createHmac(\"sha256\", signingSecret)\n .update(basestring)\n .digest(\"hex\");\n\n // Timing-safe comparison\n try {\n return crypto.timingSafeEqual(\n Buffer.from(signature),\n Buffer.from(expectedSignature),\n );\n } catch {\n return false;\n }\n },\n\n async parseIncomingMessage(\n event: H3Event,\n ): Promise<IncomingMessage | null> {\n const raw = await readRawBodyCached(event);\n let payload: any;\n try {\n payload = JSON.parse(raw);\n } catch {\n return null;\n }\n\n // H1 (webhook audit): cross-workspace event injection. The global\n // SLACK_SIGNING_SECRET is the same key for every workspace this Slack\n // app is installed to — without a per-tenant allowlist any installed\n // workspace can drive the agent. We enforce SLACK_ALLOWED_TEAM_IDS\n // here AFTER the signature has already been verified by the webhook\n // handler, so this is purely a tenant-isolation gate (not a forgery\n // defense). When unset in production we surface a one-time warning\n // recommending it be configured.\n enforceWorkspaceAllowlist(payload);\n\n // Handle Events API wrapper\n if (payload.type === \"event_callback\") {\n const e = payload.event;\n if (!e) return null;\n\n // Ignore bot messages\n if (e.bot_id || e.subtype === \"bot_message\") return null;\n // Ignore message edits and deletes\n if (e.subtype === \"message_changed\" || e.subtype === \"message_deleted\")\n return null;\n\n // Handle both direct messages and app_mentions\n const text = e.text?.trim();\n if (!text) return null;\n\n // Remove bot mention from text (e.g., \"<@U123> do something\" → \"do something\")\n const cleanText = text.replace(/<@[A-Z0-9]+>/g, \"\").trim();\n if (!cleanText) return null;\n\n // Thread ID: use thread_ts if in a thread, otherwise message ts\n const threadTs = e.thread_ts || e.ts;\n const externalThreadId = `${e.channel}:${threadTs}`;\n\n return {\n platform: \"slack\",\n externalThreadId,\n text: cleanText,\n senderName: e.user,\n senderId: e.user,\n platformContext: {\n channelId: e.channel,\n threadTs: threadTs,\n messageTs: e.ts,\n teamId: payload.team_id,\n eventId: payload.event_id,\n },\n timestamp: Math.floor(parseFloat(e.ts) * 1000),\n };\n }\n\n return null;\n },\n\n async postProcessingPlaceholder(\n incoming: IncomingMessage,\n ): Promise<{ placeholderRef: string } | null> {\n // No placeholder reply in the thread — Slack's native assistant\n // status bar (\"agent-native is thinking…\", below the composer) is the\n // loading affordance. A second visible \"Working on it…\" reply was\n // redundant and added an extra chunk that we then had to overwrite.\n // We just set the native status and return null so sendResponse posts\n // the final reply as a fresh message.\n const token = process.env.SLACK_BOT_TOKEN;\n if (!token) return null;\n\n const channelId = incoming.platformContext.channelId as string;\n const threadTs = incoming.platformContext.threadTs as string;\n if (!channelId || !threadTs) return null;\n\n // Best-effort: flip the native AI-assistant \"is thinking…\" status bar\n // in the channel input area. Requires `assistant:write` scope on the\n // app — otherwise silently no-ops.\n setSlackAssistantStatus(token, channelId, threadTs, \"is thinking…\");\n return null;\n },\n\n async sendResponse(\n message: OutgoingMessage,\n context: IncomingMessage,\n opts?: { placeholderRef?: string },\n ): Promise<void> {\n const token = process.env.SLACK_BOT_TOKEN;\n if (!token) {\n console.error(\"[slack] SLACK_BOT_TOKEN not configured\");\n return;\n }\n\n const channelId = context.platformContext.channelId as string;\n const threadTs = context.platformContext.threadTs as string;\n const blocks = (message.platformContext as any)?.blocks as\n | unknown[]\n | undefined;\n const placeholderRef = opts?.placeholderRef;\n\n // Block-rich path: split text into chunks but render the FIRST chunk as\n // blocks (so we keep the in-place edit + button) and any overflow as\n // plain follow-up posts. The vast majority of replies fit in one block.\n const chunks = splitMessage(message.text, SLACK_MAX_LENGTH);\n const firstChunk = chunks[0] ?? \"\";\n const restChunks = chunks.slice(1);\n\n const finalBlocks =\n blocks ??\n buildResponseBlocks(firstChunk, {\n threadDeepLinkUrl: (message.platformContext as any)\n ?.threadDeepLinkUrl,\n });\n\n const baseBody: Record<string, unknown> = {\n channel: channelId,\n text: firstChunk,\n blocks: finalBlocks,\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n };\n\n try {\n if (placeholderRef) {\n // Replace the \"thinking…\" placeholder in place.\n const res = await fetch(\"https://slack.com/api/chat.update\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ ...baseBody, ts: placeholderRef }),\n });\n const data = (await res.json()) as {\n ok: boolean;\n error?: string;\n };\n if (!data.ok) {\n console.error(\"[slack] chat.update error:\", data.error);\n // Fall back to a fresh post so the user still sees a reply\n await postFresh(token, channelId, threadTs, baseBody);\n }\n } else {\n await postFresh(token, channelId, threadTs, baseBody);\n }\n\n // Clear the AI-assistant \"is thinking…\" status now that we've\n // delivered the final answer. Empty status clears it.\n if (threadTs) {\n setSlackAssistantStatus(token, channelId, threadTs, \"\");\n }\n\n // Overflow chunks (rare) — post as plain follow-ups in the same thread\n for (const chunk of restChunks) {\n await postFresh(token, channelId, threadTs, {\n channel: channelId,\n text: chunk,\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n });\n }\n } catch (err) {\n console.error(\"[slack] Failed to send message:\", err);\n }\n },\n\n async sendMessageToTarget(\n message: OutgoingMessage,\n target: OutboundTarget,\n ): Promise<void> {\n const token = process.env.SLACK_BOT_TOKEN;\n if (!token) {\n console.error(\"[slack] SLACK_BOT_TOKEN not configured\");\n return;\n }\n\n const chunks = splitMessage(message.text, SLACK_MAX_LENGTH);\n for (const chunk of chunks) {\n const body: Record<string, unknown> = {\n channel: target.destination,\n text: chunk,\n };\n if (target.threadRef) body.thread_ts = target.threadRef;\n\n try {\n const res = await fetch(\"https://slack.com/api/chat.postMessage\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n const data = (await res.json()) as { ok: boolean; error?: string };\n if (!data.ok) {\n throw new Error(data.error || \"chat.postMessage failed\");\n }\n } catch (err) {\n console.error(\"[slack] Failed to send proactive message:\", err);\n throw err;\n }\n }\n },\n\n formatAgentResponse(\n text: string,\n opts?: { threadDeepLinkUrl?: string },\n ): OutgoingMessage {\n return {\n text: markdownToSlackMrkdwn(text),\n platformContext: opts?.threadDeepLinkUrl\n ? { threadDeepLinkUrl: opts.threadDeepLinkUrl }\n : {},\n };\n },\n\n async getStatus(baseUrl?: string): Promise<IntegrationStatus> {\n const hasToken = !!process.env.SLACK_BOT_TOKEN;\n const hasSecret = !!process.env.SLACK_SIGNING_SECRET;\n const configured = hasToken && hasSecret;\n\n return {\n platform: \"slack\",\n label: \"Slack\",\n enabled: false, // overridden by plugin\n configured,\n details: {\n hasToken,\n hasSecret,\n },\n error: !configured\n ? \"Set SLACK_BOT_TOKEN and SLACK_SIGNING_SECRET in your environment\"\n : undefined,\n };\n },\n };\n}\n\n/**\n * Parse a comma-separated env var into a Set of trimmed, non-empty values.\n * Returns null when the env var is unset or empty (so callers can\n * distinguish \"no allowlist configured\" from \"empty allowlist\").\n */\nfunction parseAllowlistEnv(name: string): Set<string> | null {\n const raw = process.env[name];\n if (!raw) return null;\n const values = raw\n .split(\",\")\n .map((v) => v.trim())\n .filter((v) => v.length > 0);\n if (values.length === 0) return null;\n return new Set(values);\n}\n\nlet _missingAllowlistWarned = false;\n\n/**\n * Enforce that an incoming Slack event comes from an allowlisted workspace.\n *\n * H1 in the webhook audit: the framework uses a SINGLE global\n * SLACK_SIGNING_SECRET for every workspace the Slack app is installed to,\n * so a valid signature alone doesn't prove the request belongs to the\n * tenant the deployment intends to serve. This helper layers a per-tenant\n * allowlist on top of signature verification.\n *\n * Behavior:\n * - If `SLACK_ALLOWED_TEAM_IDS` is set: reject any payload whose\n * `team_id` isn't in the list.\n * - If `SLACK_ALLOWED_API_APP_IDS` is set: also reject payloads whose\n * `api_app_id` isn't in the list (bot apps can be installed under the\n * same Slack app id across multiple workspaces — pinning both keeps\n * the surface tight when team_id allows multiple workspaces).\n * - If neither is set AND `NODE_ENV === \"production\"`: log a one-time\n * warning recommending the env var be configured. Continue (preserves\n * existing behavior to avoid breaking single-tenant prod deployments\n * that have always run without an allowlist).\n * - If neither is set in dev / single-tenant: accept (current behavior).\n *\n * Throws an h3 401 error when an allowlisted-but-mismatched payload is\n * received, which the integrations plugin surfaces to the caller as\n * \"Unrecognized Slack workspace\" without enqueuing the event.\n */\nfunction enforceWorkspaceAllowlist(payload: any): void {\n const teamId =\n typeof payload?.team_id === \"string\" ? payload.team_id : undefined;\n const apiAppId =\n typeof payload?.api_app_id === \"string\" ? payload.api_app_id : undefined;\n\n const allowedTeamIds = parseAllowlistEnv(\"SLACK_ALLOWED_TEAM_IDS\");\n const allowedAppIds = parseAllowlistEnv(\"SLACK_ALLOWED_API_APP_IDS\");\n\n if (allowedTeamIds) {\n if (!teamId || !allowedTeamIds.has(teamId)) {\n throw createError({\n statusCode: 401,\n statusMessage: \"Unrecognized Slack workspace\",\n });\n }\n }\n\n if (allowedAppIds) {\n if (!apiAppId || !allowedAppIds.has(apiAppId)) {\n throw createError({\n statusCode: 401,\n statusMessage: \"Unrecognized Slack workspace\",\n });\n }\n }\n\n if (\n !allowedTeamIds &&\n !allowedAppIds &&\n process.env.NODE_ENV === \"production\" &&\n !_missingAllowlistWarned\n ) {\n _missingAllowlistWarned = true;\n console.warn(\n \"[slack] SLACK_ALLOWED_TEAM_IDS not set in production — accepting events from any workspace whose signature matches SLACK_SIGNING_SECRET. \" +\n \"Set SLACK_ALLOWED_TEAM_IDS to a comma-separated list of allowed team_id values to prevent cross-workspace event injection (H1 in the webhook audit).\",\n );\n }\n}\n\n/**\n * Read the raw request body as a string and cache on the event context.\n *\n * This MUST read raw bytes from the request stream — never `JSON.stringify`\n * a parsed body, because Slack's HMAC is computed over the exact bytes Slack\n * sent. Re-stringifying a parsed object loses key ordering, whitespace, and\n * Unicode-escape choices, so the signature check would silently fail for\n * legitimate requests (M2 in the webhook security audit).\n *\n * h3 v2's body stream is consume-once, so we cache the raw string on the\n * event context after the first read. All call sites (handleVerification,\n * verifyWebhook, parseIncomingMessage) MUST go through this helper.\n */\nasync function readRawBodyCached(event: H3Event): Promise<string> {\n const cached = event.context.__rawBody;\n if (typeof cached === \"string\") return cached;\n // h3's readRawBody returns the bytes Slack actually sent, defaulting to\n // utf8-decoded. Returns undefined for empty bodies — we coerce to \"\" so\n // the HMAC check can proceed deterministically.\n const raw = (await readRawBody(event)) ?? \"\";\n event.context.__rawBody = raw;\n return raw;\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 // Try to split at a newline\n let splitIdx = remaining.lastIndexOf(\"\\n\", maxLength);\n if (splitIdx <= 0) {\n // Try to split at a space\n splitIdx = remaining.lastIndexOf(\" \", maxLength);\n }\n if (splitIdx <= 0) {\n splitIdx = maxLength;\n }\n chunks.push(remaining.slice(0, splitIdx));\n remaining = remaining.slice(splitIdx).trimStart();\n }\n return chunks;\n}\n\n/** Hard cap on input length we feed to the regex-based mrkdwn converter.\n * L2 in the webhook audit: `\\*\\*(.+?)\\*\\*` with the `s` flag on a long\n * string of asterisks can exhibit super-linear backtracking. Slack\n * itself caps message bodies at 4000 chars (SLACK_MAX_LENGTH); we cap\n * the input here at 10x that as a defensive bound for any caller that\n * passes a longer rendering source through this helper before chunking. */\nconst MRKDWN_MAX_LENGTH = 40_000;\n\n/**\n * Convert standard markdown to Slack's mrkdwn dialect.\n * - `[text](url)` → `<url|text>`\n * - `**bold**` → `*bold*` (Slack uses single asterisks for bold)\n *\n * Inputs longer than MRKDWN_MAX_LENGTH are truncated before the regex\n * pass to bound worst-case backtracking on pathological input (L2 in the\n * webhook audit).\n */\nfunction markdownToSlackMrkdwn(text: string): string {\n const bounded =\n text.length > MRKDWN_MAX_LENGTH ? text.slice(0, MRKDWN_MAX_LENGTH) : text;\n return (\n bounded\n .replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, \"<$2|$1>\")\n // Bounded character class instead of `.+?` with the `s` flag — caps\n // each bold span at 5000 chars so an attacker can't construct a\n // pathological \"**\" sequence that exhibits super-linear backtracking.\n // Newlines are allowed because `[^*]` excludes only the asterisk\n // itself, so multi-line bold spans still match.\n .replace(/\\*\\*([^*]{1,5000})\\*\\*/g, \"*$1*\")\n );\n}\n\n/**\n * Optionally set Slack's native AI-assistant status indicator (the small\n * \"is thinking…\" line under the message composer) for an app configured\n * with the `assistant:write` scope. Pure best-effort — fails silently for\n * apps that aren't set up as AI assistants.\n */\nfunction setSlackAssistantStatus(\n token: string,\n channelId: string,\n threadTs: string,\n status: string,\n): void {\n fetch(\"https://slack.com/api/assistant.threads.setStatus\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n channel_id: channelId,\n thread_ts: threadTs,\n status,\n }),\n }).catch(() => {});\n}\n\n/**\n * Block Kit payload for the final answer. We avoid auto-unfurl previews by\n * separating the deep-link out into a button instead of inlining it as a\n * `<url|text>` markdown link in the section body — that's what was producing\n * the giant \"Agent-Native Dispatch\" card in every thread reply.\n */\nfunction buildResponseBlocks(\n text: string,\n opts: { threadDeepLinkUrl?: string },\n): unknown[] {\n const blocks: any[] = [\n {\n type: \"section\",\n text: { type: \"mrkdwn\", text: text || \"_(no response)_\" },\n },\n ];\n if (opts.threadDeepLinkUrl) {\n blocks.push({\n type: \"actions\",\n elements: [\n {\n type: \"button\",\n text: { type: \"plain_text\", text: \"Open thread\", emoji: true },\n url: opts.threadDeepLinkUrl,\n action_id: \"open_dispatch_thread\",\n },\n ],\n });\n }\n return blocks;\n}\n\n/**\n * Post a fresh message to a thread. Used as the placeholder-fallback path\n * (e.g. when chat.update fails) and for follow-up overflow chunks.\n */\nasync function postFresh(\n token: string,\n channelId: string,\n threadTs: string | undefined,\n body: Record<string, unknown>,\n): Promise<void> {\n const payload: Record<string, unknown> = {\n ...body,\n channel: channelId,\n };\n if (threadTs && !payload.thread_ts) payload.thread_ts = threadTs;\n const res = await fetch(\"https://slack.com/api/chat.postMessage\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(payload),\n });\n const data = (await res.json()) as { ok: boolean; error?: string };\n if (!data.ok) {\n console.error(\"[slack] chat.postMessage error:\", data.error);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"slack.js","sourceRoot":"","sources":["../../../src/integrations/adapters/slack.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,IAAI,CAAC;AAWzD,iCAAiC;AACjC,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,YAAY;IAC1B,OAAO;QACL,QAAQ,EAAE,OAAO;QACjB,KAAK,EAAE,OAAO;QAEd,kBAAkB;YAChB,OAAO;gBACL;oBACE,GAAG,EAAE,iBAAiB;oBACtB,KAAK,EAAE,iBAAiB;oBACxB,QAAQ,EAAE,IAAI;oBACd,QAAQ,EACN,iGAAiG;iBACpG;gBACD;oBACE,GAAG,EAAE,sBAAsB;oBAC3B,KAAK,EAAE,sBAAsB;oBAC7B,QAAQ,EAAE,IAAI;oBACd,QAAQ,EACN,qFAAqF;iBACxF;aACF,CAAC;QACJ,CAAC;QAED,KAAK,CAAC,kBAAkB,CACtB,KAAc;YAEd,kEAAkE;YAClE,uEAAuE;YACvE,oEAAoE;YACpE,2DAA2D;YAC3D,MAAM,IAAI,GAAG,MAAM,iBAAiB,CAAC,KAAK,CAAC,CAAC;YAC5C,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAChC,IAAI,MAAM,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;oBACvC,8DAA8D;oBAC9D,mEAAmE;oBACnE,8DAA8D;oBAC9D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC;gBACvD,CAAC;YACH,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;YACV,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAC5B,CAAC;QAED,KAAK,CAAC,aAAa,CAAC,KAAc;YAChC,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;YACvD,IAAI,CAAC,aAAa;gBAAE,OAAO,KAAK,CAAC;YAEjC,MAAM,SAAS,GAAG,SAAS,CAAC,KAAK,EAAE,mBAAmB,CAAC,CAAC;YACxD,MAAM,SAAS,GAAG,SAAS,CAAC,KAAK,EAAE,2BAA2B,CAAC,CAAC;YAChE,IAAI,CAAC,SAAS,IAAI,CAAC,SAAS;gBAAE,OAAO,KAAK,CAAC;YAE3C,2DAA2D;YAC3D,MAAM,EAAE,GAAG,QAAQ,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;YACnC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC,GAAG,GAAG;gBAAE,OAAO,KAAK,CAAC;YAEzD,MAAM,IAAI,GAAG,MAAM,iBAAiB,CAAC,KAAK,CAAC,CAAC;YAC5C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;YAC3C,MAAM,UAAU,GAAG,MAAM,SAAS,IAAI,IAAI,EAAE,CAAC;YAC7C,MAAM,iBAAiB,GACrB,KAAK;gBACL,MAAM;qBACH,UAAU,CAAC,QAAQ,EAAE,aAAa,CAAC;qBACnC,MAAM,CAAC,UAAU,CAAC;qBAClB,MAAM,CAAC,KAAK,CAAC,CAAC;YAEnB,yBAAyB;YACzB,IAAI,CAAC;gBACH,OAAO,MAAM,CAAC,eAAe,CAC3B,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EACtB,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAC/B,CAAC;YACJ,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;QAED,KAAK,CAAC,oBAAoB,CACxB,KAAc;YAEd,MAAM,GAAG,GAAG,MAAM,iBAAiB,CAAC,KAAK,CAAC,CAAC;YAC3C,IAAI,OAAY,CAAC;YACjB,IAAI,CAAC;gBACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC5B,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,CAAC;YACd,CAAC;YAED,kEAAkE;YAClE,sEAAsE;YACtE,qEAAqE;YACrE,mEAAmE;YACnE,oEAAoE;YACpE,oEAAoE;YACpE,mEAAmE;YACnE,iCAAiC;YACjC,yBAAyB,CAAC,OAAO,CAAC,CAAC;YAEnC,4BAA4B;YAC5B,IAAI,OAAO,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;gBACtC,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC;gBACxB,IAAI,CAAC,CAAC;oBAAE,OAAO,IAAI,CAAC;gBAEpB,sBAAsB;gBACtB,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,OAAO,KAAK,aAAa;oBAAE,OAAO,IAAI,CAAC;gBACzD,mCAAmC;gBACnC,IAAI,CAAC,CAAC,OAAO,KAAK,iBAAiB,IAAI,CAAC,CAAC,OAAO,KAAK,iBAAiB;oBACpE,OAAO,IAAI,CAAC;gBAEd,+CAA+C;gBAC/C,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC;gBAC5B,IAAI,CAAC,IAAI;oBAAE,OAAO,IAAI,CAAC;gBAEvB,+EAA+E;gBAC/E,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC3D,IAAI,CAAC,SAAS;oBAAE,OAAO,IAAI,CAAC;gBAE5B,gEAAgE;gBAChE,MAAM,QAAQ,GAAG,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,EAAE,CAAC;gBACrC,MAAM,gBAAgB,GAAG,GAAG,CAAC,CAAC,OAAO,IAAI,QAAQ,EAAE,CAAC;gBAEpD,OAAO;oBACL,QAAQ,EAAE,OAAO;oBACjB,gBAAgB;oBAChB,IAAI,EAAE,SAAS;oBACf,UAAU,EAAE,CAAC,CAAC,IAAI;oBAClB,QAAQ,EAAE,CAAC,CAAC,IAAI;oBAChB,eAAe,EAAE;wBACf,SAAS,EAAE,CAAC,CAAC,OAAO;wBACpB,QAAQ,EAAE,QAAQ;wBAClB,SAAS,EAAE,CAAC,CAAC,EAAE;wBACf,MAAM,EAAE,OAAO,CAAC,OAAO;wBACvB,OAAO,EAAE,OAAO,CAAC,QAAQ;qBAC1B;oBACD,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;iBAC/C,CAAC;YACJ,CAAC;YAED,OAAO,IAAI,CAAC;QACd,CAAC;QAED,KAAK,CAAC,yBAAyB,CAC7B,QAAyB;YAEzB,gEAAgE;YAChE,sEAAsE;YACtE,kEAAkE;YAClE,oEAAoE;YACpE,sEAAsE;YACtE,sCAAsC;YACtC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;YAC1C,IAAI,CAAC,KAAK;gBAAE,OAAO,IAAI,CAAC;YAExB,MAAM,SAAS,GAAG,QAAQ,CAAC,eAAe,CAAC,SAAmB,CAAC;YAC/D,MAAM,QAAQ,GAAG,QAAQ,CAAC,eAAe,CAAC,QAAkB,CAAC;YAC7D,IAAI,CAAC,SAAS,IAAI,CAAC,QAAQ;gBAAE,OAAO,IAAI,CAAC;YAEzC,sEAAsE;YACtE,qEAAqE;YACrE,mCAAmC;YACnC,uBAAuB,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC;YACpE,OAAO,IAAI,CAAC;QACd,CAAC;QAED,KAAK,CAAC,YAAY,CAChB,OAAwB,EACxB,OAAwB,EACxB,IAAkC;YAElC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;YAC1C,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC;gBACxD,OAAO;YACT,CAAC;YAED,MAAM,SAAS,GAAG,OAAO,CAAC,eAAe,CAAC,SAAmB,CAAC;YAC9D,MAAM,QAAQ,GAAG,OAAO,CAAC,eAAe,CAAC,QAAkB,CAAC;YAC5D,MAAM,MAAM,GAAI,OAAO,CAAC,eAAuB,EAAE,MAEpC,CAAC;YACd,MAAM,cAAc,GAAG,IAAI,EAAE,cAAc,CAAC;YAE5C,wEAAwE;YACxE,qEAAqE;YACrE,wEAAwE;YACxE,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;YAC5D,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YACnC,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAEnC,MAAM,WAAW,GACf,MAAM;gBACN,mBAAmB,CAAC,UAAU,EAAE;oBAC9B,iBAAiB,EAAG,OAAO,CAAC,eAAuB;wBACjD,EAAE,iBAAiB;iBACtB,CAAC,CAAC;YAEL,MAAM,QAAQ,GAA4B;gBACxC,OAAO,EAAE,SAAS;gBAClB,IAAI,EAAE,UAAU;gBAChB,MAAM,EAAE,WAAW;gBACnB,YAAY,EAAE,KAAK;gBACnB,YAAY,EAAE,KAAK;gBACnB,MAAM,EAAE,IAAI;aACb,CAAC;YAEF,IAAI,CAAC;gBACH,IAAI,cAAc,EAAE,CAAC;oBACnB,gDAAgD;oBAChD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,mCAAmC,EAAE;wBAC3D,MAAM,EAAE,MAAM;wBACd,OAAO,EAAE;4BACP,aAAa,EAAE,UAAU,KAAK,EAAE;4BAChC,cAAc,EAAE,kBAAkB;yBACnC;wBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,QAAQ,EAAE,EAAE,EAAE,cAAc,EAAE,CAAC;qBAC1D,CAAC,CAAC;oBACH,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAG7B,CAAC;oBACF,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;wBACb,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;wBACxD,2DAA2D;wBAC3D,MAAM,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;oBACxD,CAAC;gBACH,CAAC;qBAAM,CAAC;oBACN,MAAM,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;gBACxD,CAAC;gBAED,8DAA8D;gBAC9D,sDAAsD;gBACtD,IAAI,QAAQ,EAAE,CAAC;oBACb,uBAAuB,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;gBAC1D,CAAC;gBAED,uEAAuE;gBACvE,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;oBAC/B,MAAM,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE;wBAC1C,OAAO,EAAE,SAAS;wBAClB,IAAI,EAAE,KAAK;wBACX,YAAY,EAAE,KAAK;wBACnB,YAAY,EAAE,KAAK;wBACnB,MAAM,EAAE,IAAI;qBACb,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,GAAG,CAAC,CAAC;YACxD,CAAC;QACH,CAAC;QAED,KAAK,CAAC,mBAAmB,CACvB,OAAwB,EACxB,MAAsB;YAEtB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;YAC1C,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC;gBACxD,OAAO;YACT,CAAC;YAED,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;YAC5D,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBAC3B,MAAM,IAAI,GAA4B;oBACpC,OAAO,EAAE,MAAM,CAAC,WAAW;oBAC3B,IAAI,EAAE,KAAK;iBACZ,CAAC;gBACF,IAAI,MAAM,CAAC,SAAS;oBAAE,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;gBAExD,IAAI,CAAC;oBACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,wCAAwC,EAAE;wBAChE,MAAM,EAAE,MAAM;wBACd,OAAO,EAAE;4BACP,aAAa,EAAE,UAAU,KAAK,EAAE;4BAChC,cAAc,EAAE,kBAAkB;yBACnC;wBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;qBAC3B,CAAC,CAAC;oBACH,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAoC,CAAC;oBACnE,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;wBACb,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,yBAAyB,CAAC,CAAC;oBAC3D,CAAC;gBACH,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,OAAO,CAAC,KAAK,CAAC,2CAA2C,EAAE,GAAG,CAAC,CAAC;oBAChE,MAAM,GAAG,CAAC;gBACZ,CAAC;YACH,CAAC;QACH,CAAC;QAED,mBAAmB,CACjB,IAAY,EACZ,IAAqC;YAErC,OAAO;gBACL,IAAI,EAAE,qBAAqB,CAAC,IAAI,CAAC;gBACjC,eAAe,EAAE,IAAI,EAAE,iBAAiB;oBACtC,CAAC,CAAC,EAAE,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,EAAE;oBAC/C,CAAC,CAAC,EAAE;aACP,CAAC;QACJ,CAAC;QAED,KAAK,CAAC,SAAS,CAAC,OAAgB;YAC9B,MAAM,QAAQ,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;YAC/C,MAAM,SAAS,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;YACrD,MAAM,UAAU,GAAG,QAAQ,IAAI,SAAS,CAAC;YAEzC,OAAO;gBACL,QAAQ,EAAE,OAAO;gBACjB,KAAK,EAAE,OAAO;gBACd,OAAO,EAAE,KAAK,EAAE,uBAAuB;gBACvC,UAAU;gBACV,OAAO,EAAE;oBACP,QAAQ;oBACR,SAAS;iBACV;gBACD,KAAK,EAAE,CAAC,UAAU;oBAChB,CAAC,CAAC,kEAAkE;oBACpE,CAAC,CAAC,SAAS;aACd,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,iBAAiB,CAAC,IAAY;IACrC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,MAAM,MAAM,GAAG,GAAG;SACf,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC/B,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACrC,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;AACzB,CAAC;AAED,IAAI,uBAAuB,GAAG,KAAK,CAAC;AAEpC;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,SAAS,yBAAyB,CAAC,OAAY;IAC7C,MAAM,MAAM,GACV,OAAO,OAAO,EAAE,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IACrE,MAAM,QAAQ,GACZ,OAAO,OAAO,EAAE,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IAE3E,MAAM,cAAc,GAAG,iBAAiB,CAAC,wBAAwB,CAAC,CAAC;IACnE,MAAM,aAAa,GAAG,iBAAiB,CAAC,2BAA2B,CAAC,CAAC;IAErE,IAAI,cAAc,EAAE,CAAC;QACnB,IAAI,CAAC,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3C,MAAM,WAAW,CAAC;gBAChB,UAAU,EAAE,GAAG;gBACf,aAAa,EAAE,8BAA8B;aAC9C,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,IAAI,aAAa,EAAE,CAAC;QAClB,IAAI,CAAC,QAAQ,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9C,MAAM,WAAW,CAAC;gBAChB,UAAU,EAAE,GAAG;gBACf,aAAa,EAAE,8BAA8B;aAC9C,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,IACE,CAAC,cAAc;QACf,CAAC,aAAa;QACd,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY;QACrC,CAAC,uBAAuB,EACxB,CAAC;QACD,uBAAuB,GAAG,IAAI,CAAC;QAC/B,OAAO,CAAC,IAAI,CACV,2IAA2I;YACzI,sJAAsJ,CACzJ,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,KAAK,UAAU,iBAAiB,CAAC,KAAc;IAC7C,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC;IACvC,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC;IAC9C,wEAAwE;IACxE,wEAAwE;IACxE,gDAAgD;IAChD,MAAM,GAAG,GAAG,CAAC,MAAM,WAAW,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7C,KAAK,CAAC,OAAO,CAAC,SAAS,GAAG,GAAG,CAAC;IAC9B,OAAO,GAAG,CAAC;AACb,CAAC;AAED,uEAAuE;AACvE,SAAS,YAAY,CAAC,IAAY,EAAE,SAAiB;IACnD,IAAI,IAAI,CAAC,MAAM,IAAI,SAAS;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,SAAS,GAAG,IAAI,CAAC;IACrB,OAAO,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,IAAI,SAAS,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC;YAClC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACvB,MAAM;QACR,CAAC;QACD,4BAA4B;QAC5B,IAAI,QAAQ,GAAG,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACtD,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;YAClB,0BAA0B;YAC1B,QAAQ,GAAG,SAAS,CAAC,WAAW,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QACnD,CAAC;QACD,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;YAClB,QAAQ,GAAG,SAAS,CAAC;QACvB,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;QAC1C,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,SAAS,EAAE,CAAC;IACpD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;4EAK4E;AAC5E,MAAM,iBAAiB,GAAG,MAAM,CAAC;AAEjC;;;;;;;;GAQG;AACH,SAAS,qBAAqB,CAAC,IAAY;IACzC,MAAM,OAAO,GACX,IAAI,CAAC,MAAM,GAAG,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5E,OAAO,CACL,OAAO;SACJ,OAAO,CAAC,0BAA0B,EAAE,SAAS,CAAC;QAC/C,oEAAoE;QACpE,gEAAgE;QAChE,sEAAsE;QACtE,iEAAiE;QACjE,gDAAgD;SAC/C,OAAO,CAAC,yBAAyB,EAAE,MAAM,CAAC,CAC9C,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,uBAAuB,CAC9B,KAAa,EACb,SAAiB,EACjB,QAAgB,EAChB,MAAc;IAEd,KAAK,CAAC,mDAAmD,EAAE;QACzD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,aAAa,EAAE,UAAU,KAAK,EAAE;YAChC,cAAc,EAAE,kBAAkB;SACnC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,UAAU,EAAE,SAAS;YACrB,SAAS,EAAE,QAAQ;YACnB,MAAM;SACP,CAAC;KACH,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AACrB,CAAC;AAED;;;;;GAKG;AACH,SAAS,mBAAmB,CAC1B,IAAY,EACZ,IAAoC;IAEpC,MAAM,MAAM,GAAU;QACpB;YACE,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,IAAI,iBAAiB,EAAE;SAC1D;KACF,CAAC;IACF,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC3B,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,SAAS;YACf,QAAQ,EAAE;gBACR;oBACE,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,IAAI,EAAE;oBAC9D,GAAG,EAAE,IAAI,CAAC,iBAAiB;oBAC3B,SAAS,EAAE,sBAAsB;iBAClC;aACF;SACF,CAAC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,SAAS,CACtB,KAAa,EACb,SAAiB,EACjB,QAA4B,EAC5B,IAA6B;IAE7B,MAAM,OAAO,GAA4B;QACvC,GAAG,IAAI;QACP,OAAO,EAAE,SAAS;KACnB,CAAC;IACF,IAAI,QAAQ,IAAI,CAAC,OAAO,CAAC,SAAS;QAAE,OAAO,CAAC,SAAS,GAAG,QAAQ,CAAC;IACjE,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,wCAAwC,EAAE;QAChE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,aAAa,EAAE,UAAU,KAAK,EAAE;YAChC,cAAc,EAAE,kBAAkB;SACnC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;KAC9B,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAoC,CAAC;IACnE,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IAC/D,CAAC;AACH,CAAC","sourcesContent":["import type { H3Event } from \"h3\";\nimport { createError, getHeader, readRawBody } from \"h3\";\nimport type {\n PlatformAdapter,\n IncomingMessage,\n OutgoingMessage,\n IntegrationStatus,\n OutboundTarget,\n} from \"../types.js\";\nimport type { EnvKeyConfig } from \"../../server/create-server.js\";\nimport { getIntegrationConfig } from \"../config-store.js\";\n\n/** Slack's max message length */\nconst SLACK_MAX_LENGTH = 4000;\n\n/**\n * Create a Slack platform adapter.\n *\n * Required env vars:\n * - SLACK_BOT_TOKEN — Bot user OAuth token (xoxb-...)\n * - SLACK_SIGNING_SECRET — Used to verify webhook signatures\n *\n * Optional env vars:\n * - SLACK_ALLOWED_TEAM_IDS — Comma-separated list of Slack workspace\n * `team_id` values (e.g. \"T012ABCDEF,T034GHIJKL\") that this deployment\n * accepts events from. Strongly recommended in multi-tenant deployments\n * to prevent cross-workspace event injection (H1 in the webhook audit):\n * the global `SLACK_SIGNING_SECRET` is the same key for every workspace\n * the app is installed to, so without an allowlist any installed\n * workspace can drive the agent. When unset the adapter accepts events\n * from any workspace — fine for single-tenant dev, unsafe for prod.\n * - SLACK_ALLOWED_API_APP_IDS — Comma-separated list of Slack app IDs\n * (`api_app_id`) to additionally pin events to. Useful when the same\n * signing secret rotation surfaces multiple app installs.\n */\nexport function slackAdapter(): PlatformAdapter {\n return {\n platform: \"slack\",\n label: \"Slack\",\n\n getRequiredEnvKeys(): EnvKeyConfig[] {\n return [\n {\n key: \"SLACK_BOT_TOKEN\",\n label: \"Slack Bot Token\",\n required: true,\n helpText:\n \"In your Slack app's left nav: OAuth & Permissions → Bot User OAuth Token (starts with `xoxb-`).\",\n },\n {\n key: \"SLACK_SIGNING_SECRET\",\n label: \"Slack Signing Secret\",\n required: true,\n helpText:\n \"In your Slack app's left nav: Basic Information → App Credentials → Signing Secret.\",\n },\n ];\n },\n\n async handleVerification(\n event: H3Event,\n ): Promise<{ handled: boolean; response?: unknown }> {\n // Slack sends url_verification when first setting up the webhook.\n // readRawBodyCached caches the raw bytes on event.context.__rawBody so\n // subsequent verifyWebhook + parseIncomingMessage calls re-use them\n // without re-stringifying a parsed body (M2 in the audit).\n const body = await readRawBodyCached(event);\n try {\n const parsed = JSON.parse(body);\n if (parsed.type === \"url_verification\") {\n // Slack's URL verifier expects the raw challenge value in the\n // response body. Returning JSON works for some clients but the app\n // settings verifier rejects it as not matching the challenge.\n return { handled: true, response: parsed.challenge };\n }\n } catch {}\n return { handled: false };\n },\n\n async verifyWebhook(event: H3Event): Promise<boolean> {\n const signingSecret = process.env.SLACK_SIGNING_SECRET;\n if (!signingSecret) return false;\n\n const signature = getHeader(event, \"x-slack-signature\");\n const timestamp = getHeader(event, \"x-slack-request-timestamp\");\n if (!signature || !timestamp) return false;\n\n // Reject requests older than 5 minutes (replay protection)\n const ts = parseInt(timestamp, 10);\n if (Math.abs(Date.now() / 1000 - ts) > 300) return false;\n\n const body = await readRawBodyCached(event);\n const crypto = await import(\"node:crypto\");\n const basestring = `v0:${timestamp}:${body}`;\n const expectedSignature =\n \"v0=\" +\n crypto\n .createHmac(\"sha256\", signingSecret)\n .update(basestring)\n .digest(\"hex\");\n\n // Timing-safe comparison\n try {\n return crypto.timingSafeEqual(\n Buffer.from(signature),\n Buffer.from(expectedSignature),\n );\n } catch {\n return false;\n }\n },\n\n async parseIncomingMessage(\n event: H3Event,\n ): Promise<IncomingMessage | null> {\n const raw = await readRawBodyCached(event);\n let payload: any;\n try {\n payload = JSON.parse(raw);\n } catch {\n return null;\n }\n\n // H1 (webhook audit): cross-workspace event injection. The global\n // SLACK_SIGNING_SECRET is the same key for every workspace this Slack\n // app is installed to — without a per-tenant allowlist any installed\n // workspace can drive the agent. We enforce SLACK_ALLOWED_TEAM_IDS\n // here AFTER the signature has already been verified by the webhook\n // handler, so this is purely a tenant-isolation gate (not a forgery\n // defense). When unset in production we surface a one-time warning\n // recommending it be configured.\n enforceWorkspaceAllowlist(payload);\n\n // Handle Events API wrapper\n if (payload.type === \"event_callback\") {\n const e = payload.event;\n if (!e) return null;\n\n // Ignore bot messages\n if (e.bot_id || e.subtype === \"bot_message\") return null;\n // Ignore message edits and deletes\n if (e.subtype === \"message_changed\" || e.subtype === \"message_deleted\")\n return null;\n\n // Handle both direct messages and app_mentions\n const text = e.text?.trim();\n if (!text) return null;\n\n // Remove bot mention from text (e.g., \"<@U123> do something\" → \"do something\")\n const cleanText = text.replace(/<@[A-Z0-9]+>/g, \"\").trim();\n if (!cleanText) return null;\n\n // Thread ID: use thread_ts if in a thread, otherwise message ts\n const threadTs = e.thread_ts || e.ts;\n const externalThreadId = `${e.channel}:${threadTs}`;\n\n return {\n platform: \"slack\",\n externalThreadId,\n text: cleanText,\n senderName: e.user,\n senderId: e.user,\n platformContext: {\n channelId: e.channel,\n threadTs: threadTs,\n messageTs: e.ts,\n teamId: payload.team_id,\n eventId: payload.event_id,\n },\n timestamp: Math.floor(parseFloat(e.ts) * 1000),\n };\n }\n\n return null;\n },\n\n async postProcessingPlaceholder(\n incoming: IncomingMessage,\n ): Promise<{ placeholderRef: string } | null> {\n // No placeholder reply in the thread — Slack's native assistant\n // status bar (\"agent-native is thinking…\", below the composer) is the\n // loading affordance. A second visible \"Working on it…\" reply was\n // redundant and added an extra chunk that we then had to overwrite.\n // We just set the native status and return null so sendResponse posts\n // the final reply as a fresh message.\n const token = process.env.SLACK_BOT_TOKEN;\n if (!token) return null;\n\n const channelId = incoming.platformContext.channelId as string;\n const threadTs = incoming.platformContext.threadTs as string;\n if (!channelId || !threadTs) return null;\n\n // Best-effort: flip the native AI-assistant \"is thinking…\" status bar\n // in the channel input area. Requires `assistant:write` scope on the\n // app — otherwise silently no-ops.\n setSlackAssistantStatus(token, channelId, threadTs, \"is thinking…\");\n return null;\n },\n\n async sendResponse(\n message: OutgoingMessage,\n context: IncomingMessage,\n opts?: { placeholderRef?: string },\n ): Promise<void> {\n const token = process.env.SLACK_BOT_TOKEN;\n if (!token) {\n console.error(\"[slack] SLACK_BOT_TOKEN not configured\");\n return;\n }\n\n const channelId = context.platformContext.channelId as string;\n const threadTs = context.platformContext.threadTs as string;\n const blocks = (message.platformContext as any)?.blocks as\n | unknown[]\n | undefined;\n const placeholderRef = opts?.placeholderRef;\n\n // Block-rich path: split text into chunks but render the FIRST chunk as\n // blocks (so we keep the in-place edit + button) and any overflow as\n // plain follow-up posts. The vast majority of replies fit in one block.\n const chunks = splitMessage(message.text, SLACK_MAX_LENGTH);\n const firstChunk = chunks[0] ?? \"\";\n const restChunks = chunks.slice(1);\n\n const finalBlocks =\n blocks ??\n buildResponseBlocks(firstChunk, {\n threadDeepLinkUrl: (message.platformContext as any)\n ?.threadDeepLinkUrl,\n });\n\n const baseBody: Record<string, unknown> = {\n channel: channelId,\n text: firstChunk,\n blocks: finalBlocks,\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n };\n\n try {\n if (placeholderRef) {\n // Replace the \"thinking…\" placeholder in place.\n const res = await fetch(\"https://slack.com/api/chat.update\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ ...baseBody, ts: placeholderRef }),\n });\n const data = (await res.json()) as {\n ok: boolean;\n error?: string;\n };\n if (!data.ok) {\n console.error(\"[slack] chat.update error:\", data.error);\n // Fall back to a fresh post so the user still sees a reply\n await postFresh(token, channelId, threadTs, baseBody);\n }\n } else {\n await postFresh(token, channelId, threadTs, baseBody);\n }\n\n // Clear the AI-assistant \"is thinking…\" status now that we've\n // delivered the final answer. Empty status clears it.\n if (threadTs) {\n setSlackAssistantStatus(token, channelId, threadTs, \"\");\n }\n\n // Overflow chunks (rare) — post as plain follow-ups in the same thread\n for (const chunk of restChunks) {\n await postFresh(token, channelId, threadTs, {\n channel: channelId,\n text: chunk,\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n });\n }\n } catch (err) {\n console.error(\"[slack] Failed to send message:\", err);\n }\n },\n\n async sendMessageToTarget(\n message: OutgoingMessage,\n target: OutboundTarget,\n ): Promise<void> {\n const token = process.env.SLACK_BOT_TOKEN;\n if (!token) {\n console.error(\"[slack] SLACK_BOT_TOKEN not configured\");\n return;\n }\n\n const chunks = splitMessage(message.text, SLACK_MAX_LENGTH);\n for (const chunk of chunks) {\n const body: Record<string, unknown> = {\n channel: target.destination,\n text: chunk,\n };\n if (target.threadRef) body.thread_ts = target.threadRef;\n\n try {\n const res = await fetch(\"https://slack.com/api/chat.postMessage\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n const data = (await res.json()) as { ok: boolean; error?: string };\n if (!data.ok) {\n throw new Error(data.error || \"chat.postMessage failed\");\n }\n } catch (err) {\n console.error(\"[slack] Failed to send proactive message:\", err);\n throw err;\n }\n }\n },\n\n formatAgentResponse(\n text: string,\n opts?: { threadDeepLinkUrl?: string },\n ): OutgoingMessage {\n return {\n text: markdownToSlackMrkdwn(text),\n platformContext: opts?.threadDeepLinkUrl\n ? { threadDeepLinkUrl: opts.threadDeepLinkUrl }\n : {},\n };\n },\n\n async getStatus(baseUrl?: string): Promise<IntegrationStatus> {\n const hasToken = !!process.env.SLACK_BOT_TOKEN;\n const hasSecret = !!process.env.SLACK_SIGNING_SECRET;\n const configured = hasToken && hasSecret;\n\n return {\n platform: \"slack\",\n label: \"Slack\",\n enabled: false, // overridden by plugin\n configured,\n details: {\n hasToken,\n hasSecret,\n },\n error: !configured\n ? \"Set SLACK_BOT_TOKEN and SLACK_SIGNING_SECRET in your environment\"\n : undefined,\n };\n },\n };\n}\n\n/**\n * Parse a comma-separated env var into a Set of trimmed, non-empty values.\n * Returns null when the env var is unset or empty (so callers can\n * distinguish \"no allowlist configured\" from \"empty allowlist\").\n */\nfunction parseAllowlistEnv(name: string): Set<string> | null {\n const raw = process.env[name];\n if (!raw) return null;\n const values = raw\n .split(\",\")\n .map((v) => v.trim())\n .filter((v) => v.length > 0);\n if (values.length === 0) return null;\n return new Set(values);\n}\n\nlet _missingAllowlistWarned = false;\n\n/**\n * Enforce that an incoming Slack event comes from an allowlisted workspace.\n *\n * H1 in the webhook audit: the framework uses a SINGLE global\n * SLACK_SIGNING_SECRET for every workspace the Slack app is installed to,\n * so a valid signature alone doesn't prove the request belongs to the\n * tenant the deployment intends to serve. This helper layers a per-tenant\n * allowlist on top of signature verification.\n *\n * Behavior:\n * - If `SLACK_ALLOWED_TEAM_IDS` is set: reject any payload whose\n * `team_id` isn't in the list.\n * - If `SLACK_ALLOWED_API_APP_IDS` is set: also reject payloads whose\n * `api_app_id` isn't in the list (bot apps can be installed under the\n * same Slack app id across multiple workspaces — pinning both keeps\n * the surface tight when team_id allows multiple workspaces).\n * - If neither is set AND `NODE_ENV === \"production\"`: log a one-time\n * warning recommending the env var be configured. Continue (preserves\n * existing behavior to avoid breaking single-tenant prod deployments\n * that have always run without an allowlist).\n * - If neither is set in dev / single-tenant: accept (current behavior).\n *\n * Throws an h3 401 error when an allowlisted-but-mismatched payload is\n * received, which the integrations plugin surfaces to the caller as\n * \"Unrecognized Slack workspace\" without enqueuing the event.\n */\nfunction enforceWorkspaceAllowlist(payload: any): void {\n const teamId =\n typeof payload?.team_id === \"string\" ? payload.team_id : undefined;\n const apiAppId =\n typeof payload?.api_app_id === \"string\" ? payload.api_app_id : undefined;\n\n const allowedTeamIds = parseAllowlistEnv(\"SLACK_ALLOWED_TEAM_IDS\");\n const allowedAppIds = parseAllowlistEnv(\"SLACK_ALLOWED_API_APP_IDS\");\n\n if (allowedTeamIds) {\n if (!teamId || !allowedTeamIds.has(teamId)) {\n throw createError({\n statusCode: 401,\n statusMessage: \"Unrecognized Slack workspace\",\n });\n }\n }\n\n if (allowedAppIds) {\n if (!apiAppId || !allowedAppIds.has(apiAppId)) {\n throw createError({\n statusCode: 401,\n statusMessage: \"Unrecognized Slack workspace\",\n });\n }\n }\n\n if (\n !allowedTeamIds &&\n !allowedAppIds &&\n process.env.NODE_ENV === \"production\" &&\n !_missingAllowlistWarned\n ) {\n _missingAllowlistWarned = true;\n console.warn(\n \"[slack] SLACK_ALLOWED_TEAM_IDS not set in production — accepting events from any workspace whose signature matches SLACK_SIGNING_SECRET. \" +\n \"Set SLACK_ALLOWED_TEAM_IDS to a comma-separated list of allowed team_id values to prevent cross-workspace event injection (H1 in the webhook audit).\",\n );\n }\n}\n\n/**\n * Read the raw request body as a string and cache on the event context.\n *\n * This MUST read raw bytes from the request stream — never `JSON.stringify`\n * a parsed body, because Slack's HMAC is computed over the exact bytes Slack\n * sent. Re-stringifying a parsed object loses key ordering, whitespace, and\n * Unicode-escape choices, so the signature check would silently fail for\n * legitimate requests (M2 in the webhook security audit).\n *\n * h3 v2's body stream is consume-once, so we cache the raw string on the\n * event context after the first read. All call sites (handleVerification,\n * verifyWebhook, parseIncomingMessage) MUST go through this helper.\n */\nasync function readRawBodyCached(event: H3Event): Promise<string> {\n const cached = event.context.__rawBody;\n if (typeof cached === \"string\") return cached;\n // h3's readRawBody returns the bytes Slack actually sent, defaulting to\n // utf8-decoded. Returns undefined for empty bodies — we coerce to \"\" so\n // the HMAC check can proceed deterministically.\n const raw = (await readRawBody(event)) ?? \"\";\n event.context.__rawBody = raw;\n return raw;\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 // Try to split at a newline\n let splitIdx = remaining.lastIndexOf(\"\\n\", maxLength);\n if (splitIdx <= 0) {\n // Try to split at a space\n splitIdx = remaining.lastIndexOf(\" \", maxLength);\n }\n if (splitIdx <= 0) {\n splitIdx = maxLength;\n }\n chunks.push(remaining.slice(0, splitIdx));\n remaining = remaining.slice(splitIdx).trimStart();\n }\n return chunks;\n}\n\n/** Hard cap on input length we feed to the regex-based mrkdwn converter.\n * L2 in the webhook audit: `\\*\\*(.+?)\\*\\*` with the `s` flag on a long\n * string of asterisks can exhibit super-linear backtracking. Slack\n * itself caps message bodies at 4000 chars (SLACK_MAX_LENGTH); we cap\n * the input here at 10x that as a defensive bound for any caller that\n * passes a longer rendering source through this helper before chunking. */\nconst MRKDWN_MAX_LENGTH = 40_000;\n\n/**\n * Convert standard markdown to Slack's mrkdwn dialect.\n * - `[text](url)` → `<url|text>`\n * - `**bold**` → `*bold*` (Slack uses single asterisks for bold)\n *\n * Inputs longer than MRKDWN_MAX_LENGTH are truncated before the regex\n * pass to bound worst-case backtracking on pathological input (L2 in the\n * webhook audit).\n */\nfunction markdownToSlackMrkdwn(text: string): string {\n const bounded =\n text.length > MRKDWN_MAX_LENGTH ? text.slice(0, MRKDWN_MAX_LENGTH) : text;\n return (\n bounded\n .replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, \"<$2|$1>\")\n // Bounded character class instead of `.+?` with the `s` flag — caps\n // each bold span at 5000 chars so an attacker can't construct a\n // pathological \"**\" sequence that exhibits super-linear backtracking.\n // Newlines are allowed because `[^*]` excludes only the asterisk\n // itself, so multi-line bold spans still match.\n .replace(/\\*\\*([^*]{1,5000})\\*\\*/g, \"*$1*\")\n );\n}\n\n/**\n * Optionally set Slack's native AI-assistant status indicator (the small\n * \"is thinking…\" line under the message composer) for an app configured\n * with the `assistant:write` scope. Pure best-effort — fails silently for\n * apps that aren't set up as AI assistants.\n */\nfunction setSlackAssistantStatus(\n token: string,\n channelId: string,\n threadTs: string,\n status: string,\n): void {\n fetch(\"https://slack.com/api/assistant.threads.setStatus\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n channel_id: channelId,\n thread_ts: threadTs,\n status,\n }),\n }).catch(() => {});\n}\n\n/**\n * Block Kit payload for the final answer. We avoid auto-unfurl previews by\n * separating the deep-link out into a button instead of inlining it as a\n * `<url|text>` markdown link in the section body — that's what was producing\n * the giant \"Agent-Native Dispatch\" card in every thread reply.\n */\nfunction buildResponseBlocks(\n text: string,\n opts: { threadDeepLinkUrl?: string },\n): unknown[] {\n const blocks: any[] = [\n {\n type: \"section\",\n text: { type: \"mrkdwn\", text: text || \"_(no response)_\" },\n },\n ];\n if (opts.threadDeepLinkUrl) {\n blocks.push({\n type: \"actions\",\n elements: [\n {\n type: \"button\",\n text: { type: \"plain_text\", text: \"Open thread\", emoji: true },\n url: opts.threadDeepLinkUrl,\n action_id: \"open_dispatch_thread\",\n },\n ],\n });\n }\n return blocks;\n}\n\n/**\n * Post a fresh message to a thread. Used as the placeholder-fallback path\n * (e.g. when chat.update fails) and for follow-up overflow chunks.\n */\nasync function postFresh(\n token: string,\n channelId: string,\n threadTs: string | undefined,\n body: Record<string, unknown>,\n): Promise<void> {\n const payload: Record<string, unknown> = {\n ...body,\n channel: channelId,\n };\n if (threadTs && !payload.thread_ts) payload.thread_ts = threadTs;\n const res = await fetch(\"https://slack.com/api/chat.postMessage\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(payload),\n });\n const data = (await res.json()) as { ok: boolean; error?: string };\n if (!data.ok) {\n console.error(\"[slack] chat.postMessage error:\", data.error);\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/integrations/plugin.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAEV,yBAAyB,EAE1B,MAAM,YAAY,CAAC;AA6BpB,KAAK,cAAc,GAAG,CAAC,QAAQ,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAuG9D;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,CAAC,EAAE,yBAAyB,GAClC,cAAc,
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/integrations/plugin.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAEV,yBAAyB,EAE1B,MAAM,YAAY,CAAC;AA6BpB,KAAK,cAAc,GAAG,CAAC,QAAQ,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAuG9D;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,CAAC,EAAE,yBAAyB,GAClC,cAAc,CAmfhB;AAED;;GAEG;AACH,eAAO,MAAM,yBAAyB,gBAA6B,CAAC"}
|
|
@@ -307,9 +307,10 @@ export function createIntegrationsPlugin(options) {
|
|
|
307
307
|
setResponseStatus(event, 404);
|
|
308
308
|
return { error: "Unknown platform" };
|
|
309
309
|
}
|
|
310
|
+
const resources = await loadResourcesForPrompt(task.ownerEmail);
|
|
310
311
|
await processIntegrationTask(task, {
|
|
311
312
|
adapter,
|
|
312
|
-
systemPrompt: baseSystemPrompt,
|
|
313
|
+
systemPrompt: baseSystemPrompt + resources,
|
|
313
314
|
actions,
|
|
314
315
|
model,
|
|
315
316
|
apiKey: getApiKey(),
|