@opentag/slack 0.1.0 → 0.3.0

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/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/normalize.ts"],"sourcesContent":["import { commandFromRawText, type OpenTagEvent, type PermissionGrant } from \"@opentag/core\";\n\nexport type SlackChannelBinding = {\n teamId: string;\n channelId: string;\n owner: string;\n repo: string;\n};\n\nexport type SlackAppMentionInput = {\n teamId: string;\n channelId: string;\n userId: string;\n text: string;\n ts: string;\n threadTs?: string;\n eventId: string;\n eventTime: number;\n botUserId?: string;\n callbackUri?: string;\n binding: SlackChannelBinding;\n};\n\nexport function stripSlackAppMention(text: string, botUserId?: string): string | null {\n const patterns = botUserId\n ? [new RegExp(`^<@${botUserId}>\\\\s*`, \"i\"), /^<@[^>]+>\\s*/]\n : [/^<@[^>]+>\\s*/];\n\n for (const pattern of patterns) {\n const stripped = text.replace(pattern, \"\").trim();\n if (stripped !== text.trim()) {\n return stripped.length > 0 ? stripped : null;\n }\n }\n\n return null;\n}\n\nexport function encodeSlackThreadKey(input: { teamId: string; channelId: string; threadTs: string }): string {\n return `${input.teamId}|${input.channelId}|${input.threadTs}`;\n}\n\nexport function parseSlackThreadKey(threadKey: string): { teamId: string; channelId: string; threadTs: string } {\n const [teamId, channelId, threadTs] = threadKey.split(\"|\");\n if (!teamId || !channelId || !threadTs) {\n throw new Error(`Invalid Slack thread key: ${threadKey}`);\n }\n return { teamId, channelId, threadTs };\n}\n\nfunction permissionsForIntent(intent: ReturnType<typeof commandFromRawText>[\"intent\"]): PermissionGrant[] {\n const permissions: PermissionGrant[] = [\n {\n scope: \"chat:postMessage\",\n reason: \"reply in the originating Slack thread\"\n },\n {\n scope: \"runner:local\",\n reason: \"execute the run on a paired local daemon\"\n }\n ];\n\n if (intent === \"fix\" || intent === \"run\") {\n permissions.push(\n {\n scope: \"repo:read\",\n reason: \"inspect the repository in the paired local checkout\"\n },\n {\n scope: \"repo:write\",\n reason: \"commit code changes on an isolated run branch\"\n },\n {\n scope: \"pr:create\",\n reason: \"open a pull request for completed code changes\"\n }\n );\n }\n\n return permissions;\n}\n\nexport function normalizeSlackAppMention(input: SlackAppMentionInput): OpenTagEvent | null {\n const rawText = stripSlackAppMention(input.text, input.botUserId);\n if (!rawText) return null;\n\n const command = commandFromRawText(rawText);\n const replyThreadTs = input.threadTs ?? input.ts;\n\n return {\n id: `evt_slack_app_mention_${input.eventId}`,\n source: \"slack\",\n sourceEventId: input.eventId,\n receivedAt: new Date(input.eventTime * 1000).toISOString(),\n actor: {\n provider: \"slack\",\n providerUserId: input.userId,\n handle: input.userId,\n organizationId: input.teamId\n },\n target: {\n mention: input.botUserId ? `<@${input.botUserId}>` : \"<@app>\",\n agentId: \"opentag\"\n },\n command,\n context: [\n {\n kind: \"url\",\n uri: `slack://team/${input.teamId}/channel/${input.channelId}/message/${input.ts}`,\n visibility: \"organization\",\n title: \"Slack message\"\n },\n {\n kind: \"text\",\n uri: input.text,\n visibility: \"organization\",\n title: \"Slack message text\"\n }\n ],\n permissions: permissionsForIntent(command.intent),\n callback: {\n provider: \"slack\",\n uri: input.callbackUri ?? \"https://slack.com/api/chat.postMessage\",\n threadKey: encodeSlackThreadKey({\n teamId: input.teamId,\n channelId: input.channelId,\n threadTs: replyThreadTs\n })\n },\n metadata: {\n teamId: input.teamId,\n channelId: input.channelId,\n messageTs: input.ts,\n repoProvider: \"github\",\n owner: input.binding.owner,\n repo: input.binding.repo\n }\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/events.ts","../src/normalize.ts","../src/render.ts","../src/ingress.ts","../src/dispatcher-events.ts","../src/socket-mode.ts"],"sourcesContent":["import { parseThreadActionCommand, type OpenTagEvent } from \"@opentag/core\";\nimport { encodeSlackThreadKey, normalizeSlackAppMention, stripSlackAppMention, type SlackChannelBinding } from \"./normalize.js\";\nimport { parseSlackSuggestedActionButtonValue } from \"./render.js\";\n\nexport type SlackThreadActionInput = {\n id: string;\n rawText: string;\n actor: {\n provider: \"slack\";\n providerUserId: string;\n handle: string;\n organizationId: string;\n };\n callback: {\n provider: \"slack\";\n uri: string;\n threadKey: string;\n };\n metadata: Record<string, unknown>;\n};\n\nexport type SlackEventEnvelope = {\n token?: string;\n type: \"url_verification\" | \"event_callback\";\n challenge?: string;\n team_id?: string;\n api_app_id?: string;\n event?: {\n type: string;\n user?: string;\n text?: string;\n ts?: string;\n thread_ts?: string;\n channel?: string;\n subtype?: string;\n bot_id?: string;\n };\n event_id?: string;\n event_time?: number;\n authorizations?: Array<{ user_id?: string }>;\n};\n\nexport type SlackInteractiveBlockAction = {\n type?: string;\n action_id?: string;\n block_id?: string;\n value?: string;\n action_ts?: string;\n};\n\nexport type SlackInteractivePayload = {\n type: \"block_actions\";\n api_app_id?: string;\n team?: { id?: string; domain?: string };\n user?: { id?: string; username?: string; name?: string };\n channel?: { id?: string; name?: string };\n message?: { ts?: string; thread_ts?: string };\n container?: { type?: string; channel_id?: string; message_ts?: string; thread_ts?: string };\n trigger_id?: string;\n actions?: SlackInteractiveBlockAction[];\n};\n\nexport type SlackIngressPayload = SlackEventEnvelope | SlackInteractivePayload;\n\nexport type SlackAppRuntimeConfig = {\n agentId: string;\n appId?: string;\n callbackUri?: string;\n};\n\nexport type SlackEventProcessorInput = {\n resolveChannelBinding(input: { teamId: string; channelId: string }): Promise<SlackChannelBinding | null>;\n createRun(event: OpenTagEvent): Promise<{ runId: string }>;\n submitThreadAction?(action: SlackThreadActionInput): Promise<unknown>;\n now(): string;\n};\n\nexport type SlackEventProcessorStatus = 200 | 400;\n\nexport type SlackEventProcessorResult =\n | {\n kind: \"json\";\n status: SlackEventProcessorStatus;\n body: Record<string, unknown>;\n }\n | {\n kind: \"text\";\n status: SlackEventProcessorStatus;\n body: string;\n };\n\nfunction json(body: Record<string, unknown>, status: SlackEventProcessorStatus = 200): SlackEventProcessorResult {\n return { kind: \"json\", status, body };\n}\n\nfunction text(body: string, status: SlackEventProcessorStatus = 200): SlackEventProcessorResult {\n return { kind: \"text\", status, body };\n}\n\nexport function createSlackEventProcessor(input: SlackEventProcessorInput) {\n async function processBlockActions(payload: SlackInteractivePayload, slackApp: SlackAppRuntimeConfig): Promise<SlackEventProcessorResult> {\n const action = payload.actions?.find((candidate) => {\n if (candidate.action_id?.startsWith(\"opentag:\")) return true;\n return typeof candidate.value === \"string\" && parseSlackSuggestedActionButtonValue(candidate.value) !== null;\n });\n if (!action) {\n return json({ ok: true });\n }\n\n const parsedValue = typeof action.value === \"string\" ? parseSlackSuggestedActionButtonValue(action.value) : null;\n const rawText =\n parsedValue?.command ??\n (typeof action.value === \"string\" && parseThreadActionCommand(action.value) ? action.value.trim() : undefined);\n if (!rawText || !parseThreadActionCommand(rawText)) {\n return json({ error: \"invalid_interactive_action\" }, 400);\n }\n if (!input.submitThreadAction) {\n return json({ ok: true });\n }\n\n const teamId = payload.team?.id;\n const userId = payload.user?.id;\n const channelId = payload.channel?.id ?? payload.container?.channel_id;\n const messageTs = payload.message?.ts ?? payload.container?.message_ts;\n const threadTs = payload.message?.thread_ts ?? payload.container?.thread_ts ?? messageTs;\n if (!teamId || !userId || !channelId || !messageTs || !threadTs) {\n return json({ error: \"invalid_interactive_payload\" }, 400);\n }\n\n const binding = await input.resolveChannelBinding({\n teamId,\n channelId\n });\n if (!binding) {\n return json({ ok: true, ignored: \"unbound_channel\" });\n }\n\n await input.submitThreadAction({\n id: `approval_slack_block_${payload.trigger_id ?? `${action.action_id ?? \"action\"}_${action.action_ts ?? messageTs}`}`,\n rawText,\n actor: {\n provider: \"slack\",\n providerUserId: userId,\n handle: payload.user?.username ?? payload.user?.name ?? userId,\n organizationId: teamId\n },\n callback: {\n provider: \"slack\",\n uri: slackApp.callbackUri ?? \"https://slack.com/api/chat.postMessage\",\n threadKey: encodeSlackThreadKey({\n teamId,\n channelId,\n threadTs\n })\n },\n metadata: {\n source: \"slack_button\",\n teamId,\n channelId,\n messageTs,\n ...(payload.api_app_id ? { slackAppId: payload.api_app_id } : {}),\n ...(action.action_id ? { actionId: action.action_id } : {}),\n ...(action.block_id ? { blockId: action.block_id } : {}),\n ...(action.action_ts ? { actionTs: action.action_ts } : {}),\n ...(parsedValue ? { proposalId: parsedValue.proposalId, intentId: parsedValue.intentId } : {}),\n repoProvider: binding.repoProvider ?? \"github\",\n owner: binding.owner,\n repo: binding.repo\n }\n });\n return json({ ok: true });\n }\n\n return {\n async process(payload: SlackIngressPayload, slackApp: SlackAppRuntimeConfig): Promise<SlackEventProcessorResult> {\n if (payload.type === \"block_actions\") {\n return processBlockActions(payload, slackApp);\n }\n if (payload.type === \"url_verification\") {\n return text(payload.challenge ?? \"\");\n }\n if (payload.type !== \"event_callback\" || !payload.event || ![\"app_mention\", \"message\"].includes(payload.event.type)) {\n return json({ ok: true });\n }\n if (payload.event.type === \"message\" && (payload.event.subtype || payload.event.bot_id)) {\n return json({ ok: true });\n }\n if (!payload.team_id || !payload.event.channel || !payload.event.user || !payload.event.text || !payload.event.ts || !payload.event_id) {\n return json({ error: \"invalid_event_payload\" }, 400);\n }\n\n const rawThreadActionText =\n payload.event.type === \"app_mention\"\n ? stripSlackAppMention(payload.event.text, payload.authorizations?.[0]?.user_id)\n : payload.event.text.trim();\n if (payload.event.type === \"message\" && (!rawThreadActionText || !parseThreadActionCommand(rawThreadActionText))) {\n return json({ ok: true });\n }\n\n const binding = await input.resolveChannelBinding({\n teamId: payload.team_id,\n channelId: payload.event.channel\n });\n if (!binding) {\n return json({ ok: true, ignored: \"unbound_channel\" });\n }\n\n if (rawThreadActionText && parseThreadActionCommand(rawThreadActionText) && input.submitThreadAction) {\n await input.submitThreadAction({\n id: `approval_slack_${payload.event_id}`,\n rawText: rawThreadActionText,\n actor: {\n provider: \"slack\",\n providerUserId: payload.event.user,\n handle: payload.event.user,\n organizationId: payload.team_id\n },\n callback: {\n provider: \"slack\",\n uri: slackApp.callbackUri ?? \"https://slack.com/api/chat.postMessage\",\n threadKey: encodeSlackThreadKey({\n teamId: payload.team_id,\n channelId: payload.event.channel,\n threadTs: payload.event.thread_ts ?? payload.event.ts\n })\n },\n metadata: {\n teamId: payload.team_id,\n channelId: payload.event.channel,\n messageTs: payload.event.ts,\n ...(payload.api_app_id ? { slackAppId: payload.api_app_id } : {}),\n ...(payload.authorizations?.[0]?.user_id ? { slackBotUserId: payload.authorizations[0].user_id } : {}),\n repoProvider: binding.repoProvider ?? \"github\",\n owner: binding.owner,\n repo: binding.repo\n }\n });\n return json({ ok: true });\n }\n\n if (payload.event.type !== \"app_mention\") {\n return json({ ok: true });\n }\n\n const event = normalizeSlackAppMention({\n teamId: payload.team_id,\n channelId: payload.event.channel,\n userId: payload.event.user,\n text: payload.event.text,\n ts: payload.event.ts,\n eventId: payload.event_id,\n eventTime: payload.event_time ?? Math.floor(Date.parse(input.now()) / 1000),\n agentId: slackApp.agentId,\n binding,\n ...(payload.api_app_id ? { appId: payload.api_app_id } : {}),\n ...(payload.event.thread_ts ? { threadTs: payload.event.thread_ts } : {}),\n ...(payload.authorizations?.[0]?.user_id ? { botUserId: payload.authorizations[0].user_id } : {}),\n ...(slackApp.callbackUri ? { callbackUri: slackApp.callbackUri } : {})\n });\n if (!event) {\n return json({ ok: true, ignored: \"empty_command\" });\n }\n\n await input.createRun(event);\n return json({ ok: true });\n }\n };\n}\n","import { commandFromRawText, type ContextPointer, type OpenTagCommand, type OpenTagEvent, type PermissionGrant } from \"@opentag/core\";\n\nexport type SlackChannelBinding = {\n teamId: string;\n channelId: string;\n repoProvider?: string;\n owner: string;\n repo: string;\n};\n\nexport type SlackAppMentionInput = {\n teamId: string;\n channelId: string;\n userId: string;\n text: string;\n ts: string;\n threadTs?: string;\n eventId: string;\n eventTime: number;\n appId?: string;\n agentId?: string;\n botUserId?: string;\n callbackUri?: string;\n binding: SlackChannelBinding;\n};\n\n// Matches a run of one or more leading user mentions (e.g. a teammate mention\n// placed before the bot mention) along with the whitespace separating them.\nconst LEADING_MENTION_RUN = /^(?:<@[^>]+>\\s*)+/;\n// Matches a single mention token, used to confirm the bot itself appears in the\n// leading run so we only treat the message as an at-mention of the bot.\nconst MENTION_TOKEN = /<@([^>]+)>/g;\n\nexport function stripSlackAppMention(text: string, botUserId?: string): string | null {\n const run = text.match(LEADING_MENTION_RUN);\n if (!run) return null;\n\n // Only strip the *leading* run of mentions; mentions later in the message\n // (e.g. \"<@bot> ping <@teammate> now\") must be preserved as command text.\n const leadingRun = run[0];\n\n if (botUserId) {\n // Require that the bot is mentioned somewhere in the leading run before we\n // accept this as a command directed at the bot. This lets a teammate\n // mention precede the bot mention without breaking routing.\n const mentionedIds = leadingRun.match(MENTION_TOKEN) ?? [];\n const botIsMentioned = mentionedIds.some((token) => {\n // Slack mentions may carry a display-name/label suffix, e.g.\n // \"<@U12345|alice>\". Strip the label so we compare against the bare\n // user ID rather than \"U12345|alice\".\n const id = token.slice(2, -1).split(\"|\")[0] ?? \"\";\n return id.toLowerCase() === botUserId.toLowerCase();\n });\n if (!botIsMentioned) return null;\n }\n\n const stripped = text.slice(leadingRun.length).trim();\n return stripped.length > 0 ? stripped : null;\n}\n\nexport function encodeSlackThreadKey(input: { teamId: string; channelId: string; threadTs: string }): string {\n return `${input.teamId}|${input.channelId}|${input.threadTs}`;\n}\n\nexport function parseSlackThreadKey(threadKey: string): { teamId: string; channelId: string; threadTs: string } {\n const [teamId, channelId, threadTs] = threadKey.split(\"|\");\n if (!teamId || !channelId || !threadTs) {\n throw new Error(`Invalid Slack thread key: ${threadKey}`);\n }\n return { teamId, channelId, threadTs };\n}\n\nconst UNKNOWN_WRITE_VERB_PATTERN = /\\b(add|append|apply|change|commit|create|delete|edit|fix|modify|open\\s+a?\\s*pr|pull\\s+request|remove|update|write)\\b/i;\nconst REPO_WRITE_TARGET_PATTERN =\n /\\b(repo|repository|code|file|files|branch|commit|diff|patch|readme|pr|pull\\s+request|package\\.json|pnpm|npm|test|build)\\b|(?:^|\\s)[./\\w-]+\\.(?:cjs|css|gitignore|go|html|js|json|jsx|lock|md|mjs|py|rb|rs|sh|toml|ts|tsx|txt|yaml|yml)\\b|(?:^|[\\s`'\"(])(?:[./\\w-]+\\/)?(?:Dockerfile|Makefile|Procfile|Rakefile|Gemfile|Brewfile|Justfile|Taskfile|\\.dockerignore|\\.env(?:\\.[\\w-]+)?|\\.gitignore|\\.npmrc)(?=$|[\\s`'\",.):])/i;\n\nfunction commandLooksRepoWriteCapable(command: OpenTagCommand): boolean {\n return UNKNOWN_WRITE_VERB_PATTERN.test(command.rawText) && REPO_WRITE_TARGET_PATTERN.test(command.rawText);\n}\n\nfunction permissionsForCommand(command: OpenTagCommand): PermissionGrant[] {\n const permissions: PermissionGrant[] = [\n {\n scope: \"chat:postMessage\",\n reason: \"reply in the originating Slack thread\"\n },\n {\n scope: \"reactions:write\",\n reason: \"mark the originating Slack message as received without posting a thread reply\"\n },\n {\n scope: \"runner:local\",\n reason: \"execute the run on a paired local daemon\"\n }\n ];\n\n if (command.intent === \"fix\" || command.intent === \"run\" || (command.intent === \"unknown\" && commandLooksRepoWriteCapable(command))) {\n permissions.push(\n {\n scope: \"repo:read\",\n reason: \"inspect the repository in the paired local checkout\"\n },\n {\n scope: \"repo:write\",\n reason: \"commit code changes on an isolated run branch\"\n },\n {\n scope: \"pr:create\",\n reason: \"open a pull request for completed code changes\"\n }\n );\n }\n\n return permissions;\n}\n\nfunction contextPointersForCommand(command: OpenTagCommand): ContextPointer[] {\n const context: ContextPointer[] = [];\n\n for (const reference of command.parsed?.references ?? []) {\n if (reference.kind === \"url\") {\n context.push({\n kind: \"url\",\n uri: reference.uri,\n visibility: \"organization\",\n title: reference.title ?? \"Command URL reference\"\n });\n continue;\n }\n\n if (reference.kind === \"file\" || reference.kind === \"path\") {\n context.push({\n kind: \"file\",\n uri: reference.uri,\n ...(reference.line ? { line: reference.line } : {}),\n ...(reference.startLine ? { startLine: reference.startLine } : {}),\n ...(reference.endLine ? { endLine: reference.endLine } : {}),\n visibility: \"organization\",\n title: referenceTitle(reference)\n });\n }\n }\n\n return context;\n}\n\nfunction referenceTitle(reference: NonNullable<OpenTagCommand[\"parsed\"]>[\"references\"][number]): string {\n return reference.title ?? \"Command file reference\";\n}\n\nfunction commandMetadata(command: OpenTagCommand): Record<string, unknown> {\n if (!command.parsed) return {};\n return {\n commandParser: command.parsed.version,\n commandDiagnostics: command.parsed.diagnostics,\n ...(command.parsed.approval ? { approval: command.parsed.approval } : {}),\n ...(command.parsed.network ? { network: command.parsed.network } : {})\n };\n}\n\nexport function normalizeSlackAppMention(input: SlackAppMentionInput): OpenTagEvent | null {\n const rawText = stripSlackAppMention(input.text, input.botUserId);\n if (!rawText) return null;\n\n const command = commandFromRawText(rawText);\n const replyThreadTs = input.threadTs ?? input.ts;\n const agentId = input.agentId ?? \"opentag\";\n\n return {\n id: `evt_slack_app_mention_${input.eventId}`,\n source: \"slack\",\n sourceEventId: input.eventId,\n receivedAt: new Date(input.eventTime * 1000).toISOString(),\n actor: {\n provider: \"slack\",\n providerUserId: input.userId,\n handle: input.userId,\n organizationId: input.teamId\n },\n target: {\n mention: input.botUserId ? `<@${input.botUserId}>` : \"<@app>\",\n agentId,\n ...(command.parsed?.executorHint ? { executorHint: command.parsed.executorHint } : {})\n },\n command,\n context: [\n {\n provider: \"slack\",\n kind: \"message\",\n uri: `slack://team/${input.teamId}/channel/${input.channelId}/message/${input.ts}`,\n visibility: \"organization\",\n title: \"Slack message\"\n },\n {\n kind: \"text\",\n uri: input.text,\n visibility: \"organization\",\n title: \"Slack message text\"\n },\n ...contextPointersForCommand(command)\n ],\n permissions: permissionsForCommand(command),\n callback: {\n provider: \"slack\",\n uri: input.callbackUri ?? \"https://slack.com/api/chat.postMessage\",\n threadKey: encodeSlackThreadKey({\n teamId: input.teamId,\n channelId: input.channelId,\n threadTs: replyThreadTs\n })\n },\n metadata: {\n teamId: input.teamId,\n channelId: input.channelId,\n messageTs: input.ts,\n ...(input.appId ? { slackAppId: input.appId } : {}),\n ...(input.botUserId ? { slackBotUserId: input.botUserId } : {}),\n ...commandMetadata(command),\n repoProvider: input.binding.repoProvider ?? \"github\",\n owner: input.binding.owner,\n repo: input.binding.repo\n }\n };\n}\n","import { suggestedActionCandidatesFromResult, type OpenTagRunResult, type SuggestedActionCandidate } from \"@opentag/core\";\n\nexport type SlackTextBlock = {\n type: \"section\";\n text: {\n type: \"mrkdwn\";\n text: string;\n };\n};\n\nexport type SlackDividerBlock = {\n type: \"divider\";\n};\n\nexport type SlackButtonElement = {\n type: \"button\";\n text: {\n type: \"plain_text\";\n text: string;\n emoji?: boolean;\n };\n action_id: string;\n value: string;\n style?: \"primary\" | \"danger\";\n};\n\nexport type SlackActionsBlock = {\n type: \"actions\";\n block_id?: string;\n elements: SlackButtonElement[];\n};\n\nexport type SlackBlock = SlackTextBlock | SlackDividerBlock | SlackActionsBlock;\n\nexport type SlackSuggestedActionButtonValue = {\n version: 1;\n command: string;\n proposalId: string;\n intentId: string;\n};\n\nexport type SlackMessagePayload = {\n channel: string;\n text: string;\n thread_ts?: string;\n ts?: string;\n blocks?: SlackBlock[];\n};\n\nexport type SlackReactionPayload = {\n channel: string;\n timestamp: string;\n name: string;\n};\n\nexport type SlackSourceReceiptState = \"received\";\n\nconst MAX_SLACK_SUGGESTED_ACTION_CANDIDATES = 20;\n\nexport function buildSlackSuggestedActionButtonValue(input: SlackSuggestedActionButtonValue): string {\n return JSON.stringify(input);\n}\n\nexport function parseSlackSuggestedActionButtonValue(value: string): SlackSuggestedActionButtonValue | null {\n try {\n const parsed = JSON.parse(value) as Partial<SlackSuggestedActionButtonValue>;\n if (\n parsed.version !== 1 ||\n typeof parsed.command !== \"string\" ||\n parsed.command.trim().length === 0 ||\n typeof parsed.proposalId !== \"string\" ||\n parsed.proposalId.length === 0 ||\n typeof parsed.intentId !== \"string\" ||\n parsed.intentId.length === 0\n ) {\n return null;\n }\n return {\n version: 1,\n command: parsed.command.trim(),\n proposalId: parsed.proposalId,\n intentId: parsed.intentId\n };\n } catch {\n return null;\n }\n}\n\nfunction escapeSlackText(text: string): string {\n return text.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\");\n}\n\nexport function markdownToSlackMrkdwn(text: string): string {\n const links: string[] = [];\n const withoutLinks = text.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, (_match, label: string, url: string) => {\n const token = `\\u0000SLACK_LINK_${links.length}\\u0000`;\n links.push(`<${url}|${escapeSlackText(label)}>`);\n return token;\n });\n const converted = escapeSlackText(withoutLinks)\n .replace(/\\*\\*(.+?)\\*\\*/g, \"*$1*\")\n .replace(/__(.+?)__/g, \"*$1*\");\n return links.reduce((output, link, index) => output.replace(`\\u0000SLACK_LINK_${index}\\u0000`, link), converted);\n}\n\nexport function renderSlackAcknowledgement(runId: string): string {\n void runId;\n return \"Working on it.\";\n}\n\nexport function slackSourceReceiptReactionName(state: SlackSourceReceiptState): string {\n if (state === \"received\") return \"eyes\";\n return \"eyes\";\n}\n\nexport function createSlackReactionPayload(input: { channelId: string; messageTs: string; name: string }): SlackReactionPayload {\n return {\n channel: input.channelId,\n timestamp: input.messageTs,\n name: input.name\n };\n}\n\nfunction nextActionSummary(result: OpenTagRunResult): string | undefined {\n if (!result.nextAction) return undefined;\n if (typeof result.nextAction === \"string\") return result.nextAction;\n return result.nextAction.summary;\n}\n\nfunction stringParam(params: Record<string, unknown> | undefined, key: string): string | undefined {\n const value = params?.[key];\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction stringArrayParam(params: Record<string, unknown> | undefined, key: string): string[] {\n const value = params?.[key];\n if (!Array.isArray(value)) return [];\n return value.filter((item): item is string => typeof item === \"string\" && item.length > 0);\n}\n\nfunction renderVerificationParams(params: Record<string, unknown> | undefined): string[] {\n const value = params?.[\"verification\"];\n if (!Array.isArray(value)) return [];\n return value\n .map((item) => {\n if (!item || typeof item !== \"object\" || Array.isArray(item)) return undefined;\n const command = (item as Record<string, unknown>)[\"command\"];\n const outcome = (item as Record<string, unknown>)[\"outcome\"];\n return typeof command === \"string\" && typeof outcome === \"string\" ? ` - \\`${command}\\`: ${outcome}` : undefined;\n })\n .filter((line): line is string => Boolean(line));\n}\n\nfunction renderSuggestedActionDetails(params: Record<string, unknown> | undefined, action: string): string[] {\n if (action !== \"create_pull_request\") return [];\n const lines: string[] = [];\n const title = stringParam(params, \"title\");\n const head = stringParam(params, \"head\") ?? stringParam(params, \"branch\");\n const base = stringParam(params, \"base\") ?? stringParam(params, \"baseBranch\");\n const changedFiles = stringArrayParam(params, \"changedFiles\");\n const risks = stringArrayParam(params, \"risks\");\n const verification = renderVerificationParams(params);\n if (title) lines.push(` Title: ${markdownToSlackMrkdwn(title)}`);\n if (head || base) lines.push(` Branch: \\`${head ?? \"unknown\"}\\` -> \\`${base ?? \"main\"}\\``);\n if (changedFiles.length > 0) lines.push(` Changed files: ${changedFiles.map((file) => `\\`${file}\\``).join(\", \")}`);\n if (risks.length > 0) {\n lines.push(\" Risks:\");\n for (const risk of risks) {\n lines.push(` - ${markdownToSlackMrkdwn(risk)}`);\n }\n }\n if (verification.length > 0) {\n lines.push(\" Verification:\");\n lines.push(...verification);\n }\n return lines;\n}\n\nfunction truncateSlackText(text: string, maxLength: number): string {\n const normalized = text.replace(/\\s+/g, \" \").trim();\n if (normalized.length <= maxLength) return normalized;\n return `${normalized.slice(0, Math.max(0, maxLength - 1)).trimEnd()}…`;\n}\n\nfunction firstMarkdownSection(text: string, heading: string): string | undefined {\n const pattern = new RegExp(`\\\\*\\\\*${heading}:\\\\*\\\\*\\\\s*([\\\\s\\\\S]*?)(?=\\\\n\\\\s*\\\\n\\\\*\\\\*[^*]+:\\\\*\\\\*|\\\\n\\\\s*\\\\n[A-Z][^\\\\n]{0,60}:|$)`, \"i\");\n const match = text.match(pattern);\n return match?.[1]?.trim();\n}\n\nfunction compactSlackSummary(summary: string): string {\n const whatChanged = firstMarkdownSection(summary, \"What changed\");\n const firstParagraph = summary\n .split(/\\n\\s*\\n/)\n .map((part) => part.trim())\n .find(Boolean);\n const selected = whatChanged ?? firstParagraph ?? summary;\n return truncateSlackText(selected.replace(/^\\*\\*[^*]+:\\*\\*\\s*/i, \"\"), 360);\n}\n\nfunction compactNextAction(nextAction: string): string {\n return truncateSlackText(nextAction, 180);\n}\n\nfunction renderSuggestedActionCandidateLines(candidate: SuggestedActionCandidate): string[] {\n const lines = [`${candidate.index}. *${markdownToSlackMrkdwn(candidate.intent.summary)}*`];\n const details = renderSuggestedActionDetails(candidate.intent.params, candidate.intent.action)\n .filter((line) => line.trim().startsWith(\"Branch:\") || line.trim().startsWith(\"Changed files:\"))\n .map((line) => line.replace(/^\\s+/, \"\"));\n lines.push(...details);\n if (candidate.proposalPreconditions?.length) {\n lines.push(`Preconditions: ${candidate.proposalPreconditions.length} check(s) in the audit log.`);\n }\n return lines;\n}\n\nfunction renderSuggestedActionsMarkdown(result: OpenTagRunResult): string[] {\n const candidates = suggestedActionCandidatesFromResult(result);\n if (candidates.length === 0) return [];\n\n const lines = [\"*Suggested actions*\"];\n const visibleCandidates = candidates.slice(0, MAX_SLACK_SUGGESTED_ACTION_CANDIDATES);\n for (const candidate of visibleCandidates) {\n lines.push(\"\", ...renderSuggestedActionCandidateLines(candidate));\n }\n\n const remainingCount = candidates.length - visibleCandidates.length;\n if (remainingCount > 0) {\n lines.push(\"\", `Showing first ${visibleCandidates.length} of ${candidates.length} actions. Reply with an action number for the rest.`);\n }\n lines.push(\"\", \"Use the buttons below, or reply `apply 1`, `approve 1`, or `reject 1`.\");\n return lines;\n}\n\nfunction createSuggestedActionButtons(candidate: SuggestedActionCandidate): SlackButtonElement[] {\n return [\n {\n type: \"button\",\n text: { type: \"plain_text\", text: `Apply ${candidate.index}`, emoji: true },\n action_id: `opentag:apply:${candidate.index}`,\n value: buildSlackSuggestedActionButtonValue({\n version: 1,\n command: `apply ${candidate.index}`,\n proposalId: candidate.proposalId,\n intentId: candidate.intent.intentId\n }),\n style: \"primary\"\n },\n {\n type: \"button\",\n text: { type: \"plain_text\", text: \"Approve\", emoji: true },\n action_id: `opentag:approve:${candidate.index}`,\n value: buildSlackSuggestedActionButtonValue({\n version: 1,\n command: `approve ${candidate.index}`,\n proposalId: candidate.proposalId,\n intentId: candidate.intent.intentId\n })\n },\n {\n type: \"button\",\n text: { type: \"plain_text\", text: \"Reject\", emoji: true },\n action_id: `opentag:reject:${candidate.index}`,\n value: buildSlackSuggestedActionButtonValue({\n version: 1,\n command: `reject ${candidate.index}`,\n proposalId: candidate.proposalId,\n intentId: candidate.intent.intentId\n }),\n style: \"danger\"\n }\n ];\n}\n\nexport function renderSlackFinalResult(result: OpenTagRunResult): string {\n const lines = [`*Finished: ${result.conclusion}.*`, markdownToSlackMrkdwn(compactSlackSummary(result.summary))];\n\n if (result.verification?.length) {\n lines.push(\n `Verified: ${result.verification\n .slice(0, 3)\n .map((check) => `\\`${markdownToSlackMrkdwn(check.command)}\\` ${markdownToSlackMrkdwn(check.outcome)}`)\n .join(\", \")}`\n );\n }\n\n const nextAction = nextActionSummary(result);\n if (nextAction && !result.suggestedChanges?.length) {\n lines.push(`Next: ${markdownToSlackMrkdwn(compactNextAction(nextAction))}`);\n }\n\n const suggestedActions = renderSuggestedActionsMarkdown(result);\n if (suggestedActions.length > 0) {\n lines.push(\"\", ...suggestedActions);\n }\n\n return lines.join(\"\\n\");\n}\n\nexport function createSlackFinalResultBlocks(result: OpenTagRunResult): SlackBlock[] {\n const blocks: SlackBlock[] = [\n {\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: `*Finished: ${result.conclusion}.*\\n${markdownToSlackMrkdwn(compactSlackSummary(result.summary))}`\n }\n }\n ];\n\n if (result.verification?.length) {\n blocks.push({\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: `Verified: ${markdownToSlackMrkdwn(\n result.verification\n .slice(0, 3)\n .map((check) => `\\`${check.command}\\` ${check.outcome}`)\n .join(\", \")\n )}`\n }\n });\n }\n\n const nextAction = nextActionSummary(result);\n const suggestedActionCandidates = suggestedActionCandidatesFromResult(result);\n if (nextAction && suggestedActionCandidates.length === 0) {\n blocks.push({\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: `Next: ${markdownToSlackMrkdwn(compactNextAction(nextAction))}`\n }\n });\n }\n\n if (suggestedActionCandidates.length > 0) {\n blocks.push({ type: \"divider\" });\n blocks.push({\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: \"*Suggested actions*\\nChoose an action in this thread. Details stay in the OpenTag audit log.\"\n }\n });\n const visibleCandidates = suggestedActionCandidates.slice(0, MAX_SLACK_SUGGESTED_ACTION_CANDIDATES);\n for (const candidate of visibleCandidates) {\n blocks.push({\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: renderSuggestedActionCandidateLines(candidate).join(\"\\n\")\n }\n });\n blocks.push({\n type: \"actions\",\n block_id: `opentag_actions_${candidate.index}`,\n elements: createSuggestedActionButtons(candidate)\n });\n }\n const remainingCount = suggestedActionCandidates.length - visibleCandidates.length;\n if (remainingCount > 0) {\n blocks.push({\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: `Showing first ${visibleCandidates.length} of ${suggestedActionCandidates.length} actions. Reply with an action number for the rest.`\n }\n });\n }\n }\n\n return blocks;\n}\n\nexport function createSlackPostMessagePayload(input: { channelId: string; text: string; threadTs: string; blocks?: SlackBlock[] }): SlackMessagePayload {\n return {\n channel: input.channelId,\n text: markdownToSlackMrkdwn(input.text),\n thread_ts: input.threadTs,\n ...(input.blocks?.length ? { blocks: input.blocks } : {})\n };\n}\n\nexport function createSlackUpdateMessagePayload(input: { channelId: string; text: string; messageTs: string; blocks?: SlackBlock[] }): SlackMessagePayload {\n return {\n channel: input.channelId,\n text: markdownToSlackMrkdwn(input.text),\n ts: input.messageTs,\n ...(input.blocks?.length ? { blocks: input.blocks } : {})\n };\n}\n","import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport { serve } from \"@hono/node-server\";\nimport { Hono } from \"hono\";\nimport { createSlackDispatcherEventProcessorInput } from \"./dispatcher-events.js\";\nimport { createSlackEventProcessor, type SlackAppRuntimeConfig, type SlackEventProcessorInput, type SlackIngressPayload } from \"./events.js\";\n\nexport type SlackEventsAppInput = {\n slackApps: Array<\n SlackAppRuntimeConfig & {\n signingSecret: string;\n }\n >;\n clock?: () => number;\n} & SlackEventProcessorInput;\n\nexport type SlackEventsApiIngressConfig = {\n signingSecret: string;\n dispatcherUrl: string;\n dispatcherToken?: string;\n port?: number;\n agentId?: string;\n appId?: string;\n callbackUri?: string;\n};\n\nexport type SlackIngressConfig = SlackEventsApiIngressConfig;\n\nexport type SlackIngressHandle = {\n url: string;\n server: ReturnType<typeof serve>;\n close(): Promise<void>;\n};\n\nexport function computeSlackSignature(input: {\n signingSecret: string;\n timestamp: string;\n rawBody: string;\n}): string {\n const base = `v0:${input.timestamp}:${input.rawBody}`;\n const digest = createHmac(\"sha256\", input.signingSecret).update(base).digest(\"hex\");\n return `v0=${digest}`;\n}\n\nexport function verifySlackSignature(input: {\n signingSecret: string;\n timestamp: string;\n rawBody: string;\n signature: string;\n}): boolean {\n const expected = computeSlackSignature(input);\n const expectedBuffer = Buffer.from(expected);\n const actualBuffer = Buffer.from(input.signature);\n return expectedBuffer.length === actualBuffer.length && timingSafeEqual(expectedBuffer, actualBuffer);\n}\n\nexport function verifySlackTimestamp(input: { timestamp: string; nowMs: number; toleranceSeconds?: number }): boolean {\n const timestampSeconds = Number(input.timestamp);\n if (!Number.isFinite(timestampSeconds)) return false;\n const toleranceSeconds = input.toleranceSeconds ?? 300;\n const ageSeconds = Math.abs(Math.floor(input.nowMs / 1000) - timestampSeconds);\n return ageSeconds <= toleranceSeconds;\n}\n\nexport function createSlackEventsApp(input: SlackEventsAppInput) {\n const app = new Hono();\n const processor = createSlackEventProcessor(input);\n\n function parseSlackPayload(rawBody: string, contentType?: string): SlackIngressPayload | null {\n try {\n if (contentType?.includes(\"application/x-www-form-urlencoded\") || rawBody.startsWith(\"payload=\")) {\n const interactivePayload = new URLSearchParams(rawBody).get(\"payload\");\n if (!interactivePayload) return null;\n return JSON.parse(interactivePayload) as SlackIngressPayload;\n }\n return JSON.parse(rawBody) as SlackIngressPayload;\n } catch {\n return null;\n }\n }\n\n function resolveSlackApp(inputValue: {\n apiAppId?: string;\n rawBody: string;\n signature: string;\n timestamp: string;\n }) {\n const candidates = inputValue.apiAppId\n ? input.slackApps.filter((candidate) => !candidate.appId || candidate.appId === inputValue.apiAppId)\n : input.slackApps;\n if (candidates.length === 0) {\n return { error: \"unknown_slack_app\" as const };\n }\n const slackApp = candidates.find((candidate) =>\n verifySlackSignature({\n signingSecret: candidate.signingSecret,\n timestamp: inputValue.timestamp,\n rawBody: inputValue.rawBody,\n signature: inputValue.signature\n })\n );\n return slackApp ? { slackApp } : { error: \"invalid_signature\" as const };\n }\n\n app.post(\"/slack/events\", async (c) => {\n const timestamp = c.req.header(\"x-slack-request-timestamp\");\n const signature = c.req.header(\"x-slack-signature\");\n if (!timestamp || !signature) {\n return c.json({ error: \"missing_signature_headers\" }, 401);\n }\n if (!verifySlackTimestamp({ timestamp, nowMs: input.clock?.() ?? Date.now() })) {\n return c.json({ error: \"stale_signature_timestamp\" }, 401);\n }\n const rawBody = await c.req.text();\n const payload = parseSlackPayload(rawBody, c.req.header(\"content-type\"));\n if (!payload) {\n return c.json({ error: \"invalid_json\" }, 400);\n }\n const resolvedSlackApp = resolveSlackApp({\n rawBody,\n signature,\n timestamp,\n ...(payload.api_app_id ? { apiAppId: payload.api_app_id } : {})\n });\n if (\"error\" in resolvedSlackApp) {\n return c.json({ error: resolvedSlackApp.error }, 401);\n }\n const result = await processor.process(payload, resolvedSlackApp.slackApp);\n if (result.kind === \"text\") {\n return c.text(result.body, result.status);\n }\n return c.json(result.body, result.status);\n });\n\n return app;\n}\n\nexport function startSlackIngress(config: SlackEventsApiIngressConfig): SlackIngressHandle {\n const port = config.port ?? 3040;\n const server = serve({\n fetch: createSlackEventsApp({\n slackApps: [\n {\n signingSecret: config.signingSecret,\n agentId: config.agentId ?? \"opentag\",\n ...(config.appId ? { appId: config.appId } : {}),\n ...(config.callbackUri ? { callbackUri: config.callbackUri } : {})\n }\n ],\n ...createSlackDispatcherEventProcessorInput(config)\n }).fetch,\n port\n });\n\n return {\n url: `http://localhost:${port}`,\n server,\n close() {\n return new Promise((resolve, reject) => {\n server.close((error?: Error) => {\n if (error) {\n reject(error);\n return;\n }\n resolve();\n });\n });\n }\n };\n}\n","import { randomUUID } from \"node:crypto\";\nimport { createOpenTagClient } from \"@opentag/client\";\nimport type { SlackEventProcessorInput } from \"./events.js\";\n\nexport type SlackDispatcherEventConfig = {\n dispatcherUrl: string;\n dispatcherToken?: string;\n};\n\nexport function createSlackDispatcherEventProcessorInput(config: SlackDispatcherEventConfig): SlackEventProcessorInput {\n const dispatcherClient = createOpenTagClient({\n dispatcherUrl: config.dispatcherUrl,\n ...(config.dispatcherToken ? { pairingToken: config.dispatcherToken } : {})\n });\n\n return {\n async resolveChannelBinding(input) {\n try {\n const { binding } = await dispatcherClient.getChannelBinding({\n provider: \"slack\",\n accountId: input.teamId,\n conversationId: input.channelId\n });\n return {\n teamId: binding.accountId,\n channelId: binding.conversationId,\n repoProvider: binding.repoProvider,\n owner: binding.owner,\n repo: binding.repo\n };\n } catch (error) {\n if (error instanceof Error && error.message.includes(\"channel_binding_not_found\")) {\n return null;\n }\n throw error;\n }\n },\n async createRun(event) {\n const runId = `run_${randomUUID()}`;\n const created = await dispatcherClient.createRun({ runId, event });\n return created.outcome === \"run_created\" ? { runId: created.run.id } : { runId };\n },\n async submitThreadAction(action) {\n await dispatcherClient.submitThreadAction(action);\n },\n now: () => new Date().toISOString()\n };\n}\n","import WebSocket, { type RawData } from \"ws\";\nimport { createSlackDispatcherEventProcessorInput, type SlackDispatcherEventConfig } from \"./dispatcher-events.js\";\nimport { createSlackEventProcessor, type SlackAppRuntimeConfig, type SlackEventProcessorInput, type SlackIngressPayload } from \"./events.js\";\n\nconst SLACK_CONNECTIONS_OPEN_URL = \"https://slack.com/api/apps.connections.open\";\nconst DEFAULT_RECONNECT_DELAY_MS = 1_000;\n\n// Slack Web API `error` codes that represent a terminal authentication or\n// configuration problem with the app token. Retrying these is pointless: the\n// request will fail identically forever, spamming logs and risking rate limits\n// or an API ban. Startup must fail loudly so the operator fixes the config,\n// rather than the daemon silently looping. Transient/network errors (e.g.\n// `ratelimited`, HTTP 5xx, fetch failures) are NOT in this set and are retried.\nconst TERMINAL_SLACK_ERROR_CODES = [\n \"invalid_auth\",\n \"not_authed\",\n \"account_inactive\",\n \"token_revoked\",\n \"token_expired\",\n \"not_allowed_token_type\",\n \"no_permission\",\n \"missing_scope\",\n \"ekm_access_denied\"\n] as const;\n\n/**\n * Returns true when the error from opening the Socket Mode connection is a\n * terminal auth/config failure that must propagate (reject startup) instead of\n * being retried. The Slack error code is embedded in the thrown Error message by\n * {@link openSlackSocketUrl} (e.g. \"...connection failed: invalid_auth\").\n */\nfunction isTerminalSlackAuthError(error: unknown): boolean {\n if (!(error instanceof Error)) return false;\n return TERMINAL_SLACK_ERROR_CODES.some((code) => error.message.includes(code));\n}\n\nexport type SlackSocketModeEnvelope = {\n type?: string;\n envelope_id?: string;\n payload?: SlackIngressPayload;\n accepts_response_payload?: boolean;\n};\n\nexport type SlackSocketModeAppInput = {\n appToken: string;\n slackApp: SlackAppRuntimeConfig;\n} & SlackEventProcessorInput;\n\nexport type SlackSocketModeIngressConfig = SlackDispatcherEventConfig & {\n appToken: string;\n agentId?: string;\n appId?: string;\n callbackUri?: string;\n};\n\nexport type SlackSocketModeIngressHandle = {\n startPromise: Promise<void>;\n close(): Promise<void>;\n};\n\nexport type SlackSocketModeDependencies = {\n fetchImpl?: typeof fetch;\n createWebSocket?(url: string): WebSocket;\n reconnectDelayMs?: number;\n log?(message: string): void;\n logError?(message: string, error?: unknown): void;\n};\n\ntype SlackConnectionsOpenResponse = {\n ok?: boolean;\n url?: string;\n error?: string;\n needed?: string;\n provided?: string;\n};\n\nfunction rawDataToString(data: RawData): string {\n if (typeof data === \"string\") return data;\n if (Buffer.isBuffer(data)) return data.toString(\"utf8\");\n if (Array.isArray(data)) return Buffer.concat(data).toString(\"utf8\");\n if (data instanceof ArrayBuffer) return Buffer.from(data).toString(\"utf8\");\n return Buffer.from(data).toString(\"utf8\");\n}\n\nasync function openSlackSocketUrl(input: { appToken: string; fetchImpl: typeof fetch }): Promise<string> {\n const response = await input.fetchImpl(SLACK_CONNECTIONS_OPEN_URL, {\n method: \"POST\",\n headers: {\n authorization: `Bearer ${input.appToken}`\n }\n });\n const body = (await response.json().catch(() => ({}))) as SlackConnectionsOpenResponse;\n if (!response.ok || !body.ok || !body.url) {\n const reason = body.error ?? `http_${response.status}`;\n throw new Error(`Slack Socket Mode connection failed: ${reason}`);\n }\n return body.url;\n}\n\nfunction parseSocketEnvelope(data: RawData): SlackSocketModeEnvelope | null {\n try {\n return JSON.parse(rawDataToString(data)) as SlackSocketModeEnvelope;\n } catch {\n return null;\n }\n}\n\nasync function handleSocketMessage(input: {\n data: RawData;\n socket: WebSocket;\n processor: ReturnType<typeof createSlackEventProcessor>;\n slackApp: SlackAppRuntimeConfig;\n logError(message: string, error?: unknown): void;\n}): Promise<void> {\n const envelope = parseSocketEnvelope(input.data);\n if (!envelope?.envelope_id) {\n input.logError(\"[slack] ignored Socket Mode envelope without envelope_id\");\n return;\n }\n\n input.socket.send(JSON.stringify({ envelope_id: envelope.envelope_id }));\n\n if (!envelope.payload) {\n return;\n }\n if (input.slackApp.appId && envelope.payload.api_app_id && envelope.payload.api_app_id !== input.slackApp.appId) {\n return;\n }\n if (envelope.type !== \"events_api\" && envelope.payload.type !== \"block_actions\") {\n return;\n }\n\n await input.processor.process(envelope.payload, input.slackApp);\n}\n\nfunction wait(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport function startSlackSocketModeApp(\n input: SlackSocketModeAppInput,\n dependencies: SlackSocketModeDependencies = {}\n): SlackSocketModeIngressHandle {\n const fetchImpl = dependencies.fetchImpl ?? fetch;\n const createWebSocket = dependencies.createWebSocket ?? ((url: string) => new WebSocket(url));\n const reconnectDelayMs = dependencies.reconnectDelayMs ?? DEFAULT_RECONNECT_DELAY_MS;\n const log = dependencies.log ?? ((message: string) => console.log(message));\n const logError = dependencies.logError ?? ((message: string, error?: unknown) => (error ? console.error(message, error) : console.error(message)));\n const processor = createSlackEventProcessor(input);\n let closed = false;\n let activeSocket: WebSocket | undefined;\n\n async function runOneConnection(socketUrl: string): Promise<void> {\n await new Promise<void>((resolve) => {\n const socket = createWebSocket(socketUrl);\n activeSocket = socket;\n let settled = false;\n const finish = () => {\n if (settled) return;\n settled = true;\n if (activeSocket === socket) activeSocket = undefined;\n resolve();\n };\n\n socket.once(\"open\", () => {\n log(\"[slack] Socket Mode connected\");\n });\n socket.on(\"message\", (data) => {\n void handleSocketMessage({\n data,\n socket,\n processor,\n slackApp: input.slackApp,\n logError\n }).catch((error: unknown) => {\n logError(\"[slack] failed to handle Socket Mode event:\", error);\n });\n });\n socket.once(\"close\", finish);\n socket.once(\"error\", (error) => {\n if (!closed) {\n logError(\"[slack] Socket Mode connection error:\", error);\n }\n socket.close();\n finish();\n });\n });\n }\n\n const startPromise = (async () => {\n while (!closed) {\n try {\n const socketUrl = await openSlackSocketUrl({ appToken: input.appToken, fetchImpl });\n await runOneConnection(socketUrl);\n } catch (error) {\n // A terminal auth/config error (invalid app token, missing scope, etc.)\n // will never succeed on retry, so we must NOT swallow it: rethrow so\n // startPromise rejects and startup fails loudly instead of looping\n // forever against Slack's API.\n if (isTerminalSlackAuthError(error)) {\n if (!closed) {\n logError(\"[slack] terminal Socket Mode auth/config error, aborting:\", error);\n }\n throw error;\n }\n // A transient apps.connections.open failure (or any error opening the\n // connection) must NOT reject startPromise: the CLI aborts the entire\n // OpenTag daemon when this promise rejects, so one blip in Slack's API\n // would otherwise take down every other ingress and the dispatcher.\n // Log it and fall through to the shared backoff/retry below.\n if (!closed) {\n logError(\"[slack] failed to open Socket Mode connection, retrying:\", error);\n }\n }\n if (!closed) {\n await wait(reconnectDelayMs);\n }\n }\n })();\n\n return {\n startPromise,\n async close() {\n closed = true;\n activeSocket?.close();\n await startPromise.catch(() => undefined);\n }\n };\n}\n\nexport function startSlackSocketModeIngress(\n config: SlackSocketModeIngressConfig,\n dependencies: SlackSocketModeDependencies = {}\n): SlackSocketModeIngressHandle {\n return startSlackSocketModeApp(\n {\n appToken: config.appToken,\n slackApp: {\n agentId: config.agentId ?? \"opentag\",\n ...(config.appId ? { appId: config.appId } : {}),\n ...(config.callbackUri ? { callbackUri: config.callbackUri } : {})\n },\n ...createSlackDispatcherEventProcessorInput(config)\n },\n dependencies\n );\n}\n"],"mappings":"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","names":["text","text"]}
@@ -0,0 +1,43 @@
1
+ import { serve } from "@hono/node-server";
2
+ import { Hono } from "hono";
3
+ import { type SlackAppRuntimeConfig, type SlackEventProcessorInput } from "./events.js";
4
+ export type SlackEventsAppInput = {
5
+ slackApps: Array<SlackAppRuntimeConfig & {
6
+ signingSecret: string;
7
+ }>;
8
+ clock?: () => number;
9
+ } & SlackEventProcessorInput;
10
+ export type SlackEventsApiIngressConfig = {
11
+ signingSecret: string;
12
+ dispatcherUrl: string;
13
+ dispatcherToken?: string;
14
+ port?: number;
15
+ agentId?: string;
16
+ appId?: string;
17
+ callbackUri?: string;
18
+ };
19
+ export type SlackIngressConfig = SlackEventsApiIngressConfig;
20
+ export type SlackIngressHandle = {
21
+ url: string;
22
+ server: ReturnType<typeof serve>;
23
+ close(): Promise<void>;
24
+ };
25
+ export declare function computeSlackSignature(input: {
26
+ signingSecret: string;
27
+ timestamp: string;
28
+ rawBody: string;
29
+ }): string;
30
+ export declare function verifySlackSignature(input: {
31
+ signingSecret: string;
32
+ timestamp: string;
33
+ rawBody: string;
34
+ signature: string;
35
+ }): boolean;
36
+ export declare function verifySlackTimestamp(input: {
37
+ timestamp: string;
38
+ nowMs: number;
39
+ toleranceSeconds?: number;
40
+ }): boolean;
41
+ export declare function createSlackEventsApp(input: SlackEventsAppInput): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
42
+ export declare function startSlackIngress(config: SlackEventsApiIngressConfig): SlackIngressHandle;
43
+ //# sourceMappingURL=ingress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ingress.d.ts","sourceRoot":"","sources":["../src/ingress.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAA6B,KAAK,qBAAqB,EAAE,KAAK,wBAAwB,EAA4B,MAAM,aAAa,CAAC;AAE7I,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,KAAK,CACd,qBAAqB,GAAG;QACtB,aAAa,EAAE,MAAM,CAAC;KACvB,CACF,CAAC;IACF,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CACtB,GAAG,wBAAwB,CAAC;AAE7B,MAAM,MAAM,2BAA2B,GAAG;IACxC,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,2BAA2B,CAAC;AAE7D,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC;IACjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE;IAC3C,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,CAIT;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAC1C,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAKV;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAMpH;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,mBAAmB,8EAuE9D;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,2BAA2B,GAAG,kBAAkB,CAgCzF"}
@@ -2,6 +2,7 @@ import { type OpenTagEvent } from "@opentag/core";
2
2
  export type SlackChannelBinding = {
3
3
  teamId: string;
4
4
  channelId: string;
5
+ repoProvider?: string;
5
6
  owner: string;
6
7
  repo: string;
7
8
  };
@@ -14,6 +15,8 @@ export type SlackAppMentionInput = {
14
15
  threadTs?: string;
15
16
  eventId: string;
16
17
  eventTime: number;
18
+ appId?: string;
19
+ agentId?: string;
17
20
  botUserId?: string;
18
21
  callbackUri?: string;
19
22
  binding: SlackChannelBinding;
@@ -1 +1 @@
1
- {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,YAAY,EAAwB,MAAM,eAAe,CAAC;AAE5F,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,mBAAmB,CAAC;CAC9B,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAapF;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAE3G;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAM9G;AAkCD,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,oBAAoB,GAAG,YAAY,GAAG,IAAI,CAwDzF"}
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgE,KAAK,YAAY,EAAwB,MAAM,eAAe,CAAC;AAEtI,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,mBAAmB,CAAC;CAC9B,CAAC;AASF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAyBpF;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAE3G;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAM9G;AA0FD,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,oBAAoB,GAAG,YAAY,GAAG,IAAI,CA+DzF"}
@@ -0,0 +1,72 @@
1
+ import { type OpenTagRunResult } from "@opentag/core";
2
+ export type SlackTextBlock = {
3
+ type: "section";
4
+ text: {
5
+ type: "mrkdwn";
6
+ text: string;
7
+ };
8
+ };
9
+ export type SlackDividerBlock = {
10
+ type: "divider";
11
+ };
12
+ export type SlackButtonElement = {
13
+ type: "button";
14
+ text: {
15
+ type: "plain_text";
16
+ text: string;
17
+ emoji?: boolean;
18
+ };
19
+ action_id: string;
20
+ value: string;
21
+ style?: "primary" | "danger";
22
+ };
23
+ export type SlackActionsBlock = {
24
+ type: "actions";
25
+ block_id?: string;
26
+ elements: SlackButtonElement[];
27
+ };
28
+ export type SlackBlock = SlackTextBlock | SlackDividerBlock | SlackActionsBlock;
29
+ export type SlackSuggestedActionButtonValue = {
30
+ version: 1;
31
+ command: string;
32
+ proposalId: string;
33
+ intentId: string;
34
+ };
35
+ export type SlackMessagePayload = {
36
+ channel: string;
37
+ text: string;
38
+ thread_ts?: string;
39
+ ts?: string;
40
+ blocks?: SlackBlock[];
41
+ };
42
+ export type SlackReactionPayload = {
43
+ channel: string;
44
+ timestamp: string;
45
+ name: string;
46
+ };
47
+ export type SlackSourceReceiptState = "received";
48
+ export declare function buildSlackSuggestedActionButtonValue(input: SlackSuggestedActionButtonValue): string;
49
+ export declare function parseSlackSuggestedActionButtonValue(value: string): SlackSuggestedActionButtonValue | null;
50
+ export declare function markdownToSlackMrkdwn(text: string): string;
51
+ export declare function renderSlackAcknowledgement(runId: string): string;
52
+ export declare function slackSourceReceiptReactionName(state: SlackSourceReceiptState): string;
53
+ export declare function createSlackReactionPayload(input: {
54
+ channelId: string;
55
+ messageTs: string;
56
+ name: string;
57
+ }): SlackReactionPayload;
58
+ export declare function renderSlackFinalResult(result: OpenTagRunResult): string;
59
+ export declare function createSlackFinalResultBlocks(result: OpenTagRunResult): SlackBlock[];
60
+ export declare function createSlackPostMessagePayload(input: {
61
+ channelId: string;
62
+ text: string;
63
+ threadTs: string;
64
+ blocks?: SlackBlock[];
65
+ }): SlackMessagePayload;
66
+ export declare function createSlackUpdateMessagePayload(input: {
67
+ channelId: string;
68
+ text: string;
69
+ messageTs: string;
70
+ blocks?: SlackBlock[];
71
+ }): SlackMessagePayload;
72
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import WebSocket from "ws";
2
+ import { type SlackDispatcherEventConfig } from "./dispatcher-events.js";
3
+ import { type SlackAppRuntimeConfig, type SlackEventProcessorInput, type SlackIngressPayload } from "./events.js";
4
+ export type SlackSocketModeEnvelope = {
5
+ type?: string;
6
+ envelope_id?: string;
7
+ payload?: SlackIngressPayload;
8
+ accepts_response_payload?: boolean;
9
+ };
10
+ export type SlackSocketModeAppInput = {
11
+ appToken: string;
12
+ slackApp: SlackAppRuntimeConfig;
13
+ } & SlackEventProcessorInput;
14
+ export type SlackSocketModeIngressConfig = SlackDispatcherEventConfig & {
15
+ appToken: string;
16
+ agentId?: string;
17
+ appId?: string;
18
+ callbackUri?: string;
19
+ };
20
+ export type SlackSocketModeIngressHandle = {
21
+ startPromise: Promise<void>;
22
+ close(): Promise<void>;
23
+ };
24
+ export type SlackSocketModeDependencies = {
25
+ fetchImpl?: typeof fetch;
26
+ createWebSocket?(url: string): WebSocket;
27
+ reconnectDelayMs?: number;
28
+ log?(message: string): void;
29
+ logError?(message: string, error?: unknown): void;
30
+ };
31
+ export declare function startSlackSocketModeApp(input: SlackSocketModeAppInput, dependencies?: SlackSocketModeDependencies): SlackSocketModeIngressHandle;
32
+ export declare function startSlackSocketModeIngress(config: SlackSocketModeIngressConfig, dependencies?: SlackSocketModeDependencies): SlackSocketModeIngressHandle;
33
+ //# sourceMappingURL=socket-mode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"socket-mode.d.ts","sourceRoot":"","sources":["../src/socket-mode.ts"],"names":[],"mappings":"AAAA,OAAO,SAA2B,MAAM,IAAI,CAAC;AAC7C,OAAO,EAA4C,KAAK,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACnH,OAAO,EAA6B,KAAK,qBAAqB,EAAE,KAAK,wBAAwB,EAAE,KAAK,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAkC7I,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,qBAAqB,CAAC;CACjC,GAAG,wBAAwB,CAAC;AAE7B,MAAM,MAAM,4BAA4B,GAAG,0BAA0B,GAAG;IACtE,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,YAAY,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,eAAe,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,GAAG,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CACnD,CAAC;AAyEF,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,uBAAuB,EAC9B,YAAY,GAAE,2BAAgC,GAC7C,4BAA4B,CAsF9B;AAED,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,4BAA4B,EACpC,YAAY,GAAE,2BAAgC,GAC7C,4BAA4B,CAa9B"}
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@opentag/slack",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Slack app mention normalization and callback helpers for OpenTag.",
5
5
  "type": "module",
6
+ "engines": {
7
+ "node": ">=20"
8
+ },
6
9
  "main": "./dist/index.js",
7
10
  "types": "./dist/index.d.ts",
8
11
  "exports": {
@@ -26,11 +29,16 @@
26
29
  "agents",
27
30
  "webhooks"
28
31
  ],
29
- "license": "Apache-2.0",
32
+ "license": "MIT",
30
33
  "dependencies": {
31
- "@opentag/core": "0.1.0"
34
+ "@hono/node-server": "^1.13.7",
35
+ "hono": "^4.6.15",
36
+ "ws": "^8.21.0",
37
+ "@opentag/client": "0.3.0",
38
+ "@opentag/core": "0.3.0"
32
39
  },
33
40
  "devDependencies": {
41
+ "@types/ws": "^8.18.1",
34
42
  "tsup": "^8.5.1",
35
43
  "typescript": "^5.9.3"
36
44
  },