chat 4.18.0 → 4.19.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/README.md +2 -0
- package/dist/index.d.ts +87 -9
- package/dist/index.js +57 -10
- package/dist/index.js.map +1 -1
- package/docs/adapters.mdx +56 -0
- package/docs/error-handling.mdx +1 -1
- package/docs/guides/code-review-hono.mdx +3 -3
- package/docs/guides/discord-nuxt.mdx +3 -2
- package/docs/guides/slack-nextjs.mdx +3 -2
- package/docs/meta.json +3 -4
- package/docs/slash-commands.mdx +4 -4
- package/docs/{state/index.mdx → state.mdx} +24 -12
- package/docs/usage.mdx +5 -0
- package/package.json +1 -1
- package/docs/adapters/discord.mdx +0 -217
- package/docs/adapters/gchat.mdx +0 -237
- package/docs/adapters/github.mdx +0 -222
- package/docs/adapters/index.mdx +0 -129
- package/docs/adapters/linear.mdx +0 -206
- package/docs/adapters/meta.json +0 -13
- package/docs/adapters/slack.mdx +0 -314
- package/docs/adapters/teams.mdx +0 -287
- package/docs/adapters/telegram.mdx +0 -161
- package/docs/state/ioredis.mdx +0 -81
- package/docs/state/memory.mdx +0 -52
- package/docs/state/meta.json +0 -4
- package/docs/state/postgres.mdx +0 -98
- package/docs/state/redis.mdx +0 -100
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/channel.ts","../src/chat-singleton.ts","../src/message.ts","../src/errors.ts","../src/logger.ts","../src/types.ts","../src/thread.ts","../src/from-full-stream.ts","../src/streaming-markdown.ts","../src/chat.ts","../src/emoji.ts","../src/index.ts"],"sourcesContent":["import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from \"@workflow/serde\";\nimport { cardToFallbackText } from \"./cards\";\nimport { getChatSingleton } from \"./chat-singleton\";\nimport { type ChatElement, isJSX, toCardElement } from \"./jsx-runtime\";\nimport {\n paragraph,\n parseMarkdown,\n root,\n text as textNode,\n toPlainText,\n} from \"./markdown\";\nimport { Message } from \"./message\";\nimport type {\n Adapter,\n AdapterPostableMessage,\n Author,\n Channel,\n ChannelInfo,\n EphemeralMessage,\n PostableMessage,\n PostEphemeralOptions,\n SentMessage,\n StateAdapter,\n ThreadSummary,\n} from \"./types\";\nimport { THREAD_STATE_TTL_MS } from \"./types\";\n\n/** State key prefix for channel state */\nconst CHANNEL_STATE_KEY_PREFIX = \"channel-state:\";\n\n/**\n * Serialized channel data for passing to external systems (e.g., workflow engines).\n */\nexport interface SerializedChannel {\n _type: \"chat:Channel\";\n adapterName: string;\n id: string;\n isDM: boolean;\n}\n\n/**\n * Config for creating a ChannelImpl with explicit adapter/state instances.\n */\ninterface ChannelImplConfigWithAdapter {\n adapter: Adapter;\n id: string;\n isDM?: boolean;\n stateAdapter: StateAdapter;\n}\n\n/**\n * Config for creating a ChannelImpl with lazy adapter resolution.\n */\ninterface ChannelImplConfigLazy {\n adapterName: string;\n id: string;\n isDM?: boolean;\n}\n\ntype ChannelImplConfig = ChannelImplConfigWithAdapter | ChannelImplConfigLazy;\n\nfunction isLazyConfig(\n config: ChannelImplConfig\n): config is ChannelImplConfigLazy {\n return \"adapterName\" in config && !(\"adapter\" in config);\n}\n\n/**\n * Check if a value is an AsyncIterable (like AI SDK's textStream).\n */\nfunction isAsyncIterable(value: unknown): value is AsyncIterable<string> {\n return (\n value !== null && typeof value === \"object\" && Symbol.asyncIterator in value\n );\n}\n\nexport class ChannelImpl<TState = Record<string, unknown>>\n implements Channel<TState>\n{\n readonly id: string;\n readonly isDM: boolean;\n\n private _adapter?: Adapter;\n private readonly _adapterName?: string;\n private _stateAdapterInstance?: StateAdapter;\n private _name: string | null = null;\n\n constructor(config: ChannelImplConfig) {\n this.id = config.id;\n this.isDM = config.isDM ?? false;\n\n if (isLazyConfig(config)) {\n this._adapterName = config.adapterName;\n } else {\n this._adapter = config.adapter;\n this._stateAdapterInstance = config.stateAdapter;\n }\n }\n\n get adapter(): Adapter {\n if (this._adapter) {\n return this._adapter;\n }\n\n if (!this._adapterName) {\n throw new Error(\"Channel has no adapter configured\");\n }\n\n const chat = getChatSingleton();\n const adapter = chat.getAdapter(this._adapterName);\n if (!adapter) {\n throw new Error(\n `Adapter \"${this._adapterName}\" not found in Chat singleton`\n );\n }\n\n this._adapter = adapter;\n return adapter;\n }\n\n private get _stateAdapter(): StateAdapter {\n if (this._stateAdapterInstance) {\n return this._stateAdapterInstance;\n }\n\n const chat = getChatSingleton();\n this._stateAdapterInstance = chat.getState();\n return this._stateAdapterInstance;\n }\n\n get name(): string | null {\n return this._name;\n }\n\n get state(): Promise<TState | null> {\n return this._stateAdapter.get<TState>(\n `${CHANNEL_STATE_KEY_PREFIX}${this.id}`\n );\n }\n\n async setState(\n newState: Partial<TState>,\n options?: { replace?: boolean }\n ): Promise<void> {\n const key = `${CHANNEL_STATE_KEY_PREFIX}${this.id}`;\n\n if (options?.replace) {\n await this._stateAdapter.set(key, newState, THREAD_STATE_TTL_MS);\n } else {\n const existing = await this._stateAdapter.get<TState>(key);\n const merged = { ...existing, ...newState };\n await this._stateAdapter.set(key, merged, THREAD_STATE_TTL_MS);\n }\n }\n\n /**\n * Iterate messages newest first (backward from most recent).\n * Uses adapter.fetchChannelMessages if available, otherwise falls back\n * to adapter.fetchMessages with the channel ID.\n */\n get messages(): AsyncIterable<Message> {\n const adapter = this.adapter;\n const channelId = this.id;\n\n return {\n async *[Symbol.asyncIterator]() {\n let cursor: string | undefined;\n\n while (true) {\n const fetchOptions = { cursor, direction: \"backward\" as const };\n const result = adapter.fetchChannelMessages\n ? await adapter.fetchChannelMessages(channelId, fetchOptions)\n : await adapter.fetchMessages(channelId, fetchOptions);\n\n // Messages within a page are chronological (oldest first),\n // but we want newest first, so reverse the page\n const reversed = [...result.messages].reverse();\n for (const message of reversed) {\n yield message;\n }\n\n if (!result.nextCursor || result.messages.length === 0) {\n break;\n }\n\n cursor = result.nextCursor;\n }\n },\n };\n }\n\n /**\n * Iterate threads in this channel, most recently active first.\n */\n threads(): AsyncIterable<ThreadSummary> {\n const adapter = this.adapter;\n const channelId = this.id;\n\n return {\n async *[Symbol.asyncIterator]() {\n if (!adapter.listThreads) {\n // Platform doesn't support threading — return empty\n return;\n }\n\n let cursor: string | undefined;\n\n while (true) {\n const result = await adapter.listThreads(channelId, {\n cursor,\n });\n\n for (const thread of result.threads) {\n yield thread;\n }\n\n if (!result.nextCursor || result.threads.length === 0) {\n break;\n }\n\n cursor = result.nextCursor;\n }\n },\n };\n }\n\n async fetchMetadata(): Promise<ChannelInfo> {\n if (this.adapter.fetchChannelInfo) {\n const info = await this.adapter.fetchChannelInfo(this.id);\n this._name = info.name ?? null;\n return info;\n }\n\n // Fallback: return basic info\n return {\n id: this.id,\n isDM: this.isDM,\n metadata: {},\n };\n }\n\n async post(\n message: string | PostableMessage | ChatElement\n ): Promise<SentMessage> {\n // Handle AsyncIterable (streaming) — not supported at channel level,\n // fall through to postMessage\n if (isAsyncIterable(message)) {\n // For channel-level streaming, accumulate and post as single message\n let accumulated = \"\";\n for await (const chunk of message) {\n accumulated += chunk;\n }\n return this.postSingleMessage(accumulated);\n }\n\n // Auto-convert JSX elements to CardElement\n let postable: string | AdapterPostableMessage = message as\n | string\n | AdapterPostableMessage;\n if (isJSX(message)) {\n const card = toCardElement(message);\n if (!card) {\n throw new Error(\"Invalid JSX element: must be a Card element\");\n }\n postable = card;\n }\n\n return this.postSingleMessage(postable);\n }\n\n private async postSingleMessage(\n postable: AdapterPostableMessage\n ): Promise<SentMessage> {\n const rawMessage = this.adapter.postChannelMessage\n ? await this.adapter.postChannelMessage(this.id, postable)\n : await this.adapter.postMessage(this.id, postable);\n\n return this.createSentMessage(rawMessage.id, postable, rawMessage.threadId);\n }\n\n async postEphemeral(\n user: string | Author,\n message: AdapterPostableMessage | ChatElement,\n options: PostEphemeralOptions\n ): Promise<EphemeralMessage | null> {\n const { fallbackToDM } = options;\n const userId = typeof user === \"string\" ? user : user.userId;\n\n let postable: AdapterPostableMessage;\n if (isJSX(message)) {\n const card = toCardElement(message);\n if (!card) {\n throw new Error(\"Invalid JSX element: must be a Card element\");\n }\n postable = card;\n } else {\n postable = message as AdapterPostableMessage;\n }\n\n if (this.adapter.postEphemeral) {\n return this.adapter.postEphemeral(this.id, userId, postable);\n }\n\n if (!fallbackToDM) {\n return null;\n }\n\n if (this.adapter.openDM) {\n const dmThreadId = await this.adapter.openDM(userId);\n const result = await this.adapter.postMessage(dmThreadId, postable);\n return {\n id: result.id,\n threadId: dmThreadId,\n usedFallback: true,\n raw: result.raw,\n };\n }\n\n return null;\n }\n\n async startTyping(status?: string): Promise<void> {\n await this.adapter.startTyping(this.id, status);\n }\n\n mentionUser(userId: string): string {\n return `<@${userId}>`;\n }\n\n toJSON(): SerializedChannel {\n return {\n _type: \"chat:Channel\",\n id: this.id,\n adapterName: this.adapter.name,\n isDM: this.isDM,\n };\n }\n\n static fromJSON<TState = Record<string, unknown>>(\n json: SerializedChannel,\n adapter?: Adapter\n ): ChannelImpl<TState> {\n const channel = new ChannelImpl<TState>({\n id: json.id,\n adapterName: json.adapterName,\n isDM: json.isDM,\n });\n if (adapter) {\n channel._adapter = adapter;\n }\n return channel;\n }\n\n static [WORKFLOW_SERIALIZE](instance: ChannelImpl): SerializedChannel {\n return instance.toJSON();\n }\n\n static [WORKFLOW_DESERIALIZE](data: SerializedChannel): ChannelImpl {\n return ChannelImpl.fromJSON(data);\n }\n\n private createSentMessage(\n messageId: string,\n postable: AdapterPostableMessage,\n threadIdOverride?: string\n ): SentMessage {\n const adapter = this.adapter;\n const threadId = threadIdOverride || this.id;\n const self = this;\n\n const { plainText, formatted, attachments } =\n extractMessageContent(postable);\n\n const sentMessage: SentMessage = {\n id: messageId,\n threadId,\n text: plainText,\n formatted,\n raw: null,\n author: {\n userId: \"self\",\n userName: adapter.userName,\n fullName: adapter.userName,\n isBot: true,\n isMe: true,\n },\n metadata: {\n dateSent: new Date(),\n edited: false,\n },\n attachments,\n\n toJSON() {\n return new Message(this).toJSON();\n },\n\n async edit(\n newContent: string | PostableMessage | ChatElement\n ): Promise<SentMessage> {\n let editPostable: string | AdapterPostableMessage = newContent as\n | string\n | AdapterPostableMessage;\n if (isJSX(newContent)) {\n const card = toCardElement(newContent);\n if (!card) {\n throw new Error(\"Invalid JSX element: must be a Card element\");\n }\n editPostable = card;\n }\n await adapter.editMessage(threadId, messageId, editPostable);\n return self.createSentMessage(messageId, editPostable);\n },\n\n async delete(): Promise<void> {\n await adapter.deleteMessage(threadId, messageId);\n },\n\n async addReaction(emoji: string): Promise<void> {\n await adapter.addReaction(threadId, messageId, emoji);\n },\n\n async removeReaction(emoji: string): Promise<void> {\n await adapter.removeReaction(threadId, messageId, emoji);\n },\n };\n\n return sentMessage;\n }\n}\n\n/**\n * Derive the channel ID from a thread ID.\n * Uses adapter.channelIdFromThreadId if available, otherwise defaults to\n * first two colon-separated parts.\n */\nexport function deriveChannelId(adapter: Adapter, threadId: string): string {\n if (adapter.channelIdFromThreadId) {\n return adapter.channelIdFromThreadId(threadId);\n }\n // Default: first two colon-separated parts\n const parts = threadId.split(\":\");\n return parts.slice(0, 2).join(\":\");\n}\n\n/**\n * Extract plain text, AST, and attachments from a message.\n */\nfunction extractMessageContent(message: AdapterPostableMessage): {\n plainText: string;\n formatted: import(\"mdast\").Root;\n attachments: import(\"./types\").Attachment[];\n} {\n if (typeof message === \"string\") {\n return {\n plainText: message,\n formatted: root([paragraph([textNode(message)])]),\n attachments: [],\n };\n }\n\n if (\"raw\" in message) {\n return {\n plainText: message.raw,\n formatted: root([paragraph([textNode(message.raw)])]),\n attachments: message.attachments || [],\n };\n }\n\n if (\"markdown\" in message) {\n const ast = parseMarkdown(message.markdown);\n return {\n plainText: toPlainText(ast),\n formatted: ast,\n attachments: message.attachments || [],\n };\n }\n\n if (\"ast\" in message) {\n return {\n plainText: toPlainText(message.ast),\n formatted: message.ast,\n attachments: message.attachments || [],\n };\n }\n\n if (\"card\" in message) {\n const fallbackText =\n message.fallbackText || cardToFallbackText(message.card);\n return {\n plainText: fallbackText,\n formatted: root([paragraph([textNode(fallbackText)])]),\n attachments: [],\n };\n }\n\n if (\"type\" in message && message.type === \"card\") {\n const fallbackText = cardToFallbackText(message);\n return {\n plainText: fallbackText,\n formatted: root([paragraph([textNode(fallbackText)])]),\n attachments: [],\n };\n }\n\n throw new Error(\"Invalid PostableMessage format\");\n}\n","/**\n * Singleton holder for Chat instance.\n * Separate module to avoid circular dependency between chat.ts and thread.ts.\n */\nimport type { Adapter, StateAdapter } from \"./types\";\n\n/**\n * Interface for the Chat singleton to avoid importing the full Chat class.\n */\nexport interface ChatSingleton {\n getAdapter(name: string): Adapter | undefined;\n getState(): StateAdapter;\n}\n\nlet _singleton: ChatSingleton | null = null;\n\n/**\n * Set the Chat singleton instance.\n * @internal Used by Chat.registerSingleton()\n */\nexport function setChatSingleton(chat: ChatSingleton): void {\n _singleton = chat;\n}\n\n/**\n * Get the Chat singleton instance.\n * @throws Error if no singleton has been registered\n */\nexport function getChatSingleton(): ChatSingleton {\n if (!_singleton) {\n throw new Error(\n \"No Chat singleton registered. Call chat.registerSingleton() first.\"\n );\n }\n return _singleton;\n}\n\n/**\n * Check if a Chat singleton has been registered.\n */\nexport function hasChatSingleton(): boolean {\n return _singleton !== null;\n}\n\n/**\n * Clear the Chat singleton (for testing).\n * @internal\n */\nexport function clearChatSingleton(): void {\n _singleton = null;\n}\n","/**\n * Message class with serialization support for workflow engines.\n */\n\nimport { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from \"@workflow/serde\";\nimport type { Root } from \"mdast\";\nimport type {\n Attachment,\n Author,\n FormattedContent,\n MessageMetadata,\n} from \"./types\";\n\n/**\n * Input data for creating a Message instance.\n * Use this interface when constructing Message objects.\n */\nexport interface MessageData<TRawMessage = unknown> {\n /** Attachments */\n attachments: Attachment[];\n /** Message author */\n author: Author;\n /** Structured formatting as an AST (mdast Root) */\n formatted: FormattedContent;\n /** Unique message ID */\n id: string;\n /** Whether the bot is @-mentioned in this message */\n isMention?: boolean;\n /** Message metadata */\n metadata: MessageMetadata;\n /** Platform-specific raw payload (escape hatch) */\n raw: TRawMessage;\n /** Plain text content (all formatting stripped) */\n text: string;\n /** Thread this message belongs to */\n threadId: string;\n}\n\n/**\n * Serialized message data for passing to external systems (e.g., workflow engines).\n * Dates are converted to ISO strings, and non-serializable fields are omitted.\n */\nexport interface SerializedMessage {\n _type: \"chat:Message\";\n attachments: Array<{\n type: \"image\" | \"file\" | \"video\" | \"audio\";\n url?: string;\n name?: string;\n mimeType?: string;\n size?: number;\n width?: number;\n height?: number;\n }>;\n author: {\n userId: string;\n userName: string;\n fullName: string;\n isBot: boolean | \"unknown\";\n isMe: boolean;\n };\n formatted: Root;\n id: string;\n isMention?: boolean;\n metadata: {\n dateSent: string; // ISO string\n edited: boolean;\n editedAt?: string; // ISO string\n };\n raw: unknown;\n text: string;\n threadId: string;\n}\n\n/**\n * A chat message with serialization support for workflow engines.\n *\n * @example\n * ```typescript\n * // Create a message\n * const message = new Message({\n * id: \"msg-1\",\n * threadId: \"slack:C123:1234.5678\",\n * text: \"Hello world\",\n * formatted: parseMarkdown(\"Hello world\"),\n * raw: {},\n * author: { userId: \"U123\", userName: \"user\", fullName: \"User\", isBot: false, isMe: false },\n * metadata: { dateSent: new Date(), edited: false },\n * attachments: [],\n * });\n *\n * // Serialize for workflow\n * const serialized = message.toJSON();\n * ```\n */\nexport class Message<TRawMessage = unknown> {\n /** Unique message ID */\n readonly id: string;\n /** Thread this message belongs to */\n readonly threadId: string;\n\n /** Plain text content (all formatting stripped) */\n text: string;\n /**\n * Structured formatting as an AST (mdast Root).\n * This is the canonical representation - use this for processing.\n * Use `stringifyMarkdown(message.formatted)` to get markdown string.\n */\n formatted: FormattedContent;\n /** Platform-specific raw payload (escape hatch) */\n raw: TRawMessage;\n\n /** Message author */\n author: Author;\n /** Message metadata */\n metadata: MessageMetadata;\n /** Attachments */\n attachments: Attachment[];\n\n /**\n * Whether the bot is @-mentioned in this message.\n *\n * This is set by the Chat SDK before passing the message to handlers.\n * It checks for `@username` in the message text using the adapter's\n * configured `userName` and optional `botUserId`.\n *\n * @example\n * ```typescript\n * chat.onSubscribedMessage(async (thread, message) => {\n * if (message.isMention) {\n * await thread.post(\"You mentioned me!\");\n * }\n * });\n * ```\n */\n isMention?: boolean;\n\n constructor(data: MessageData<TRawMessage>) {\n this.id = data.id;\n this.threadId = data.threadId;\n this.text = data.text;\n this.formatted = data.formatted;\n this.raw = data.raw;\n this.author = data.author;\n this.metadata = data.metadata;\n this.attachments = data.attachments;\n this.isMention = data.isMention;\n }\n\n /**\n * Serialize the message to a plain JSON object.\n * Use this to pass message data to external systems like workflow engines.\n *\n * Note: Attachment `data` (Buffer) and `fetchData` (function) are omitted\n * as they're not serializable.\n */\n toJSON(): SerializedMessage {\n return {\n _type: \"chat:Message\",\n id: this.id,\n threadId: this.threadId,\n text: this.text,\n formatted: this.formatted,\n raw: this.raw,\n author: {\n userId: this.author.userId,\n userName: this.author.userName,\n fullName: this.author.fullName,\n isBot: this.author.isBot,\n isMe: this.author.isMe,\n },\n metadata: {\n dateSent: this.metadata.dateSent.toISOString(),\n edited: this.metadata.edited,\n editedAt: this.metadata.editedAt?.toISOString(),\n },\n attachments: this.attachments.map((att) => ({\n type: att.type,\n url: att.url,\n name: att.name,\n mimeType: att.mimeType,\n size: att.size,\n width: att.width,\n height: att.height,\n })),\n isMention: this.isMention,\n };\n }\n\n /**\n * Reconstruct a Message from serialized JSON data.\n * Converts ISO date strings back to Date objects.\n */\n static fromJSON<TRawMessage = unknown>(\n json: SerializedMessage\n ): Message<TRawMessage> {\n return new Message<TRawMessage>({\n id: json.id,\n threadId: json.threadId,\n text: json.text,\n formatted: json.formatted,\n raw: json.raw as TRawMessage,\n author: json.author,\n metadata: {\n dateSent: new Date(json.metadata.dateSent),\n edited: json.metadata.edited,\n editedAt: json.metadata.editedAt\n ? new Date(json.metadata.editedAt)\n : undefined,\n },\n attachments: json.attachments,\n isMention: json.isMention,\n });\n }\n\n /**\n * Serialize a Message instance for @workflow/serde.\n * This static method is automatically called by workflow serialization.\n */\n static [WORKFLOW_SERIALIZE](instance: Message): SerializedMessage {\n return instance.toJSON();\n }\n\n /**\n * Deserialize a Message from @workflow/serde.\n * This static method is automatically called by workflow deserialization.\n */\n static [WORKFLOW_DESERIALIZE](data: SerializedMessage): Message {\n return Message.fromJSON(data);\n }\n}\n","/**\n * Error types for chat-sdk\n */\n\nexport class ChatError extends Error {\n readonly code: string;\n override readonly cause?: unknown;\n\n constructor(message: string, code: string, cause?: unknown) {\n super(message);\n this.name = \"ChatError\";\n this.code = code;\n this.cause = cause;\n }\n}\n\nexport class RateLimitError extends ChatError {\n readonly retryAfterMs?: number;\n\n constructor(message: string, retryAfterMs?: number, cause?: unknown) {\n super(message, \"RATE_LIMITED\", cause);\n this.name = \"RateLimitError\";\n this.retryAfterMs = retryAfterMs;\n }\n}\n\nexport class LockError extends ChatError {\n constructor(message: string, cause?: unknown) {\n super(message, \"LOCK_FAILED\", cause);\n this.name = \"LockError\";\n }\n}\n\nexport class NotImplementedError extends ChatError {\n readonly feature?: string;\n\n constructor(message: string, feature?: string, cause?: unknown) {\n super(message, \"NOT_IMPLEMENTED\", cause);\n this.name = \"NotImplementedError\";\n this.feature = feature;\n }\n}\n","/**\n * Logger types and implementations for chat-sdk\n */\n\nexport type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\" | \"silent\";\n\nexport interface Logger {\n /** Create a sub-logger with a prefix */\n child(prefix: string): Logger;\n debug(message: string, ...args: unknown[]): void;\n error(message: string, ...args: unknown[]): void;\n info(message: string, ...args: unknown[]): void;\n warn(message: string, ...args: unknown[]): void;\n}\n\n/**\n * Default console logger implementation.\n */\nexport class ConsoleLogger implements Logger {\n private readonly prefix: string;\n\n private readonly level: LogLevel;\n\n constructor(level: LogLevel = \"info\", prefix = \"chat-sdk\") {\n this.level = level;\n this.prefix = prefix;\n }\n\n private shouldLog(level: LogLevel): boolean {\n const levels: LogLevel[] = [\"debug\", \"info\", \"warn\", \"error\", \"silent\"];\n return levels.indexOf(level) >= levels.indexOf(this.level);\n }\n\n child(prefix: string): Logger {\n return new ConsoleLogger(this.level, `${this.prefix}:${prefix}`);\n }\n\n // eslint-disable-next-line no-console\n debug(message: string, ...args: unknown[]): void {\n if (this.shouldLog(\"debug\")) {\n console.debug(`[${this.prefix}] ${message}`, ...args);\n }\n }\n\n // eslint-disable-next-line no-console\n info(message: string, ...args: unknown[]): void {\n if (this.shouldLog(\"info\")) {\n console.info(`[${this.prefix}] ${message}`, ...args);\n }\n }\n\n // eslint-disable-next-line no-console\n warn(message: string, ...args: unknown[]): void {\n if (this.shouldLog(\"warn\")) {\n console.warn(`[${this.prefix}] ${message}`, ...args);\n }\n }\n\n // eslint-disable-next-line no-console\n error(message: string, ...args: unknown[]): void {\n if (this.shouldLog(\"error\")) {\n console.error(`[${this.prefix}] ${message}`, ...args);\n }\n }\n}\n","/**\n * Core types for chat-sdk\n */\n\nimport type { Root } from \"mdast\";\nimport type { CardElement } from \"./cards\";\nimport type { ChatElement } from \"./jsx-runtime\";\nimport type { Logger, LogLevel } from \"./logger\";\nimport type { Message } from \"./message\";\nimport type { ModalElement } from \"./modals\";\n\n// =============================================================================\n// Re-exports from extracted modules\n// =============================================================================\n\nexport {\n ChatError,\n LockError,\n NotImplementedError,\n RateLimitError,\n} from \"./errors\";\nexport type { Logger, LogLevel } from \"./logger\";\nexport { ConsoleLogger } from \"./logger\";\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\n/**\n * Chat configuration with type-safe adapter inference.\n * @template TAdapters - Record of adapter name to adapter instance\n */\nexport interface ChatConfig<\n TAdapters extends Record<string, Adapter> = Record<string, Adapter>,\n> {\n /** Map of adapter name to adapter instance */\n adapters: TAdapters;\n /**\n * TTL for message deduplication entries in milliseconds.\n * Defaults to 300000 (5 minutes). Increase if your webhook cold starts\n * cause platform retries that arrive after the default TTL expires.\n */\n dedupeTtlMs?: number;\n /**\n * Placeholder text for fallback streaming (post + edit) adapters.\n * Defaults to `\"...\"`.\n *\n * Set to `null` to avoid posting an initial placeholder message and instead\n * wait until some real text has been streamed before creating the message.\n */\n fallbackStreamingPlaceholderText?: string | null;\n /**\n * Logger instance or log level.\n * Pass \"silent\" to disable all logging.\n */\n logger?: Logger | LogLevel;\n /** State adapter for subscriptions and locking */\n state: StateAdapter;\n /**\n * Update interval for fallback streaming (post + edit) in milliseconds.\n * Defaults to 500ms. Lower values provide smoother updates but may hit rate limits.\n */\n streamingUpdateIntervalMs?: number;\n /** Default bot username across all adapters */\n userName: string;\n}\n\n/**\n * Options for webhook handling.\n */\nexport interface WebhookOptions {\n /**\n * Function to run message handling in the background.\n * Use this to ensure fast webhook responses while processing continues.\n *\n * @example\n * // Next.js App Router\n * import { after } from \"next/server\";\n * chat.webhooks.slack(request, { waitUntil: (p) => after(() => p) });\n *\n * @example\n * // Vercel Functions\n * import { waitUntil } from \"@vercel/functions\";\n * chat.webhooks.slack(request, { waitUntil });\n */\n waitUntil?: (task: Promise<unknown>) => void;\n}\n\n// =============================================================================\n// Adapter Interface\n// =============================================================================\n\n/**\n * Adapter interface with generics for platform-specific types.\n * @template TThreadId - Platform-specific thread ID data type\n * @template TRawMessage - Platform-specific raw message type\n */\nexport interface Adapter<TThreadId = unknown, TRawMessage = unknown> {\n /** Add a reaction to a message */\n addReaction(\n threadId: string,\n messageId: string,\n emoji: EmojiValue | string\n ): Promise<void>;\n /** Bot user ID for platforms that use IDs in mentions (e.g., Slack's <@U123>) */\n readonly botUserId?: string;\n\n /**\n * Derive channel ID from a thread ID.\n * Default fallback: first two colon-separated parts (e.g., \"slack:C123\").\n * Adapters with different structures should override this.\n */\n channelIdFromThreadId?(threadId: string): string;\n\n /** Decode thread ID string back to platform-specific data */\n decodeThreadId(threadId: string): TThreadId;\n\n /** Delete a message */\n deleteMessage(threadId: string, messageId: string): Promise<void>;\n\n /** Edit an existing message */\n editMessage(\n threadId: string,\n messageId: string,\n message: AdapterPostableMessage\n ): Promise<RawMessage<TRawMessage>>;\n\n /** Encode platform-specific data into a thread ID string */\n encodeThreadId(platformData: TThreadId): string;\n\n /**\n * Fetch channel info/metadata.\n */\n fetchChannelInfo?(channelId: string): Promise<ChannelInfo>;\n\n /**\n * Fetch channel-level messages (top-level, not thread replies).\n * For example, Slack's conversations.history vs conversations.replies.\n */\n fetchChannelMessages?(\n channelId: string,\n options?: FetchOptions\n ): Promise<FetchResult<TRawMessage>>;\n\n /**\n * Fetch a single message by ID.\n * Optional - adapters that don't implement this will return null.\n *\n * @param threadId - The thread ID containing the message\n * @param messageId - The platform-specific message ID\n * @returns The message, or null if not found/not supported\n */\n fetchMessage?(\n threadId: string,\n messageId: string\n ): Promise<Message<TRawMessage> | null>;\n\n /**\n * Fetch messages from a thread.\n *\n * **Direction behavior:**\n * - `backward` (default): Fetches the most recent messages. Use this for loading\n * a chat view. The `nextCursor` points to older messages.\n * - `forward`: Fetches the oldest messages first. Use this for iterating through\n * message history. The `nextCursor` points to newer messages.\n *\n * **Message ordering:**\n * Messages within each page are always returned in chronological order (oldest first),\n * regardless of direction. This is the natural reading order for chat messages.\n *\n * @example\n * ```typescript\n * // Load most recent 50 messages for display\n * const recent = await adapter.fetchMessages(threadId, { limit: 50 });\n * // recent.messages: [older, ..., newest] in chronological order\n *\n * // Paginate backward to load older messages\n * const older = await adapter.fetchMessages(threadId, {\n * limit: 50,\n * cursor: recent.nextCursor,\n * });\n *\n * // Iterate through all history from the beginning\n * const history = await adapter.fetchMessages(threadId, {\n * limit: 100,\n * direction: 'forward',\n * });\n * ```\n */\n fetchMessages(\n threadId: string,\n options?: FetchOptions\n ): Promise<FetchResult<TRawMessage>>;\n\n /** Fetch thread metadata */\n fetchThread(threadId: string): Promise<ThreadInfo>;\n\n /** Handle incoming webhook request */\n handleWebhook(request: Request, options?: WebhookOptions): Promise<Response>;\n\n /** Called when Chat instance is created (internal use) */\n initialize(chat: ChatInstance): Promise<void>;\n\n /**\n * Check if a thread is a direct message conversation.\n *\n * @param threadId - The thread ID to check\n * @returns True if the thread is a DM, false otherwise\n */\n isDM?(threadId: string): boolean;\n\n /**\n * List threads in a channel.\n */\n listThreads?(\n channelId: string,\n options?: ListThreadsOptions\n ): Promise<ListThreadsResult<TRawMessage>>;\n /** Unique name for this adapter (e.g., \"slack\", \"teams\") */\n readonly name: string;\n\n /**\n * Optional hook called when a thread is subscribed to.\n * Adapters can use this to set up platform-specific subscriptions\n * (e.g., Google Chat Workspace Events).\n */\n onThreadSubscribe?(threadId: string): Promise<void>;\n\n /**\n * Open a direct message conversation with a user.\n *\n * @param userId - The platform-specific user ID\n * @returns The thread ID for the DM conversation\n *\n * @example\n * ```typescript\n * const dmThreadId = await adapter.openDM(\"U123456\");\n * await adapter.postMessage(dmThreadId, \"Hello!\");\n * ```\n */\n openDM?(userId: string): Promise<string>;\n\n /**\n * Open a modal/dialog form.\n *\n * @param triggerId - Platform-specific trigger ID from the action event\n * @param modal - The modal element to display\n * @param contextId - Optional context ID for server-side stored thread/message context\n * @returns The view/dialog ID\n */\n openModal?(\n triggerId: string,\n modal: ModalElement,\n contextId?: string\n ): Promise<{ viewId: string }>;\n\n /** Parse platform message format to normalized format */\n parseMessage(raw: TRawMessage): Message<TRawMessage>;\n\n /**\n * Post a message to channel top-level (not in a thread).\n */\n postChannelMessage?(\n channelId: string,\n message: AdapterPostableMessage\n ): Promise<RawMessage<TRawMessage>>;\n\n /**\n * Post an ephemeral message visible only to a specific user.\n *\n * This is optional - if not implemented, Thread.postEphemeral will\n * fall back to openDM + postMessage when fallbackToDM is true.\n *\n * @param threadId - The thread to post in\n * @param userId - The user who should see the message\n * @param message - The message content\n * @returns EphemeralMessage with usedFallback: false\n */\n postEphemeral?(\n threadId: string,\n userId: string,\n message: AdapterPostableMessage\n ): Promise<EphemeralMessage>;\n\n /** Post a message to a thread */\n postMessage(\n threadId: string,\n message: AdapterPostableMessage\n ): Promise<RawMessage<TRawMessage>>;\n\n /** Remove a reaction from a message */\n removeReaction(\n threadId: string,\n messageId: string,\n emoji: EmojiValue | string\n ): Promise<void>;\n\n /** Render formatted content to platform-specific string */\n renderFormatted(content: FormattedContent): string;\n\n /** Show typing indicator */\n startTyping(threadId: string, status?: string): Promise<void>;\n\n /**\n * Stream a message using platform-native streaming APIs.\n *\n * The adapter consumes the async iterable and handles the entire streaming lifecycle.\n * Only available on platforms with native streaming support (e.g., Slack).\n *\n * The stream can yield plain strings (text chunks) or {@link StreamChunk} objects\n * for rich content like task progress cards. Adapters that don't support structured\n * chunks will extract text from `markdown_text` chunks and ignore other types.\n *\n * @param threadId - The thread to stream to\n * @param textStream - Async iterable of text chunks or structured StreamChunk objects\n * @param options - Platform-specific streaming options\n * @returns The raw message after streaming completes\n */\n stream?(\n threadId: string,\n textStream: AsyncIterable<string | StreamChunk>,\n options?: StreamOptions\n ): Promise<RawMessage<TRawMessage>>;\n /** Bot username (can override global userName) */\n readonly userName: string;\n}\n\n/**\n * A structured streaming chunk for platform-native rich content.\n *\n * On Slack, these map directly to streaming chunk types:\n * - `markdown_text`: Streamed text content\n * - `task_update`: Tool/step progress cards (pending → in_progress → complete → error)\n * - `plan_update`: Plan title updates\n *\n * Adapters that don't support structured chunks will extract `text` from\n * `markdown_text` chunks and ignore other types gracefully.\n */\nexport type StreamChunk = MarkdownTextChunk | TaskUpdateChunk | PlanUpdateChunk;\n\nexport interface MarkdownTextChunk {\n text: string;\n type: \"markdown_text\";\n}\n\nexport interface TaskUpdateChunk {\n id: string;\n output?: string;\n status: \"pending\" | \"in_progress\" | \"complete\" | \"error\";\n title: string;\n type: \"task_update\";\n}\n\nexport interface PlanUpdateChunk {\n title: string;\n type: \"plan_update\";\n}\n\n/**\n * Options for streaming messages.\n * Platform-specific options are passed through to the adapter.\n */\nexport interface StreamOptions {\n /** Slack: The team/workspace ID */\n recipientTeamId?: string;\n /** Slack: The user ID to stream to (for AI assistant context) */\n recipientUserId?: string;\n /** Block Kit elements to attach when stopping the stream (Slack only, via chat.stopStream) */\n stopBlocks?: unknown[];\n /**\n * Slack: Controls how task_update chunks are displayed.\n * - `\"timeline\"` — individual task cards shown inline with text (default)\n * - `\"plan\"` — all tasks grouped into a single plan block\n */\n taskDisplayMode?: \"timeline\" | \"plan\";\n /** Minimum interval between updates in ms (default: 1000). Used for fallback mode (GChat/Teams). */\n updateIntervalMs?: number;\n}\n\n/** Internal interface for Chat instance passed to adapters */\nexport interface ChatInstance {\n /** Get the configured logger, optionally with a child prefix */\n getLogger(prefix?: string): Logger;\n\n getState(): StateAdapter;\n getUserName(): string;\n\n /**\n * @deprecated Use processMessage instead. This method is for internal use.\n */\n handleIncomingMessage(\n adapter: Adapter,\n threadId: string,\n message: Message\n ): Promise<void>;\n\n /**\n * Process an incoming action event (button click) from an adapter.\n * Handles waitUntil registration and error catching internally.\n *\n * @param event - The action event (without thread field, will be added)\n * @param options - Webhook options including waitUntil\n */\n processAction(\n event: Omit<ActionEvent, \"thread\" | \"openModal\"> & { adapter: Adapter },\n options?: WebhookOptions\n ): void;\n\n processAppHomeOpened(\n event: AppHomeOpenedEvent,\n options?: WebhookOptions\n ): void;\n\n processAssistantContextChanged(\n event: AssistantContextChangedEvent,\n options?: WebhookOptions\n ): void;\n\n processAssistantThreadStarted(\n event: AssistantThreadStartedEvent,\n options?: WebhookOptions\n ): void;\n\n processMemberJoinedChannel(\n event: MemberJoinedChannelEvent,\n options?: WebhookOptions\n ): void;\n /**\n * Process an incoming message from an adapter.\n * Handles waitUntil registration and error catching internally.\n *\n * @param adapter - The adapter that received the message\n * @param threadId - The thread ID\n * @param message - Either a parsed message, or a factory function for lazy async parsing\n * @param options - Webhook options including waitUntil\n */\n processMessage(\n adapter: Adapter,\n threadId: string,\n message: Message | (() => Promise<Message>),\n options?: WebhookOptions\n ): void;\n\n /**\n * Process a modal close event from an adapter.\n *\n * @param event - The modal close event (without relatedThread/relatedMessage/relatedChannel)\n * @param contextId - Context ID for retrieving stored thread/message/channel context\n * @param options - Webhook options\n */\n processModalClose(\n event: Omit<\n ModalCloseEvent,\n \"relatedThread\" | \"relatedMessage\" | \"relatedChannel\"\n >,\n contextId?: string,\n options?: WebhookOptions\n ): void;\n\n /**\n * Process a modal submit event from an adapter.\n *\n * @param event - The modal submit event (without relatedThread/relatedMessage/relatedChannel)\n * @param contextId - Context ID for retrieving stored thread/message/channel context\n * @param options - Webhook options\n */\n processModalSubmit(\n event: Omit<\n ModalSubmitEvent,\n \"relatedThread\" | \"relatedMessage\" | \"relatedChannel\"\n >,\n contextId?: string,\n options?: WebhookOptions\n ): Promise<ModalResponse | undefined>;\n\n /**\n * Process an incoming reaction event from an adapter.\n * Handles waitUntil registration and error catching internally.\n *\n * @param event - The reaction event (without adapter field, will be added)\n * @param options - Webhook options including waitUntil\n */\n processReaction(\n event: Omit<ReactionEvent, \"adapter\" | \"thread\"> & { adapter?: Adapter },\n options?: WebhookOptions\n ): void;\n\n /**\n * Process an incoming slash command from an adapter.\n * Handles waitUntil registration and error catching internally.\n *\n * @param event - The slash command event\n * @param options - Webhook options including waitUntil\n */\n processSlashCommand(\n event: Omit<SlashCommandEvent, \"channel\" | \"openModal\"> & {\n adapter: Adapter;\n channelId: string;\n },\n options?: WebhookOptions\n ): void;\n}\n\n// =============================================================================\n// State Adapter Interface\n// =============================================================================\n\nexport interface StateAdapter {\n /** Acquire a lock on a thread (returns null if already locked) */\n acquireLock(threadId: string, ttlMs: number): Promise<Lock | null>;\n /** Connect to the state backend */\n connect(): Promise<void>;\n\n /** Delete a cached value */\n delete(key: string): Promise<void>;\n\n /** Disconnect from the state backend */\n disconnect(): Promise<void>;\n\n /** Extend a lock's TTL */\n extendLock(lock: Lock, ttlMs: number): Promise<boolean>;\n\n /** Get a cached value by key */\n get<T = unknown>(key: string): Promise<T | null>;\n\n /** Check if subscribed to a thread */\n isSubscribed(threadId: string): Promise<boolean>;\n\n /** Release a lock */\n releaseLock(lock: Lock): Promise<void>;\n\n /** Set a cached value with optional TTL in milliseconds */\n set<T = unknown>(key: string, value: T, ttlMs?: number): Promise<void>;\n\n /** Atomically set a value only if the key does not already exist. Returns true if set, false if key existed. */\n setIfNotExists(key: string, value: unknown, ttlMs?: number): Promise<boolean>;\n\n /** Subscribe to a thread (persists across restarts) */\n subscribe(threadId: string): Promise<void>;\n\n /** Unsubscribe from a thread */\n unsubscribe(threadId: string): Promise<void>;\n}\n\nexport interface Lock {\n expiresAt: number;\n threadId: string;\n token: string;\n}\n\n// =============================================================================\n// Postable (base interface for Thread and Channel)\n// =============================================================================\n\n/**\n * Base interface for entities that can receive messages.\n * Both Thread and Channel extend this interface.\n *\n * @template TState - Custom state type stored per entity\n * @template TRawMessage - Platform-specific raw message type\n */\nexport interface Postable<\n TState = Record<string, unknown>,\n TRawMessage = unknown,\n> {\n /** The adapter this entity belongs to */\n readonly adapter: Adapter;\n /** Unique ID */\n readonly id: string;\n /** Whether this is a direct message conversation */\n readonly isDM: boolean;\n\n /**\n * Get a platform-specific mention string for a user.\n */\n mentionUser(userId: string): string;\n\n /**\n * Iterate messages newest first (backward from most recent).\n * Auto-paginates lazily — only fetches pages as consumed.\n */\n readonly messages: AsyncIterable<Message<TRawMessage>>;\n\n /**\n * Post a message.\n */\n post(\n message: string | PostableMessage | ChatElement\n ): Promise<SentMessage<TRawMessage>>;\n\n /**\n * Post an ephemeral message visible only to a specific user.\n */\n postEphemeral(\n user: string | Author,\n message: AdapterPostableMessage | ChatElement,\n options: PostEphemeralOptions\n ): Promise<EphemeralMessage | null>;\n\n /**\n * Set the state. Merges with existing state by default.\n */\n setState(\n state: Partial<TState>,\n options?: { replace?: boolean }\n ): Promise<void>;\n\n /** Show typing indicator */\n startTyping(status?: string): Promise<void>;\n\n /**\n * Get the current state.\n * Returns null if no state has been set.\n */\n readonly state: Promise<TState | null>;\n}\n\n// =============================================================================\n// Channel\n// =============================================================================\n\n/**\n * Represents a channel/conversation container that holds threads.\n * Extends Postable for message posting capabilities.\n *\n * @template TState - Custom state type stored per channel\n * @template TRawMessage - Platform-specific raw message type\n */\nexport interface Channel<\n TState = Record<string, unknown>,\n TRawMessage = unknown,\n> extends Postable<TState, TRawMessage> {\n /** Fetch channel metadata from the platform */\n fetchMetadata(): Promise<ChannelInfo>;\n /** Channel name (e.g., \"#general\"). Null until fetchInfo() is called. */\n readonly name: string | null;\n\n /**\n * Iterate threads in this channel, most recently active first.\n * Returns ThreadSummary (lightweight) for efficiency.\n * Empty iterable on threadless platforms.\n */\n threads(): AsyncIterable<ThreadSummary<TRawMessage>>;\n}\n\n/**\n * Lightweight summary of a thread within a channel.\n */\nexport interface ThreadSummary<TRawMessage = unknown> {\n /** Full thread ID */\n id: string;\n /** Timestamp of most recent reply */\n lastReplyAt?: Date;\n /** Reply count (if available) */\n replyCount?: number;\n /** Root/first message of the thread */\n rootMessage: Message<TRawMessage>;\n}\n\n/**\n * Channel metadata returned by fetchInfo().\n */\nexport interface ChannelInfo {\n id: string;\n isDM?: boolean;\n memberCount?: number;\n metadata: Record<string, unknown>;\n name?: string;\n}\n\n/**\n * Options for listing threads in a channel.\n */\nexport interface ListThreadsOptions {\n cursor?: string;\n limit?: number;\n}\n\n/**\n * Result of listing threads in a channel.\n */\nexport interface ListThreadsResult<TRawMessage = unknown> {\n nextCursor?: string;\n threads: ThreadSummary<TRawMessage>[];\n}\n\n// =============================================================================\n// Thread\n// =============================================================================\n\n/** Default TTL for thread state (30 days in milliseconds) */\nexport const THREAD_STATE_TTL_MS = 30 * 24 * 60 * 60 * 1000;\n\n/**\n * Thread interface with support for custom state.\n * Extends Postable for shared message posting capabilities.\n *\n * @template TState - Custom state type stored per-thread (default: Record<string, unknown>)\n * @template TRawMessage - Platform-specific raw message type\n */\nexport interface Thread<TState = Record<string, unknown>, TRawMessage = unknown>\n extends Postable<TState, TRawMessage> {\n /**\n * Async iterator for all messages in the thread.\n * Messages are yielded in chronological order (oldest first).\n * Automatically handles pagination.\n */\n allMessages: AsyncIterable<Message<TRawMessage>>;\n\n /** Get the Channel containing this thread */\n readonly channel: Channel<TState, TRawMessage>;\n // Inherited from Postable: id, adapter, isDM, state, setState,\n // messages (newest first), post, postEphemeral, startTyping, mentionUser\n\n /** Channel/conversation ID */\n readonly channelId: string;\n\n /**\n * Wrap a Message object as a SentMessage with edit/delete capabilities.\n * Used internally for reconstructing messages from serialized data.\n */\n createSentMessageFromMessage(\n message: Message<TRawMessage>\n ): SentMessage<TRawMessage>;\n\n /**\n * Check if this thread is currently subscribed.\n *\n * In subscribed message handlers, this is optimized to return true immediately\n * without a state lookup, since we already know we're in a subscribed context.\n *\n * @returns Promise resolving to true if subscribed, false otherwise\n */\n isSubscribed(): Promise<boolean>;\n\n /**\n * Get a platform-specific mention string for a user.\n * Use this to @-mention a user in a message.\n * @example\n * await thread.post(`Hey ${thread.mentionUser(userId)}, check this out!`);\n */\n mentionUser(userId: string): string;\n\n /**\n * Post a message to this thread.\n *\n * Supports text, markdown, cards, and streaming from async iterables.\n * When posting a stream (e.g., from AI SDK), uses platform-native streaming\n * APIs when available (Slack), or falls back to post + edit with throttling.\n *\n * @param message - String, PostableMessage, JSX Card, or AsyncIterable<string>\n * @returns A SentMessage with methods to edit, delete, or add reactions\n *\n * @example\n * ```typescript\n * // Simple string\n * await thread.post(\"Hello!\");\n *\n * // Markdown\n * await thread.post({ markdown: \"**Bold** and _italic_\" });\n *\n * // With emoji\n * await thread.post(`${emoji.thumbs_up} Great job!`);\n *\n * // JSX Card (with @jsxImportSource chat)\n * await thread.post(\n * <Card title=\"Welcome!\">\n * <Text>Hello world</Text>\n * </Card>\n * );\n *\n * // Stream from AI SDK\n * const result = await agent.stream({ prompt: message.text });\n * await thread.post(result.textStream);\n * ```\n */\n post(\n message: string | PostableMessage | ChatElement\n ): Promise<SentMessage<TRawMessage>>;\n\n /**\n * Post an ephemeral message visible only to a specific user.\n *\n * **Platform Behavior:**\n * - **Slack**: Native ephemeral (session-dependent, disappears on reload)\n * - **Google Chat**: Native private message (persists, only target user sees it)\n * - **Discord**: No native support - requires fallbackToDM: true\n * - **Teams**: No native support - requires fallbackToDM: true\n *\n * @param user - User ID string or Author object (from message.author or event.user)\n * @param message - Message content (string, markdown, card, etc.). Streaming is not supported.\n * @param options.fallbackToDM - Required. If true, falls back to DM when native\n * ephemeral is not supported. If false, returns null when unsupported.\n * @returns EphemeralMessage with `usedFallback: true` if DM was used, or null\n * if native ephemeral not supported and fallbackToDM is false\n *\n * @example\n * ```typescript\n * // Always send (DM fallback on Discord/Teams)\n * await thread.postEphemeral(user, 'Only you can see this!', { fallbackToDM: true })\n *\n * // Only send if native ephemeral supported (Slack/GChat)\n * const result = await thread.postEphemeral(user, 'Secret!', { fallbackToDM: false })\n * if (!result) {\n * // Platform doesn't support native ephemeral - handle accordingly\n * }\n * ```\n */\n postEphemeral(\n user: string | Author,\n message: AdapterPostableMessage | ChatElement,\n options: PostEphemeralOptions\n ): Promise<EphemeralMessage | null>;\n\n /** Recently fetched messages (cached) */\n recentMessages: Message<TRawMessage>[];\n\n /**\n * Refresh `recentMessages` from the API.\n *\n * Fetches the latest 50 messages and updates `recentMessages`.\n */\n refresh(): Promise<void>;\n\n /**\n * Show typing indicator in the thread.\n *\n * Some platforms support persistent typing indicators, others just send once.\n * Optional status (e.g. \"Typing...\", \"Searching documents...\") is shown where supported.\n */\n startTyping(status?: string): Promise<void>;\n\n /**\n * Subscribe to future messages in this thread.\n *\n * Once subscribed, all messages in this thread will trigger `onSubscribedMessage` handlers.\n * The initial message that triggered subscription will NOT fire the handler.\n *\n * @example\n * ```typescript\n * chat.onNewMention(async (thread, message) => {\n * await thread.subscribe(); // Subscribe to follow-up messages\n * await thread.post(\"I'm now watching this thread!\");\n * });\n * ```\n */\n subscribe(): Promise<void>;\n\n /**\n * Unsubscribe from this thread.\n *\n * Future messages will no longer trigger `onSubscribedMessage` handlers.\n */\n unsubscribe(): Promise<void>;\n}\n\nexport interface ThreadInfo {\n channelId: string;\n channelName?: string;\n id: string;\n /** Whether this is a direct message conversation */\n isDM?: boolean;\n /** Platform-specific metadata */\n metadata: Record<string, unknown>;\n}\n\n/**\n * Direction for fetching messages.\n *\n * - `backward`: Fetch most recent messages first. Pagination moves toward older messages.\n * This is the default, suitable for loading a chat view (show latest messages first).\n *\n * - `forward`: Fetch oldest messages first. Pagination moves toward newer messages.\n * Suitable for iterating through message history from the beginning.\n *\n * In both directions, messages within each page are returned in chronological order\n * (oldest first), which is the natural reading order for chat messages.\n *\n * @example\n * ```typescript\n * // Load most recent 50 messages (default)\n * const recent = await adapter.fetchMessages(threadId, { limit: 50 });\n * // recent.messages: [older, ..., newest] (chronological within page)\n * // recent.nextCursor: points to older messages\n *\n * // Iterate through all history from beginning\n * const history = await adapter.fetchMessages(threadId, {\n * limit: 50,\n * direction: 'forward',\n * });\n * // history.messages: [oldest, ..., newer] (chronological within page)\n * // history.nextCursor: points to even newer messages\n * ```\n */\nexport type FetchDirection = \"forward\" | \"backward\";\n\n/**\n * Options for fetching messages from a thread.\n */\nexport interface FetchOptions {\n /**\n * Pagination cursor for fetching the next page of messages.\n * Pass the `nextCursor` from a previous `FetchResult`.\n */\n cursor?: string;\n /**\n * Direction to fetch messages.\n *\n * - `backward` (default): Fetch most recent messages. Cursor moves to older messages.\n * - `forward`: Fetch oldest messages. Cursor moves to newer messages.\n *\n * Messages within each page are always returned in chronological order (oldest first).\n */\n direction?: FetchDirection;\n /** Maximum number of messages to fetch. Default varies by adapter (50-100). */\n limit?: number;\n}\n\n/**\n * Result of fetching messages from a thread.\n */\nexport interface FetchResult<TRawMessage = unknown> {\n /**\n * Messages in chronological order (oldest first within this page).\n *\n * For `direction: 'backward'` (default): These are the N most recent messages.\n * For `direction: 'forward'`: These are the N oldest messages (or next N after cursor).\n */\n messages: Message<TRawMessage>[];\n /**\n * Cursor for fetching the next page.\n * Pass this as `cursor` in the next `fetchMessages` call.\n *\n * - For `direction: 'backward'`: Points to older messages.\n * - For `direction: 'forward'`: Points to newer messages.\n *\n * Undefined if there are no more messages in that direction.\n */\n nextCursor?: string;\n}\n\n// =============================================================================\n// Message\n// =============================================================================\n\n/**\n * Formatted content using mdast AST.\n * This is the canonical representation of message formatting.\n */\nexport type FormattedContent = Root;\n\n/** Raw message returned from adapter (before wrapping as SentMessage) */\nexport interface RawMessage<TRawMessage = unknown> {\n id: string;\n raw: TRawMessage;\n threadId: string;\n}\n\nexport interface Author {\n /** Display name */\n fullName: string;\n /** Whether the author is a bot */\n isBot: boolean | \"unknown\";\n /** Whether the author is this bot */\n isMe: boolean;\n /** Unique user ID */\n userId: string;\n /** Username/handle for @-mentions */\n userName: string;\n}\n\nexport interface MessageMetadata {\n /** When the message was sent */\n dateSent: Date;\n /** Whether the message has been edited */\n edited: boolean;\n /** When the message was last edited */\n editedAt?: Date;\n}\n\n// =============================================================================\n// Sent Message (returned from thread.post())\n// =============================================================================\n\nexport interface SentMessage<TRawMessage = unknown>\n extends Message<TRawMessage> {\n /** Add a reaction to this message */\n addReaction(emoji: EmojiValue | string): Promise<void>;\n /** Delete this message */\n delete(): Promise<void>;\n /** Edit this message with text, a PostableMessage, or a JSX Card element */\n edit(\n newContent: string | PostableMessage | ChatElement\n ): Promise<SentMessage<TRawMessage>>;\n /** Remove a reaction from this message */\n removeReaction(emoji: EmojiValue | string): Promise<void>;\n}\n\n// =============================================================================\n// Ephemeral Message (returned from thread.postEphemeral())\n// =============================================================================\n\n/**\n * Result of posting an ephemeral message.\n *\n * Ephemeral messages are visible only to a specific user and typically\n * cannot be edited or deleted (platform-dependent).\n */\nexport interface EphemeralMessage {\n /** Message ID (may be empty for some platforms) */\n id: string;\n /** Platform-specific raw response */\n raw: unknown;\n /** Thread ID where message was sent (or DM thread if fallback was used) */\n threadId: string;\n /** Whether this used native ephemeral or fell back to DM */\n usedFallback: boolean;\n}\n\n/**\n * Options for posting ephemeral messages.\n */\nexport interface PostEphemeralOptions {\n /**\n * Controls behavior when native ephemeral is not supported by the platform.\n *\n * - `true`: Falls back to sending a DM to the user\n * - `false`: Returns `null` if native ephemeral is not supported\n */\n fallbackToDM: boolean;\n}\n\n// =============================================================================\n// Postable Message\n// =============================================================================\n\n/**\n * Input type for adapter postMessage/editMessage methods.\n * This excludes streams since adapters handle content synchronously.\n */\nexport type AdapterPostableMessage =\n | string\n | PostableRaw\n | PostableMarkdown\n | PostableAst\n | PostableCard\n | CardElement;\n\n/**\n * A message that can be posted to a thread.\n *\n * - `string` - Raw text, passed through as-is to the platform\n * - `{ raw: string }` - Explicit raw text, passed through as-is\n * - `{ markdown: string }` - Markdown text, converted to platform format\n * - `{ ast: Root }` - mdast AST, converted to platform format\n * - `{ card: CardElement }` - Rich card with buttons (Block Kit / Adaptive Cards / GChat Cards)\n * - `CardElement` - Direct card element\n * - `AsyncIterable<string>` - Streaming text (e.g., from AI SDK's textStream)\n * - `AsyncIterable<string | StreamEvent>` - AI SDK fullStream (auto-detected, extracts text with step separators)\n */\nexport type PostableMessage =\n | AdapterPostableMessage\n | AsyncIterable<string | StreamChunk | StreamEvent>;\n\n/**\n * Duck-typed stream event compatible with AI SDK's `fullStream`.\n * - `text-delta` events are extracted as text output.\n * - `step-finish` events trigger paragraph separators between steps.\n * - All other event types (tool-call, tool-result, etc.) are silently skipped.\n */\nexport type StreamEvent =\n | { textDelta: string; type: \"text-delta\" }\n | { type: \"step-finish\" }\n | { type: string };\n\nexport interface PostableRaw {\n /** File/image attachments */\n attachments?: Attachment[];\n /** Files to upload */\n files?: FileUpload[];\n /** Raw text passed through as-is to the platform */\n raw: string;\n}\n\nexport interface PostableMarkdown {\n /** File/image attachments */\n attachments?: Attachment[];\n /** Files to upload */\n files?: FileUpload[];\n /** Markdown text, converted to platform format */\n markdown: string;\n}\n\nexport interface PostableAst {\n /** mdast AST, converted to platform format */\n ast: Root;\n /** File/image attachments */\n attachments?: Attachment[];\n /** Files to upload */\n files?: FileUpload[];\n}\n\nexport interface PostableCard {\n /** Rich card element */\n card: CardElement;\n /** Fallback text for platforms/clients that can't render cards */\n fallbackText?: string;\n /** Files to upload */\n files?: FileUpload[];\n}\n\nexport interface Attachment {\n /** Binary data (for uploading or if already fetched) */\n data?: Buffer | Blob;\n /**\n * Fetch the attachment data.\n * For platforms that require authentication (like Slack private URLs),\n * this method handles the auth automatically.\n */\n fetchData?: () => Promise<Buffer>;\n /** Image/video height (if applicable) */\n height?: number;\n /** MIME type */\n mimeType?: string;\n /** Filename */\n name?: string;\n /** File size in bytes */\n size?: number;\n /** Type of attachment */\n type: \"image\" | \"file\" | \"video\" | \"audio\";\n /** URL to the file (for linking/downloading) */\n url?: string;\n /** Image/video width (if applicable) */\n width?: number;\n}\n\n/**\n * File to upload with a message.\n */\nexport interface FileUpload {\n /** Binary data */\n data: Buffer | Blob | ArrayBuffer;\n /** Filename */\n filename: string;\n /** MIME type (optional, will be inferred from filename if not provided) */\n mimeType?: string;\n}\n\n// =============================================================================\n// Event Handlers\n// =============================================================================\n\n/**\n * Handler for new @-mentions of the bot.\n *\n * **Important**: This handler is ONLY called for mentions in **unsubscribed** threads.\n * Once a thread is subscribed (via `thread.subscribe()`), subsequent messages\n * including @-mentions go to `onSubscribedMessage` handlers instead.\n *\n * To detect mentions in subscribed threads, check `message.isMention`:\n *\n * @example\n * ```typescript\n * // Handle new mentions (unsubscribed threads only)\n * chat.onNewMention(async (thread, message) => {\n * await thread.subscribe(); // Subscribe to follow-up messages\n * await thread.post(\"Hello! I'll be watching this thread.\");\n * });\n *\n * // Handle all messages in subscribed threads\n * chat.onSubscribedMessage(async (thread, message) => {\n * if (message.isMention) {\n * // User @-mentioned us in a thread we're already watching\n * await thread.post(\"You mentioned me again!\");\n * }\n * });\n * ```\n */\nexport type MentionHandler<TState = Record<string, unknown>> = (\n thread: Thread<TState>,\n message: Message\n) => void | Promise<void>;\n\n/**\n * Handler for messages matching a regex pattern.\n *\n * Registered via `chat.onNewMessage(pattern, handler)`. Called when a message\n * matches the pattern in an unsubscribed thread.\n */\nexport type MessageHandler<TState = Record<string, unknown>> = (\n thread: Thread<TState>,\n message: Message\n) => void | Promise<void>;\n\n/**\n * Handler for messages in subscribed threads.\n *\n * Called for all messages in threads that have been subscribed via `thread.subscribe()`.\n * This includes:\n * - Follow-up messages from users\n * - Messages that @-mention the bot (check `message.isMention`)\n *\n * Does NOT fire for:\n * - The message that triggered the subscription (e.g., the initial @mention)\n * - Messages sent by the bot itself\n *\n * @example\n * ```typescript\n * chat.onSubscribedMessage(async (thread, message) => {\n * // Handle all follow-up messages\n * if (message.isMention) {\n * // User @-mentioned us in a subscribed thread\n * }\n * await thread.post(`Got your message: ${message.text}`);\n * });\n * ```\n */\nexport type SubscribedMessageHandler<TState = Record<string, unknown>> = (\n thread: Thread<TState>,\n message: Message\n) => void | Promise<void>;\n\n// =============================================================================\n// Reactions / Emoji\n// =============================================================================\n\n/**\n * Well-known emoji that work across platforms (Slack and Google Chat).\n * These are normalized to a common format regardless of platform.\n */\nexport type WellKnownEmoji =\n // Reactions & Gestures\n | \"thumbs_up\"\n | \"thumbs_down\"\n | \"clap\"\n | \"wave\"\n | \"pray\"\n | \"muscle\"\n | \"ok_hand\"\n | \"point_up\"\n | \"point_down\"\n | \"point_left\"\n | \"point_right\"\n | \"raised_hands\"\n | \"shrug\"\n | \"facepalm\"\n // Emotions & Faces\n | \"heart\"\n | \"smile\"\n | \"laugh\"\n | \"thinking\"\n | \"sad\"\n | \"cry\"\n | \"angry\"\n | \"love_eyes\"\n | \"cool\"\n | \"wink\"\n | \"surprised\"\n | \"worried\"\n | \"confused\"\n | \"neutral\"\n | \"sleeping\"\n | \"sick\"\n | \"mind_blown\"\n | \"relieved\"\n | \"grimace\"\n | \"rolling_eyes\"\n | \"hug\"\n | \"zany\"\n // Status & Symbols\n | \"check\"\n | \"x\"\n | \"question\"\n | \"exclamation\"\n | \"warning\"\n | \"stop\"\n | \"info\"\n | \"100\"\n | \"fire\"\n | \"star\"\n | \"sparkles\"\n | \"lightning\"\n | \"boom\"\n | \"eyes\"\n // Status Indicators\n | \"green_circle\"\n | \"yellow_circle\"\n | \"red_circle\"\n | \"blue_circle\"\n | \"white_circle\"\n | \"black_circle\"\n // Objects & Tools\n | \"rocket\"\n | \"party\"\n | \"confetti\"\n | \"balloon\"\n | \"gift\"\n | \"trophy\"\n | \"medal\"\n | \"lightbulb\"\n | \"gear\"\n | \"wrench\"\n | \"hammer\"\n | \"bug\"\n | \"link\"\n | \"lock\"\n | \"unlock\"\n | \"key\"\n | \"pin\"\n | \"memo\"\n | \"clipboard\"\n | \"calendar\"\n | \"clock\"\n | \"hourglass\"\n | \"bell\"\n | \"megaphone\"\n | \"speech_bubble\"\n | \"email\"\n | \"inbox\"\n | \"outbox\"\n | \"package\"\n | \"folder\"\n | \"file\"\n | \"chart_up\"\n | \"chart_down\"\n | \"coffee\"\n | \"pizza\"\n | \"beer\"\n // Arrows & Directions\n | \"arrow_up\"\n | \"arrow_down\"\n | \"arrow_left\"\n | \"arrow_right\"\n | \"refresh\"\n // Nature & Weather\n | \"sun\"\n | \"cloud\"\n | \"rain\"\n | \"snow\"\n | \"rainbow\";\n\n/**\n * Platform-specific emoji formats for a single emoji.\n */\nexport interface EmojiFormats {\n /** Google Chat unicode emoji, e.g., \"👍\", \"❤️\" */\n gchat: string | string[];\n /** Slack emoji name (without colons), e.g., \"+1\", \"heart\" */\n slack: string | string[];\n}\n\n/**\n * Emoji map type - can be extended by users to add custom emoji.\n *\n * @example\n * ```typescript\n * // Extend with custom emoji\n * declare module \"chat\" {\n * interface CustomEmojiMap {\n * \"custom_emoji\": EmojiFormats;\n * }\n * }\n *\n * const myEmojiMap: EmojiMapConfig = {\n * custom_emoji: { slack: \"custom\", gchat: \"🎯\" },\n * };\n * ```\n */\n// biome-ignore lint/suspicious/noEmptyInterface: Required for TypeScript module augmentation\nexport interface CustomEmojiMap {}\n\n/**\n * Full emoji type including well-known and custom emoji.\n */\nexport type Emoji = WellKnownEmoji | keyof CustomEmojiMap;\n\n/**\n * Configuration for emoji mapping.\n */\nexport type EmojiMapConfig = Partial<Record<Emoji, EmojiFormats>>;\n\n/**\n * Immutable emoji value object with object identity.\n *\n * These objects are singletons - the same emoji name always returns\n * the same frozen object instance, enabling `===` comparison.\n *\n * @example\n * ```typescript\n * // Object identity comparison works\n * if (event.emoji === emoji.thumbs_up) {\n * console.log(\"User gave a thumbs up!\");\n * }\n *\n * // Works in template strings via toString()\n * await thread.post(`${emoji.thumbs_up} Great job!`);\n * ```\n */\nexport interface EmojiValue {\n /** The normalized emoji name (e.g., \"thumbs_up\") */\n readonly name: string;\n /** Returns the placeholder string (for JSON.stringify) */\n toJSON(): string;\n /** Returns the placeholder string for message formatting */\n toString(): string;\n}\n\n/**\n * Reaction event fired when a user adds or removes a reaction.\n */\nexport interface ReactionEvent<TRawMessage = unknown> {\n /** The adapter that received the event */\n adapter: Adapter;\n /** Whether the reaction was added (true) or removed (false) */\n added: boolean;\n /** The normalized emoji as an EmojiValue singleton (enables `===` comparison) */\n emoji: EmojiValue;\n /** The message that was reacted to (if available) */\n message?: Message<TRawMessage>;\n /** The message ID that was reacted to */\n messageId: string;\n /** Platform-specific raw event data */\n raw: unknown;\n /** The raw platform-specific emoji (e.g., \"+1\" for Slack, \"👍\" for GChat) */\n rawEmoji: string;\n /**\n * The thread where the reaction occurred.\n * Use this to post replies or check subscription status.\n *\n * @example\n * ```typescript\n * chat.onReaction([emoji.thumbs_up], async (event) => {\n * await event.thread.post(`Thanks for the ${event.emoji}!`);\n * });\n * ```\n */\n thread: Thread<TRawMessage>;\n /** The thread ID */\n threadId: string;\n /** The user who added/removed the reaction */\n user: Author;\n}\n\n/**\n * Handler for reaction events.\n *\n * @example\n * ```typescript\n * // Handle specific emoji\n * chat.onReaction([\"thumbs_up\", \"heart\"], async (event) => {\n * console.log(`${event.user.userName} ${event.added ? \"added\" : \"removed\"} ${event.emoji}`);\n * });\n *\n * // Handle all reactions\n * chat.onReaction(async (event) => {\n * // ...\n * });\n * ```\n */\nexport type ReactionHandler = (event: ReactionEvent) => void | Promise<void>;\n\n// =============================================================================\n// Action Events (Button Clicks)\n// =============================================================================\n\n/**\n * Action event fired when a user clicks a button in a card.\n *\n * @example\n * ```typescript\n * chat.onAction(\"approve\", async (event) => {\n * await event.thread.post(`Order ${event.value} approved by ${event.user.userName}`);\n * });\n * ```\n */\nexport interface ActionEvent<TRawMessage = unknown> {\n /** The action ID from the button (matches Button's `id` prop) */\n actionId: string;\n /** The adapter that received the event */\n adapter: Adapter;\n /** The message ID containing the card */\n messageId: string;\n /**\n * Open a modal/dialog form in response to this action.\n *\n * @param modal - The modal element to display (JSX or ModalElement)\n * @returns The view/dialog ID, or undefined if modals are not supported\n */\n openModal(\n modal: ModalElement | ChatElement\n ): Promise<{ viewId: string } | undefined>;\n /** Platform-specific raw event data */\n raw: unknown;\n /** The thread where the action occurred (null for view-based actions like home tab buttons) */\n thread: Thread<TRawMessage> | null;\n /** The thread ID */\n threadId: string;\n /** Trigger ID for opening modals (required by some platforms, may expire quickly) */\n triggerId?: string;\n /** User who clicked the button */\n user: Author;\n /** Optional value/payload from the button */\n value?: string;\n}\n\n/**\n * Handler for action events (button clicks in cards).\n *\n * @example\n * ```typescript\n * // Handle specific action\n * chat.onAction(\"approve\", async (event) => {\n * await event.thread.post(\"Approved!\");\n * });\n *\n * // Handle multiple actions\n * chat.onAction([\"approve\", \"reject\"], async (event) => {\n * if (event.actionId === \"approve\") {\n * // ...\n * }\n * });\n *\n * // Handle all actions (catch-all)\n * chat.onAction(async (event) => {\n * console.log(`Action: ${event.actionId}`);\n * });\n * ```\n */\nexport type ActionHandler = (event: ActionEvent) => void | Promise<void>;\n\n// =============================================================================\n// Modal Events (Form Submissions)\n// =============================================================================\n\n/**\n * Event emitted when a user submits a modal form.\n */\nexport interface ModalSubmitEvent<TRawMessage = unknown> {\n /** The adapter that received this event */\n adapter: Adapter;\n /** The callback ID specified when creating the modal */\n callbackId: string;\n /**\n * The private metadata string set when the modal was created.\n * Use this to pass arbitrary context (e.g., JSON) through the modal lifecycle.\n */\n privateMetadata?: string;\n /** Raw platform-specific payload */\n raw: unknown;\n /**\n * The channel where the modal was originally triggered from.\n * Available when the modal was opened via SlashCommandEvent.openModal().\n */\n relatedChannel?: Channel<Record<string, unknown>, TRawMessage>;\n /**\n * The message that contained the action which opened the modal.\n * Available when the modal was opened from a message action via ActionEvent.openModal().\n * This is a SentMessage with edit/delete capabilities.\n */\n relatedMessage?: SentMessage<TRawMessage>;\n /**\n * The thread where the modal was originally triggered from.\n * Available when the modal was opened via ActionEvent.openModal().\n */\n relatedThread?: Thread<Record<string, unknown>, TRawMessage>;\n /** The user who submitted the modal */\n user: Author;\n /** Form field values keyed by input ID */\n values: Record<string, string>;\n /** Platform-specific view/dialog ID */\n viewId: string;\n}\n\n/**\n * Event emitted when a user closes/cancels a modal (requires notifyOnClose).\n */\nexport interface ModalCloseEvent<TRawMessage = unknown> {\n /** The adapter that received this event */\n adapter: Adapter;\n /** The callback ID specified when creating the modal */\n callbackId: string;\n /**\n * The private metadata string set when the modal was created.\n * Use this to pass arbitrary context (e.g., JSON) through the modal lifecycle.\n */\n privateMetadata?: string;\n /** Raw platform-specific payload */\n raw: unknown;\n /**\n * The channel where the modal was originally triggered from.\n * Available when the modal was opened via SlashCommandEvent.openModal().\n */\n relatedChannel?: Channel<Record<string, unknown>, TRawMessage>;\n /**\n * The message that contained the action which opened the modal.\n * Available when the modal was opened from a message action via ActionEvent.openModal().\n * This is a SentMessage with edit/delete capabilities.\n */\n relatedMessage?: SentMessage<TRawMessage>;\n /**\n * The thread where the modal was originally triggered from.\n * Available when the modal was opened via ActionEvent.openModal().\n */\n relatedThread?: Thread<Record<string, unknown>, TRawMessage>;\n /** The user who closed the modal */\n user: Author;\n /** Platform-specific view/dialog ID */\n viewId: string;\n}\n\nexport interface ModalErrorsResponse {\n action: \"errors\";\n errors: Record<string, string>;\n}\n\nexport interface ModalUpdateResponse {\n action: \"update\";\n modal: import(\"./modals\").ModalElement;\n}\n\nexport interface ModalPushResponse {\n action: \"push\";\n modal: import(\"./modals\").ModalElement;\n}\n\nexport interface ModalCloseResponse {\n action: \"close\";\n}\n\nexport type ModalResponse =\n | ModalCloseResponse\n | ModalErrorsResponse\n | ModalUpdateResponse\n | ModalPushResponse;\n\nexport type ModalSubmitHandler = (\n event: ModalSubmitEvent\n // biome-ignore lint/suspicious/noConfusingVoidType: void is needed for sync handlers that return nothing\n) => void | Promise<ModalResponse | undefined>;\n\nexport type ModalCloseHandler = (\n event: ModalCloseEvent\n) => void | Promise<void>;\n\n// =============================================================================\n// Slash Command Events\n// =============================================================================\n\n/**\n * Event emitted when a user invokes a slash command.\n *\n * Slash commands are triggered when a user types `/command` in the message composer.\n * The event provides access to the channel where the command was invoked, allowing\n * you to post responses using standard SDK methods.\n *\n * @example\n * ```typescript\n * chat.onSlashCommand(\"/help\", async (event) => {\n * // Post visible to everyone in the channel\n * await event.channel.post(\"Here are the available commands...\");\n * });\n *\n * chat.onSlashCommand(\"/secret\", async (event) => {\n * // Post ephemeral (only the invoking user sees it)\n * await event.channel.postEphemeral(\n * event.user,\n * \"This is just for you!\",\n * { fallbackToDM: false }\n * );\n * });\n *\n * chat.onSlashCommand(\"/feedback\", async (event) => {\n * // Open a modal\n * await event.openModal({\n * type: \"modal\",\n * callbackId: \"feedback_modal\",\n * title: \"Submit Feedback\",\n * children: [{ type: \"text_input\", id: \"feedback\", label: \"Your feedback\" }],\n * });\n * });\n * ```\n */\nexport interface SlashCommandEvent<TState = Record<string, unknown>> {\n /** The adapter that received this event */\n adapter: Adapter;\n\n /** The channel where the command was invoked */\n channel: Channel<TState>;\n /** The slash command name (e.g., \"/help\") */\n command: string;\n\n /**\n * Open a modal/dialog form in response to this slash command.\n *\n * @param modal - The modal element to display (JSX or ModalElement)\n * @returns The view/dialog ID, or undefined if modals are not supported\n */\n openModal(\n modal: ModalElement | ChatElement\n ): Promise<{ viewId: string } | undefined>;\n\n /** Platform-specific raw payload */\n raw: unknown;\n\n /** Arguments text after the command (e.g., \"topic search\" from \"/help topic search\") */\n text: string;\n\n /** Trigger ID for opening modals (time-limited, typically ~3 seconds) */\n triggerId?: string;\n\n /** The user who invoked the command */\n user: Author;\n}\n\n/**\n * Handler for slash command events.\n *\n * @example\n * ```typescript\n * // Handle a specific command\n * chat.onSlashCommand(\"/status\", async (event) => {\n * await event.channel.post(\"All systems operational!\");\n * });\n *\n * // Handle multiple commands\n * chat.onSlashCommand([\"/help\", \"/info\"], async (event) => {\n * await event.channel.post(`You invoked ${event.command}`);\n * });\n *\n * // Catch-all handler\n * chat.onSlashCommand(async (event) => {\n * console.log(`Command: ${event.command}, Args: ${event.text}`);\n * });\n * ```\n */\nexport type SlashCommandHandler<TState = Record<string, unknown>> = (\n event: SlashCommandEvent<TState>\n) => void | Promise<void>;\n\n// =============================================================================\n// Assistant Events (Slack Assistants API / AI Apps)\n// =============================================================================\n\nexport interface AssistantThreadStartedEvent {\n adapter: Adapter;\n channelId: string;\n context: {\n channelId?: string;\n teamId?: string;\n enterpriseId?: string;\n threadEntryPoint?: string;\n forceSearch?: boolean;\n };\n threadId: string;\n threadTs: string;\n userId: string;\n}\n\nexport type AssistantThreadStartedHandler = (\n event: AssistantThreadStartedEvent\n) => void | Promise<void>;\n\nexport interface AssistantContextChangedEvent {\n adapter: Adapter;\n channelId: string;\n context: {\n channelId?: string;\n teamId?: string;\n enterpriseId?: string;\n threadEntryPoint?: string;\n forceSearch?: boolean;\n };\n threadId: string;\n threadTs: string;\n userId: string;\n}\n\nexport type AssistantContextChangedHandler = (\n event: AssistantContextChangedEvent\n) => void | Promise<void>;\n\nexport interface AppHomeOpenedEvent {\n adapter: Adapter;\n channelId: string;\n userId: string;\n}\n\nexport type AppHomeOpenedHandler = (\n event: AppHomeOpenedEvent\n) => void | Promise<void>;\n\nexport interface MemberJoinedChannelEvent {\n adapter: Adapter;\n channelId: string;\n inviterId?: string;\n userId: string;\n}\n\nexport type MemberJoinedChannelHandler = (\n event: MemberJoinedChannelEvent\n) => void | Promise<void>;\n","import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from \"@workflow/serde\";\nimport type { Root } from \"mdast\";\nimport { cardToFallbackText } from \"./cards\";\nimport { ChannelImpl, deriveChannelId } from \"./channel\";\nimport { getChatSingleton } from \"./chat-singleton\";\nimport { fromFullStream } from \"./from-full-stream\";\nimport { type ChatElement, isJSX, toCardElement } from \"./jsx-runtime\";\nimport {\n paragraph,\n parseMarkdown,\n root,\n text as textNode,\n toPlainText,\n} from \"./markdown\";\nimport { Message, type SerializedMessage } from \"./message\";\nimport { StreamingMarkdownRenderer } from \"./streaming-markdown\";\nimport type {\n Adapter,\n AdapterPostableMessage,\n Attachment,\n Author,\n Channel,\n EphemeralMessage,\n PostableMessage,\n PostEphemeralOptions,\n SentMessage,\n StateAdapter,\n StreamChunk,\n StreamEvent,\n StreamOptions,\n Thread,\n} from \"./types\";\nimport { THREAD_STATE_TTL_MS } from \"./types\";\n\n/**\n * Serialized thread data for passing to external systems (e.g., workflow engines).\n */\nexport interface SerializedThread {\n _type: \"chat:Thread\";\n adapterName: string;\n channelId: string;\n currentMessage?: SerializedMessage;\n id: string;\n isDM: boolean;\n}\n\n/**\n * Config for creating a ThreadImpl with explicit adapter/state instances.\n */\ninterface ThreadImplConfigWithAdapter {\n adapter: Adapter;\n channelId: string;\n currentMessage?: Message;\n fallbackStreamingPlaceholderText?: string | null;\n id: string;\n initialMessage?: Message;\n isDM?: boolean;\n isSubscribedContext?: boolean;\n stateAdapter: StateAdapter;\n streamingUpdateIntervalMs?: number;\n}\n\n/**\n * Config for creating a ThreadImpl with lazy adapter resolution.\n * The adapter will be looked up from the Chat singleton on first access.\n */\ninterface ThreadImplConfigLazy {\n adapterName: string;\n channelId: string;\n currentMessage?: Message;\n fallbackStreamingPlaceholderText?: string | null;\n id: string;\n initialMessage?: Message;\n isDM?: boolean;\n isSubscribedContext?: boolean;\n streamingUpdateIntervalMs?: number;\n}\n\ntype ThreadImplConfig = ThreadImplConfigWithAdapter | ThreadImplConfigLazy;\n\nfunction isLazyConfig(\n config: ThreadImplConfig\n): config is ThreadImplConfigLazy {\n return \"adapterName\" in config && !(\"adapter\" in config);\n}\n\n/** State key prefix for thread state */\nconst THREAD_STATE_KEY_PREFIX = \"thread-state:\";\n\n/**\n * Check if a value is an AsyncIterable (like AI SDK's textStream or fullStream).\n */\nfunction isAsyncIterable(\n value: unknown\n): value is AsyncIterable<string | StreamChunk | StreamEvent> {\n return (\n value !== null && typeof value === \"object\" && Symbol.asyncIterator in value\n );\n}\n\nexport class ThreadImpl<TState = Record<string, unknown>>\n implements Thread<TState>\n{\n readonly id: string;\n readonly channelId: string;\n readonly isDM: boolean;\n\n /** Direct adapter instance (if provided) */\n private _adapter?: Adapter;\n /** Adapter name for lazy resolution */\n private readonly _adapterName?: string;\n /** Direct state adapter instance (if provided) */\n private _stateAdapterInstance?: StateAdapter;\n private _recentMessages: Message[] = [];\n private readonly _isSubscribedContext: boolean;\n /** Current message context for streaming - provides userId/teamId */\n private readonly _currentMessage?: Message;\n /** Update interval for fallback streaming */\n private readonly _streamingUpdateIntervalMs: number;\n /** Placeholder text for fallback streaming (post + edit) */\n private readonly _fallbackStreamingPlaceholderText: string | null;\n /** Cached channel instance */\n private _channel?: Channel<TState>;\n\n constructor(config: ThreadImplConfig) {\n this.id = config.id;\n this.channelId = config.channelId;\n this.isDM = config.isDM ?? false;\n this._isSubscribedContext = config.isSubscribedContext ?? false;\n this._currentMessage = config.currentMessage;\n this._streamingUpdateIntervalMs = config.streamingUpdateIntervalMs ?? 500;\n this._fallbackStreamingPlaceholderText =\n config.fallbackStreamingPlaceholderText !== undefined\n ? config.fallbackStreamingPlaceholderText\n : \"...\";\n\n if (isLazyConfig(config)) {\n // Lazy resolution mode - store adapter name for later lookup\n this._adapterName = config.adapterName;\n } else {\n // Direct mode - store adapter and state instances\n this._adapter = config.adapter;\n this._stateAdapterInstance = config.stateAdapter;\n }\n\n if (config.initialMessage) {\n this._recentMessages = [config.initialMessage];\n }\n }\n\n /**\n * Get the adapter for this thread.\n * If created with lazy config, resolves from Chat singleton on first access.\n */\n get adapter(): Adapter {\n if (this._adapter) {\n return this._adapter;\n }\n\n if (!this._adapterName) {\n throw new Error(\"Thread has no adapter configured\");\n }\n\n // Lazy resolution from singleton\n const chat = getChatSingleton();\n const adapter = chat.getAdapter(this._adapterName);\n if (!adapter) {\n throw new Error(\n `Adapter \"${this._adapterName}\" not found in Chat singleton`\n );\n }\n\n // Cache for subsequent accesses\n this._adapter = adapter;\n return adapter;\n }\n\n /**\n * Get the state adapter for this thread.\n * If created with lazy config, resolves from Chat singleton on first access.\n */\n private get _stateAdapter(): StateAdapter {\n if (this._stateAdapterInstance) {\n return this._stateAdapterInstance;\n }\n\n // Lazy resolution from singleton\n const chat = getChatSingleton();\n this._stateAdapterInstance = chat.getState();\n return this._stateAdapterInstance;\n }\n\n get recentMessages(): Message[] {\n return this._recentMessages;\n }\n\n set recentMessages(messages: Message[]) {\n this._recentMessages = messages;\n }\n\n /**\n * Get the current thread state.\n * Returns null if no state has been set.\n */\n get state(): Promise<TState | null> {\n return this._stateAdapter.get<TState>(\n `${THREAD_STATE_KEY_PREFIX}${this.id}`\n );\n }\n\n /**\n * Set the thread state. Merges with existing state by default.\n * State is persisted for 30 days.\n */\n async setState(\n newState: Partial<TState>,\n options?: { replace?: boolean }\n ): Promise<void> {\n const key = `${THREAD_STATE_KEY_PREFIX}${this.id}`;\n\n if (options?.replace) {\n // Replace entire state\n await this._stateAdapter.set(key, newState, THREAD_STATE_TTL_MS);\n } else {\n // Merge with existing state\n const existing = await this._stateAdapter.get<TState>(key);\n const merged = { ...existing, ...newState };\n await this._stateAdapter.set(key, merged, THREAD_STATE_TTL_MS);\n }\n }\n\n /**\n * Get the Channel containing this thread.\n * Lazy-created and cached.\n */\n get channel(): Channel<TState> {\n if (!this._channel) {\n const channelId = deriveChannelId(this.adapter, this.id);\n this._channel = new ChannelImpl<TState>({\n id: channelId,\n adapter: this.adapter,\n stateAdapter: this._stateAdapter,\n isDM: this.isDM,\n });\n }\n return this._channel;\n }\n\n /**\n * Iterate messages newest first (backward from most recent).\n * Auto-paginates lazily.\n */\n get messages(): AsyncIterable<Message> {\n const adapter = this.adapter;\n const threadId = this.id;\n\n return {\n async *[Symbol.asyncIterator]() {\n let cursor: string | undefined;\n\n while (true) {\n const result = await adapter.fetchMessages(threadId, {\n cursor,\n direction: \"backward\",\n });\n\n // Messages within a page are chronological (oldest first),\n // but we want newest first, so reverse the page\n const reversed = [...result.messages].reverse();\n for (const message of reversed) {\n yield message;\n }\n\n if (!result.nextCursor || result.messages.length === 0) {\n break;\n }\n\n cursor = result.nextCursor;\n }\n },\n };\n }\n\n get allMessages(): AsyncIterable<Message> {\n const adapter = this.adapter;\n const threadId = this.id;\n\n return {\n async *[Symbol.asyncIterator]() {\n let cursor: string | undefined;\n\n while (true) {\n // Use forward direction to iterate from oldest to newest\n const result = await adapter.fetchMessages(threadId, {\n limit: 100,\n cursor,\n direction: \"forward\",\n });\n\n for (const message of result.messages) {\n yield message;\n }\n\n // No more pages if no nextCursor or no messages returned\n if (!result.nextCursor || result.messages.length === 0) {\n break;\n }\n\n cursor = result.nextCursor;\n }\n },\n };\n }\n\n async isSubscribed(): Promise<boolean> {\n // Short-circuit if we know we're in a subscribed context\n if (this._isSubscribedContext) {\n return true;\n }\n return this._stateAdapter.isSubscribed(this.id);\n }\n\n async subscribe(): Promise<void> {\n await this._stateAdapter.subscribe(this.id);\n // Allow adapters to set up platform-specific subscriptions\n if (this.adapter.onThreadSubscribe) {\n await this.adapter.onThreadSubscribe(this.id);\n }\n }\n\n async unsubscribe(): Promise<void> {\n await this._stateAdapter.unsubscribe(this.id);\n }\n\n async post(\n message: string | PostableMessage | ChatElement\n ): Promise<SentMessage> {\n // Handle AsyncIterable (streaming)\n if (isAsyncIterable(message)) {\n return this.handleStream(message);\n }\n\n // After filtering out streams, we have an AdapterPostableMessage\n // Auto-convert JSX elements to CardElement\n let postable: string | AdapterPostableMessage = message as\n | string\n | AdapterPostableMessage;\n if (isJSX(message)) {\n const card = toCardElement(message);\n if (!card) {\n throw new Error(\"Invalid JSX element: must be a Card element\");\n }\n postable = card;\n }\n\n const rawMessage = await this.adapter.postMessage(this.id, postable);\n\n // Create a SentMessage with edit/delete capabilities\n const result = this.createSentMessage(\n rawMessage.id,\n postable,\n rawMessage.threadId\n );\n return result;\n }\n\n async postEphemeral(\n user: string | Author,\n message: AdapterPostableMessage | ChatElement,\n options: PostEphemeralOptions\n ): Promise<EphemeralMessage | null> {\n const { fallbackToDM } = options;\n const userId = typeof user === \"string\" ? user : user.userId;\n\n // Convert JSX to card if needed\n let postable: AdapterPostableMessage;\n if (isJSX(message)) {\n const card = toCardElement(message);\n if (!card) {\n throw new Error(\"Invalid JSX element: must be a Card element\");\n }\n postable = card;\n } else {\n // Safe cast: if not JSX, it must be AdapterPostableMessage\n postable = message as AdapterPostableMessage;\n }\n\n // Try native ephemeral if adapter supports it\n if (this.adapter.postEphemeral) {\n return this.adapter.postEphemeral(this.id, userId, postable);\n }\n\n // No native support - either fallback to DM or return null\n if (!fallbackToDM) {\n return null;\n }\n\n // Fallback: send via DM\n if (this.adapter.openDM) {\n const dmThreadId = await this.adapter.openDM(userId);\n const result = await this.adapter.postMessage(dmThreadId, postable);\n return {\n id: result.id,\n threadId: dmThreadId,\n usedFallback: true,\n raw: result.raw,\n };\n }\n\n // No DM support either - return null\n return null;\n }\n\n /**\n * Handle streaming from an AsyncIterable.\n * Normalizes the stream (supports both textStream and fullStream from AI SDK),\n * then uses adapter's native streaming if available, otherwise falls back to post+edit.\n */\n private async handleStream(\n rawStream: AsyncIterable<string | StreamChunk | StreamEvent>\n ): Promise<SentMessage> {\n // Normalize: handles plain strings, AI SDK fullStream events, and StreamChunk objects\n const textStream = fromFullStream(rawStream);\n // Build streaming options from current message context\n const options: StreamOptions = {};\n if (this._currentMessage) {\n options.recipientUserId = this._currentMessage.author.userId;\n // Extract teamId from raw Slack payload\n const raw = this._currentMessage.raw as {\n team_id?: string;\n team?: string;\n };\n options.recipientTeamId = raw?.team_id ?? raw?.team;\n }\n\n // Use native streaming if adapter supports it\n if (this.adapter.stream) {\n // Wrap stream to collect accumulated text while passing through to adapter.\n // StreamChunk objects are passed through; only plain strings are accumulated.\n let accumulated = \"\";\n const wrappedStream: AsyncIterable<string | StreamChunk> = {\n [Symbol.asyncIterator]: () => {\n const iterator = textStream[Symbol.asyncIterator]();\n return {\n async next() {\n const result = await iterator.next();\n if (!result.done) {\n const value = result.value;\n if (typeof value === \"string\") {\n accumulated += value;\n } else if (value.type === \"markdown_text\") {\n accumulated += value.text;\n }\n // task_update and plan_update chunks don't contribute to accumulated text\n }\n return result;\n },\n };\n },\n };\n\n const raw = await this.adapter.stream(this.id, wrappedStream, options);\n return this.createSentMessage(\n raw.id,\n { markdown: accumulated },\n raw.threadId\n );\n }\n\n // Fallback: post + edit with throttling.\n // Extract only text content from the mixed stream for adapters without native streaming.\n const textOnlyStream: AsyncIterable<string> = {\n [Symbol.asyncIterator]: () => {\n const iterator = textStream[Symbol.asyncIterator]();\n return {\n async next(): Promise<IteratorResult<string>> {\n while (true) {\n const result = await iterator.next();\n if (result.done) {\n return { value: undefined as unknown as string, done: true };\n }\n const value = result.value;\n if (typeof value === \"string\") {\n return { value, done: false };\n }\n if (value.type === \"markdown_text\") {\n return { value: value.text, done: false };\n }\n // Skip non-text chunks (task_update, plan_update) in fallback mode\n }\n },\n };\n },\n };\n return this.fallbackStream(textOnlyStream, options);\n }\n\n async startTyping(status?: string): Promise<void> {\n await this.adapter.startTyping(this.id, status);\n }\n\n /**\n * Fallback streaming implementation using post + edit.\n * Used when adapter doesn't support native streaming.\n * Uses recursive setTimeout to send updates every intervalMs (default 500ms).\n * Schedules next update only after current edit completes to avoid overwhelming slow services.\n */\n private async fallbackStream(\n textStream: AsyncIterable<string>,\n options?: StreamOptions\n ): Promise<SentMessage> {\n const intervalMs =\n options?.updateIntervalMs ?? this._streamingUpdateIntervalMs;\n const placeholderText = this._fallbackStreamingPlaceholderText;\n let msg: { id: string; threadId: string; raw: unknown } | null =\n placeholderText === null\n ? null\n : await this.adapter.postMessage(this.id, placeholderText);\n let threadIdForEdits = this.id;\n const renderer = new StreamingMarkdownRenderer();\n let lastEditContent = \"\";\n let stopped = false;\n let pendingEdit: Promise<void> | null = null;\n let timerId: ReturnType<typeof setTimeout> | null = null;\n\n if (msg) {\n threadIdForEdits = msg.threadId || this.id;\n lastEditContent = placeholderText ?? \"\";\n }\n\n const scheduleNextEdit = (): void => {\n timerId = setTimeout(() => {\n pendingEdit = doEditAndReschedule();\n }, intervalMs);\n };\n\n const doEditAndReschedule = async (): Promise<void> => {\n if (stopped || !msg) {\n return;\n }\n\n const content = renderer.render();\n if (content !== lastEditContent) {\n try {\n await this.adapter.editMessage(threadIdForEdits, msg.id, {\n markdown: content,\n });\n lastEditContent = content;\n } catch {\n // Ignore errors, continue\n }\n }\n\n // Schedule next check after intervalMs (only after edit completes)\n if (!stopped) {\n scheduleNextEdit();\n }\n };\n\n if (msg) {\n scheduleNextEdit();\n }\n\n try {\n for await (const chunk of textStream) {\n renderer.push(chunk);\n if (!msg) {\n const content = renderer.render();\n msg = await this.adapter.postMessage(this.id, {\n markdown: content,\n });\n threadIdForEdits = msg.threadId || this.id;\n lastEditContent = content;\n scheduleNextEdit();\n }\n }\n } finally {\n stopped = true;\n if (timerId) {\n clearTimeout(timerId);\n timerId = null;\n }\n }\n\n // Wait for any pending edit to complete\n if (pendingEdit) {\n await pendingEdit;\n }\n\n const accumulated = renderer.getText();\n const finalContent = renderer.finish();\n\n if (!msg) {\n msg = await this.adapter.postMessage(this.id, {\n markdown: accumulated,\n });\n threadIdForEdits = msg.threadId || this.id;\n lastEditContent = accumulated;\n }\n\n if (finalContent !== lastEditContent) {\n await this.adapter.editMessage(threadIdForEdits, msg.id, {\n markdown: accumulated,\n });\n }\n\n return this.createSentMessage(\n msg.id,\n { markdown: accumulated },\n threadIdForEdits\n );\n }\n\n async refresh(): Promise<void> {\n const result = await this.adapter.fetchMessages(this.id, { limit: 50 });\n this._recentMessages = result.messages;\n }\n\n mentionUser(userId: string): string {\n return `<@${userId}>`;\n }\n\n /**\n * Serialize the thread to a plain JSON object.\n * Use this to pass thread data to external systems like workflow engines.\n *\n * @example\n * ```typescript\n * // Pass to a workflow\n * await workflow.start(\"my-workflow\", {\n * thread: thread.toJSON(),\n * message: serializeMessage(message),\n * });\n * ```\n */\n toJSON(): SerializedThread {\n return {\n _type: \"chat:Thread\",\n id: this.id,\n channelId: this.channelId,\n currentMessage: this._currentMessage?.toJSON(),\n isDM: this.isDM,\n adapterName: this.adapter.name,\n };\n }\n\n /**\n * Reconstruct a Thread from serialized JSON data.\n *\n * Reconstructs a ThreadImpl from serialized data.\n * Uses lazy resolution from Chat.getSingleton() for adapter and state.\n *\n * @param json - Serialized thread data\n * @requires Call `chat.registerSingleton()` before deserializing threads\n *\n * @example\n * ```typescript\n * const thread = ThreadImpl.fromJSON(serializedThread);\n * ```\n */\n static fromJSON<TState = Record<string, unknown>>(\n json: SerializedThread,\n adapter?: Adapter\n ): ThreadImpl<TState> {\n const thread = new ThreadImpl<TState>({\n id: json.id,\n adapterName: json.adapterName,\n channelId: json.channelId,\n currentMessage: json.currentMessage\n ? Message.fromJSON(json.currentMessage)\n : undefined,\n isDM: json.isDM,\n });\n if (adapter) {\n thread._adapter = adapter;\n }\n return thread;\n }\n\n /**\n * Serialize a ThreadImpl instance for @workflow/serde.\n * This static method is automatically called by workflow serialization.\n */\n static [WORKFLOW_SERIALIZE](instance: ThreadImpl): SerializedThread {\n return instance.toJSON();\n }\n\n /**\n * Deserialize a ThreadImpl from @workflow/serde.\n * Uses lazy adapter resolution from Chat.getSingleton().\n * Requires chat.registerSingleton() to have been called.\n */\n static [WORKFLOW_DESERIALIZE](data: SerializedThread): ThreadImpl {\n return ThreadImpl.fromJSON(data);\n }\n\n private createSentMessage(\n messageId: string,\n postable: AdapterPostableMessage,\n threadIdOverride?: string\n ): SentMessage {\n const adapter = this.adapter;\n // Use the threadId returned by postMessage if available (may differ after thread creation)\n const threadId = threadIdOverride || this.id;\n const self = this;\n\n // Extract text and AST from the PostableMessage\n const { plainText, formatted, attachments } =\n extractMessageContent(postable);\n\n const sentMessage: SentMessage = {\n id: messageId,\n threadId,\n text: plainText,\n formatted,\n raw: null, // Will be populated if needed\n author: {\n userId: \"self\",\n userName: adapter.userName,\n fullName: adapter.userName,\n isBot: true,\n isMe: true,\n },\n metadata: {\n dateSent: new Date(),\n edited: false,\n },\n attachments,\n\n toJSON() {\n return new Message(this).toJSON();\n },\n\n async edit(\n newContent: string | PostableMessage | ChatElement\n ): Promise<SentMessage> {\n // Auto-convert JSX elements to CardElement\n // edit doesn't support streaming, so use AdapterPostableMessage\n let postable: string | AdapterPostableMessage = newContent as\n | string\n | AdapterPostableMessage;\n if (isJSX(newContent)) {\n const card = toCardElement(newContent);\n if (!card) {\n throw new Error(\"Invalid JSX element: must be a Card element\");\n }\n postable = card;\n }\n await adapter.editMessage(threadId, messageId, postable);\n return self.createSentMessage(messageId, postable);\n },\n\n async delete(): Promise<void> {\n await adapter.deleteMessage(threadId, messageId);\n },\n\n async addReaction(emoji: string): Promise<void> {\n await adapter.addReaction(threadId, messageId, emoji);\n },\n\n async removeReaction(emoji: string): Promise<void> {\n await adapter.removeReaction(threadId, messageId, emoji);\n },\n };\n\n return sentMessage;\n }\n\n createSentMessageFromMessage(message: Message): SentMessage {\n const adapter = this.adapter;\n const threadId = this.id;\n const messageId = message.id;\n const self = this;\n\n return {\n id: message.id,\n threadId: message.threadId,\n text: message.text,\n formatted: message.formatted,\n raw: message.raw,\n author: message.author,\n metadata: message.metadata,\n attachments: message.attachments,\n isMention: message.isMention,\n\n toJSON() {\n return message.toJSON();\n },\n\n async edit(\n newContent: string | PostableMessage | ChatElement\n ): Promise<SentMessage> {\n let postable: string | AdapterPostableMessage = newContent as\n | string\n | AdapterPostableMessage;\n if (isJSX(newContent)) {\n const card = toCardElement(newContent);\n if (!card) {\n throw new Error(\"Invalid JSX element: must be a Card element\");\n }\n postable = card;\n }\n await adapter.editMessage(threadId, messageId, postable);\n return self.createSentMessage(messageId, postable, threadId);\n },\n\n async delete(): Promise<void> {\n await adapter.deleteMessage(threadId, messageId);\n },\n\n async addReaction(emoji: string): Promise<void> {\n await adapter.addReaction(threadId, messageId, emoji);\n },\n\n async removeReaction(emoji: string): Promise<void> {\n await adapter.removeReaction(threadId, messageId, emoji);\n },\n };\n }\n}\n\n/**\n * Extract plain text, AST, and attachments from a message.\n */\nfunction extractMessageContent(message: AdapterPostableMessage): {\n plainText: string;\n formatted: Root;\n attachments: Attachment[];\n} {\n if (typeof message === \"string\") {\n // Raw string - create simple AST\n return {\n plainText: message,\n formatted: root([paragraph([textNode(message)])]),\n attachments: [],\n };\n }\n\n if (\"raw\" in message) {\n // Raw text - create simple AST\n return {\n plainText: message.raw,\n formatted: root([paragraph([textNode(message.raw)])]),\n attachments: message.attachments || [],\n };\n }\n\n if (\"markdown\" in message) {\n // Markdown - parse to AST\n const ast = parseMarkdown(message.markdown);\n return {\n plainText: toPlainText(ast),\n formatted: ast,\n attachments: message.attachments || [],\n };\n }\n\n if (\"ast\" in message) {\n // AST provided directly\n return {\n plainText: toPlainText(message.ast),\n formatted: message.ast,\n attachments: message.attachments || [],\n };\n }\n\n if (\"card\" in message) {\n // PostableCard - generate fallback text from card\n const fallbackText =\n message.fallbackText || cardToFallbackText(message.card);\n return {\n plainText: fallbackText,\n formatted: root([paragraph([textNode(fallbackText)])]),\n attachments: [],\n };\n }\n\n if (\"type\" in message && message.type === \"card\") {\n // Direct CardElement\n const fallbackText = cardToFallbackText(message);\n return {\n plainText: fallbackText,\n formatted: root([paragraph([textNode(fallbackText)])]),\n attachments: [],\n };\n }\n\n // Should never reach here with proper typing\n throw new Error(\"Invalid PostableMessage format\");\n}\n","import type { StreamChunk } from \"./types\";\n\nconst STREAM_CHUNK_TYPES = new Set([\n \"markdown_text\",\n \"task_update\",\n \"plan_update\",\n]);\n\n/**\n * Normalizes an async iterable stream for use with `thread.post()`.\n *\n * Handles three stream types automatically:\n * - **Text streams** (`AsyncIterable<string>`, e.g. AI SDK `textStream`) —\n * passed through as-is.\n * - **Full streams** (`AsyncIterable<object>`, e.g. AI SDK `fullStream`) —\n * extracts `text-delta` events and injects `\"\\n\\n\"` separators between\n * steps so that multi-step agent output reads naturally.\n * - **StreamChunk objects** (`task_update`, `plan_update`, `markdown_text`) —\n * passed through as-is for adapters with native structured chunk support.\n *\n * This is used internally by `thread.post()`, so you can pass either stream\n * directly:\n * ```ts\n * await thread.post(result.fullStream); // auto-detected\n * await thread.post(result.textStream); // still works\n * ```\n */\nexport async function* fromFullStream(\n stream: AsyncIterable<unknown>\n): AsyncIterable<string | StreamChunk> {\n let needsSeparator = false;\n let hasEmittedText = false;\n\n for await (const event of stream) {\n // Plain string chunk (e.g. from AI SDK textStream)\n if (typeof event === \"string\") {\n yield event;\n continue;\n }\n\n // Skip non-objects\n if (event === null || typeof event !== \"object\" || !(\"type\" in event)) {\n continue;\n }\n const typed = event as {\n delta?: unknown;\n text?: unknown;\n textDelta?: unknown;\n type: string;\n };\n\n // Pass through StreamChunk objects (task_update, plan_update, markdown_text)\n if (STREAM_CHUNK_TYPES.has(typed.type)) {\n yield event as StreamChunk;\n continue;\n }\n\n // AI SDK v5 uses `textDelta`, v6 uses `text`\n const textContent = typed.text ?? typed.delta ?? typed.textDelta;\n if (typed.type === \"text-delta\" && typeof textContent === \"string\") {\n if (needsSeparator && hasEmittedText) {\n yield \"\\n\\n\";\n }\n needsSeparator = false;\n hasEmittedText = true;\n yield textContent;\n } else if (typed.type === \"step-finish\") {\n needsSeparator = true;\n }\n }\n}\n","import remend from \"remend\";\n\n/**\n * A streaming markdown renderer that buffers potential table headers\n * until confirmed by a separator line, preventing tables from flashing\n * as raw pipe-delimited text during LLM streaming.\n *\n * Outputs markdown (not platform text). Format conversion still happens\n * in the adapter's editMessage → renderPostable → fromAst pipeline.\n */\nexport class StreamingMarkdownRenderer {\n private accumulated = \"\";\n private dirty = true;\n private cachedRender = \"\";\n private finished = false;\n /** Number of code fence toggles from completed lines (odd = inside). */\n private fenceToggles = 0;\n /** Incomplete trailing line buffer for incremental fence tracking. */\n private incompleteLine = \"\";\n\n /** Append a chunk from the LLM stream. */\n push(chunk: string): void {\n this.accumulated += chunk;\n this.dirty = true;\n\n // Incrementally track code fence state from completed lines\n this.incompleteLine += chunk;\n const parts = this.incompleteLine.split(\"\\n\");\n this.incompleteLine = parts.pop() ?? \"\";\n for (const line of parts) {\n const trimmed = line.trimStart();\n if (trimmed.startsWith(\"```\") || trimmed.startsWith(\"~~~\")) {\n this.fenceToggles++;\n }\n }\n }\n\n /** O(1) check if accumulated text is inside an unclosed code fence. */\n private isAccumulatedInsideFence(): boolean {\n let inside = this.fenceToggles % 2 === 1;\n const trimmed = this.incompleteLine.trimStart();\n if (trimmed.startsWith(\"```\") || trimmed.startsWith(\"~~~\")) {\n inside = !inside;\n }\n return inside;\n }\n\n /**\n * Get renderable markdown for an intermediate edit.\n * - Holds back trailing lines that look like a table header (|...|)\n * until a separator line (|---|---|) confirms or the next line denies.\n * - Applies remend() to close incomplete inline markers.\n * - Idempotent: returns cached result if no push() since last call.\n */\n render(): string {\n if (!this.dirty) {\n return this.cachedRender;\n }\n\n this.dirty = false;\n\n if (this.finished) {\n this.cachedRender = remend(this.accumulated);\n return this.cachedRender;\n }\n\n // If inside an unclosed code fence, don't buffer (pipes aren't tables)\n if (this.isAccumulatedInsideFence()) {\n this.cachedRender = remend(this.accumulated);\n return this.cachedRender;\n }\n\n const committable = getCommittablePrefix(this.accumulated);\n this.cachedRender = remend(committable);\n return this.cachedRender;\n }\n\n /**\n * Get text safe for append-only streaming (e.g. Slack native streaming).\n *\n * - Holds back unconfirmed table headers until separator arrives.\n * - Wraps confirmed tables in code fences so pipes render as literal\n * text (not broken mrkdwn). The code fence is left OPEN while\n * the table is still streaming, keeping output monotonic for deltas.\n * - Holds back unclosed inline markers (**, *, ~~, `, [).\n * - The final editMessage replaces everything with properly formatted text.\n */\n getCommittableText(): string {\n if (this.finished) {\n return wrapTablesForAppend(this.accumulated, true);\n }\n\n // Strip incomplete last line (no trailing newline) to prevent committing\n // content that might change semantics when completed — e.g. \"| A\" could\n // become \"| A | B |\" which is a table row that should be held back.\n let text = this.accumulated;\n if (text.length > 0 && !text.endsWith(\"\\n\")) {\n const lastNewline = text.lastIndexOf(\"\\n\");\n const withoutIncompleteLine =\n lastNewline >= 0 ? text.slice(0, lastNewline + 1) : \"\";\n\n // If stripping puts us inside a code fence, keep the incomplete line\n // (it's likely the closing fence being typed — content is literal).\n if (isInsideCodeFence(withoutIncompleteLine)) {\n // Still wrap preceding tables for consistent coordinate space.\n return wrapTablesForAppend(text);\n }\n\n text = withoutIncompleteLine;\n }\n\n // Inside a user code fence: skip table holding and inline marker buffering\n // (pipes and markers are literal inside fences), but still wrap preceding\n // confirmed tables for consistent coordinate space.\n if (isInsideCodeFence(text)) {\n return wrapTablesForAppend(text);\n }\n\n const committed = getCommittablePrefix(text);\n const wrapped = wrapTablesForAppend(committed);\n\n // If text ends inside an open table code fence,\n // skip inline marker buffering — markers in code blocks are literal\n if (isInsideCodeFence(wrapped)) {\n return wrapped;\n }\n\n return findCleanPrefix(wrapped);\n }\n\n /** Raw accumulated text (no remend, no buffering). For the final edit. */\n getText(): string {\n return this.accumulated;\n }\n\n /** Signal stream end. Flushes held-back lines. Returns final render. */\n finish(): string {\n this.finished = true;\n this.dirty = true;\n return this.render();\n }\n}\n\n/**\n * Characters that can open an inline markdown construct.\n * Used to find the cut point when text has unclosed markers.\n */\nconst INLINE_MARKER_CHARS = new Set([\"*\", \"~\", \"`\", \"[\"]);\n\n/**\n * Check if text is \"clean\" — remend doesn't add any closing markers.\n * Uses length comparison because remend may trim trailing whitespace\n * from otherwise clean text (which is harmless for streaming).\n */\nfunction isClean(text: string): boolean {\n return remend(text).length <= text.length;\n}\n\n/**\n * Returns the longest prefix of `text` where all inline markers are balanced\n * (i.e. remend would not add closing markers). Scans backward from the end\n * for potential opening markers, grouping consecutive same characters to\n * handle multi-char markers like ** and ~~.\n *\n * Typically resolves in 1-3 remend calls since unclosed markers are\n * almost always near the end of the text.\n */\nfunction findCleanPrefix(text: string): string {\n if (text.length === 0 || isClean(text)) {\n return text;\n }\n\n for (let i = text.length - 1; i >= 0; i--) {\n if (INLINE_MARKER_CHARS.has(text[i])) {\n // Group consecutive same characters (e.g., ** or ~~)\n while (i > 0 && text[i - 1] === text[i]) {\n i--;\n }\n const candidate = text.slice(0, i);\n if (isClean(candidate)) {\n return candidate;\n }\n }\n }\n\n return \"\";\n}\n\nconst TABLE_ROW_RE = /^\\|.*\\|$/;\nconst TABLE_SEPARATOR_RE = /^\\|[\\s:]*-{1,}[\\s:]*(\\|[\\s:]*-{1,}[\\s:]*)*\\|$/;\n\n/**\n * Check if the text ends inside an unclosed code fence.\n * Counts the number of ``` (or ~~~) fence openers/closers.\n */\nfunction isInsideCodeFence(text: string): boolean {\n let inside = false;\n for (const line of text.split(\"\\n\")) {\n const trimmed = line.trimStart();\n if (trimmed.startsWith(\"```\") || trimmed.startsWith(\"~~~\")) {\n inside = !inside;\n }\n }\n return inside;\n}\n\n/**\n * Returns the prefix of `text` that can be safely rendered,\n * holding back trailing lines that look like an unconfirmed table.\n *\n * A table is \"confirmed\" when a separator line (|---|---|) appears\n * after a header row. Until then, potential table rows at the end\n * of the text are withheld.\n */\nfunction getCommittablePrefix(text: string): string {\n // Split into lines, keeping track of whether the text ends with a newline\n const endsWithNewline = text.endsWith(\"\\n\");\n const lines = text.split(\"\\n\");\n\n // If the text doesn't end with newline, the last line is still being\n // written to. Remove it from consideration for table detection.\n if (!endsWithNewline && lines.length > 0) {\n lines.pop();\n }\n\n // Remove trailing empty string from split (if text ends with \\n)\n if (endsWithNewline && lines.length > 0 && lines.at(-1) === \"\") {\n lines.pop();\n }\n\n // Walk backward to find consecutive table-like lines at the end\n let heldCount = 0;\n let separatorFound = false;\n\n for (let i = lines.length - 1; i >= 0; i--) {\n const trimmed = lines[i].trim();\n\n // Empty line breaks a table block\n if (trimmed === \"\") {\n break;\n }\n\n if (TABLE_SEPARATOR_RE.test(trimmed)) {\n // Separator found — table is confirmed from here upward\n separatorFound = true;\n break;\n }\n\n if (TABLE_ROW_RE.test(trimmed)) {\n heldCount++;\n } else {\n // Non-table line breaks the run\n break;\n }\n }\n\n if (separatorFound || heldCount === 0) {\n // Table confirmed or no table-like lines — commit everything\n return text;\n }\n\n // Hold back the trailing table-like lines\n const commitLineCount = lines.length - heldCount;\n const committedLines = lines.slice(0, commitLineCount);\n\n // Reconstruct: committed lines + trailing newline\n let result = committedLines.join(\"\\n\");\n // Preserve a trailing newline if there were committed lines\n if (committedLines.length > 0) {\n result += \"\\n\";\n }\n\n return result;\n}\n\n/**\n * Wraps confirmed GFM table blocks in code fences for append-only streaming.\n *\n * Append-only APIs (e.g. Slack streaming) can't render GFM tables natively.\n * Wrapping in a code fence makes pipes display as readable literal text.\n *\n * The code fence is left OPEN if the table is ongoing (no closing ```)\n * so that output remains monotonic — each new row just extends the block.\n * The fence is closed when a non-table line follows.\n */\nfunction wrapTablesForAppend(text: string, closeFences = false): string {\n const hadTrailingNewline = text.endsWith(\"\\n\");\n const lines = text.split(\"\\n\");\n\n // Remove trailing empty string from split (artifact of trailing newline)\n if (hadTrailingNewline && lines.length > 0 && lines.at(-1) === \"\") {\n lines.pop();\n }\n\n const result: string[] = [];\n let inTable = false;\n let inUserCodeFence = false;\n\n for (let i = 0; i < lines.length; i++) {\n const trimmed = lines[i].trim();\n\n // Track existing code fences in the source markdown.\n // Don't detect tables inside user code fences.\n if (!inTable && (trimmed.startsWith(\"```\") || trimmed.startsWith(\"~~~\"))) {\n inUserCodeFence = !inUserCodeFence;\n result.push(lines[i]);\n continue;\n }\n\n if (inUserCodeFence) {\n result.push(lines[i]);\n continue;\n }\n\n const isTableLine =\n trimmed !== \"\" &&\n (TABLE_ROW_RE.test(trimmed) || TABLE_SEPARATOR_RE.test(trimmed));\n\n if (isTableLine && !inTable) {\n // Only wrap if this block has a separator (confirmed table)\n let hasSeparator = false;\n for (let j = i; j < lines.length; j++) {\n const t = lines[j].trim();\n if (TABLE_SEPARATOR_RE.test(t)) {\n hasSeparator = true;\n break;\n }\n if (t === \"\" || !TABLE_ROW_RE.test(t)) {\n break;\n }\n }\n if (hasSeparator) {\n result.push(\"```\");\n inTable = true;\n }\n } else if (!isTableLine && inTable) {\n result.push(\"```\");\n inTable = false;\n }\n\n result.push(lines[i]);\n }\n\n // Close the fence if requested (e.g. after stream finishes)\n if (inTable && closeFences) {\n result.push(\"```\");\n }\n // Otherwise if inTable is true, the code fence is intentionally left OPEN\n // for monotonic appending — it'll be closed when the table ends.\n\n let output = result.join(\"\\n\");\n if (hadTrailingNewline) {\n output += \"\\n\";\n }\n return output;\n}\n","import { ChannelImpl, type SerializedChannel } from \"./channel\";\nimport {\n getChatSingleton,\n hasChatSingleton,\n setChatSingleton,\n} from \"./chat-singleton\";\nimport { isJSX, toModalElement } from \"./jsx-runtime\";\nimport { Message, type SerializedMessage } from \"./message\";\nimport type { ModalElement } from \"./modals\";\nimport { type SerializedThread, ThreadImpl } from \"./thread\";\nimport type {\n ActionEvent,\n ActionHandler,\n Adapter,\n AppHomeOpenedEvent,\n AppHomeOpenedHandler,\n AssistantContextChangedEvent,\n AssistantContextChangedHandler,\n AssistantThreadStartedEvent,\n AssistantThreadStartedHandler,\n Author,\n Channel,\n ChatConfig,\n ChatInstance,\n EmojiValue,\n Logger,\n LogLevel,\n MemberJoinedChannelEvent,\n MemberJoinedChannelHandler,\n MentionHandler,\n MessageHandler,\n ModalCloseEvent,\n ModalCloseHandler,\n ModalResponse,\n ModalSubmitEvent,\n ModalSubmitHandler,\n ReactionEvent,\n ReactionHandler,\n SentMessage,\n SlashCommandEvent,\n SlashCommandHandler,\n StateAdapter,\n SubscribedMessageHandler,\n Thread,\n WebhookOptions,\n} from \"./types\";\nimport { ChatError, ConsoleLogger, LockError } from \"./types\";\n\nconst DEFAULT_LOCK_TTL_MS = 30_000; // 30 seconds\nconst SLACK_USER_ID_REGEX = /^U[A-Z0-9]+$/i;\nconst DISCORD_SNOWFLAKE_REGEX = /^\\d{17,19}$/;\n/** TTL for message deduplication entries */\nconst DEDUPE_TTL_MS = 5 * 60 * 1000; // 5 minutes\nconst MODAL_CONTEXT_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours\n\n/** Server-side stored modal context */\ninterface StoredModalContext {\n channel?: SerializedChannel;\n message?: SerializedMessage;\n thread?: SerializedThread;\n}\n\ninterface MessagePattern<TState = Record<string, unknown>> {\n handler: MessageHandler<TState>;\n pattern: RegExp;\n}\n\n/** Filter can be EmojiValue objects, emoji names, or raw emoji formats */\ntype EmojiFilter = EmojiValue | string;\n\ninterface ReactionPattern {\n /** If specified, only these emoji trigger the handler. Empty means all emoji. */\n emoji: EmojiFilter[];\n handler: ReactionHandler;\n}\n\ninterface ActionPattern {\n /** If specified, only these action IDs trigger the handler. Empty means all actions. */\n actionIds: string[];\n handler: ActionHandler;\n}\n\ninterface ModalSubmitPattern {\n callbackIds: string[];\n handler: ModalSubmitHandler;\n}\n\ninterface ModalClosePattern {\n callbackIds: string[];\n handler: ModalCloseHandler;\n}\n\ninterface SlashCommandPattern<TState = Record<string, unknown>> {\n /** If specified, only these commands trigger the handler. Empty means all commands. */\n commands: string[];\n handler: SlashCommandHandler<TState>;\n}\n\n/**\n * Type-safe webhook handler that is available for each adapter.\n */\ntype WebhookHandler = (\n request: Request,\n options?: WebhookOptions\n) => Promise<Response>;\n\n/**\n * Creates a type-safe webhooks object based on the adapter names.\n */\ntype Webhooks<TAdapters extends Record<string, Adapter>> = {\n [K in keyof TAdapters]: WebhookHandler;\n};\n\n/**\n * Main Chat class with type-safe adapter inference and custom thread state.\n *\n * @template TAdapters - Map of adapter names to Adapter instances\n * @template TState - Custom state type stored per-thread (default: Record<string, unknown>)\n *\n * @example\n * // Define custom thread state type\n * interface MyThreadState {\n * aiMode?: boolean;\n * userName?: string;\n * }\n *\n * const chat = new Chat<typeof adapters, MyThreadState>({\n * userName: \"mybot\",\n * adapters: {\n * slack: createSlackAdapter({ ... }),\n * teams: createTeamsAdapter({ ... }),\n * },\n * state: createMemoryState(),\n * });\n *\n * // Type-safe thread state\n * chat.onNewMention(async (thread, message) => {\n * await thread.setState({ aiMode: true });\n * const state = await thread.state; // Type: MyThreadState | null\n * });\n */\nexport class Chat<\n TAdapters extends Record<string, Adapter> = Record<string, Adapter>,\n TState = Record<string, unknown>,\n> implements ChatInstance\n{\n /**\n * Register this Chat instance as the global singleton.\n * Required for Thread deserialization via @workflow/serde.\n *\n * @example\n * ```typescript\n * const chat = new Chat({ ... });\n * chat.registerSingleton();\n *\n * // Now threads can be deserialized without passing chat explicitly\n * const thread = ThreadImpl.fromJSON(serializedThread);\n * ```\n */\n registerSingleton(): this {\n setChatSingleton(this);\n return this;\n }\n\n /**\n * Get the registered singleton Chat instance.\n * Throws if no singleton has been registered.\n */\n static getSingleton(): Chat {\n return getChatSingleton() as Chat;\n }\n\n /**\n * Check if a singleton has been registered.\n */\n static hasSingleton(): boolean {\n return hasChatSingleton();\n }\n\n private readonly adapters: Map<string, Adapter>;\n private readonly _stateAdapter: StateAdapter;\n private readonly userName: string;\n private readonly logger: Logger;\n private readonly _streamingUpdateIntervalMs: number;\n private readonly _fallbackStreamingPlaceholderText: string | null;\n private readonly _dedupeTtlMs: number;\n\n private readonly mentionHandlers: MentionHandler<TState>[] = [];\n private readonly messagePatterns: MessagePattern<TState>[] = [];\n private readonly subscribedMessageHandlers: SubscribedMessageHandler<TState>[] =\n [];\n private readonly reactionHandlers: ReactionPattern[] = [];\n private readonly actionHandlers: ActionPattern[] = [];\n private readonly modalSubmitHandlers: ModalSubmitPattern[] = [];\n private readonly modalCloseHandlers: ModalClosePattern[] = [];\n private readonly slashCommandHandlers: SlashCommandPattern<TState>[] = [];\n private readonly assistantThreadStartedHandlers: AssistantThreadStartedHandler[] =\n [];\n private readonly assistantContextChangedHandlers: AssistantContextChangedHandler[] =\n [];\n private readonly appHomeOpenedHandlers: AppHomeOpenedHandler[] = [];\n private readonly memberJoinedChannelHandlers: MemberJoinedChannelHandler[] =\n [];\n\n /** Initialization state */\n private initPromise: Promise<void> | null = null;\n private initialized = false;\n\n /**\n * Type-safe webhook handlers keyed by adapter name.\n * @example\n * chat.webhooks.slack(request, { backgroundTask: waitUntil });\n */\n readonly webhooks: Webhooks<TAdapters>;\n\n constructor(config: ChatConfig<TAdapters>) {\n this.userName = config.userName;\n this._stateAdapter = config.state;\n this.adapters = new Map();\n this._streamingUpdateIntervalMs = config.streamingUpdateIntervalMs ?? 500;\n this._fallbackStreamingPlaceholderText =\n config.fallbackStreamingPlaceholderText !== undefined\n ? config.fallbackStreamingPlaceholderText\n : \"...\";\n this._dedupeTtlMs = config.dedupeTtlMs ?? DEDUPE_TTL_MS;\n\n // Initialize logger\n if (typeof config.logger === \"string\") {\n this.logger = new ConsoleLogger(config.logger as LogLevel);\n } else {\n this.logger = config.logger || new ConsoleLogger(\"info\");\n }\n\n // Register adapters and create webhook handlers\n const webhooks = {} as Record<string, WebhookHandler>;\n for (const [name, adapter] of Object.entries(config.adapters)) {\n this.adapters.set(name, adapter);\n // Create webhook handler for each adapter\n webhooks[name] = (request: Request, options?: WebhookOptions) =>\n this.handleWebhook(name, request, options);\n }\n this.webhooks = webhooks as Webhooks<TAdapters>;\n\n this.logger.debug(\"Chat instance created\", {\n adapters: Object.keys(config.adapters),\n });\n }\n\n /**\n * Handle a webhook request for a specific adapter.\n * Automatically initializes adapters on first call.\n */\n private async handleWebhook(\n adapterName: string,\n request: Request,\n options?: WebhookOptions\n ): Promise<Response> {\n // Ensure initialization\n await this.ensureInitialized();\n\n const adapter = this.adapters.get(adapterName);\n if (!adapter) {\n return new Response(`Unknown adapter: ${adapterName}`, { status: 404 });\n }\n\n return adapter.handleWebhook(request, options);\n }\n\n /**\n * Ensure the chat instance is initialized.\n * This is called automatically before handling webhooks.\n */\n private async ensureInitialized(): Promise<void> {\n if (this.initialized) {\n return;\n }\n\n // Avoid concurrent initialization\n if (!this.initPromise) {\n this.initPromise = this.doInitialize();\n }\n\n await this.initPromise;\n }\n\n private async doInitialize(): Promise<void> {\n this.logger.info(\"Initializing chat instance...\");\n await this._stateAdapter.connect();\n this.logger.debug(\"State connected\");\n\n const initPromises = Array.from(this.adapters.values()).map(\n async (adapter) => {\n this.logger.debug(\"Initializing adapter\", adapter.name);\n const result = await adapter.initialize(this);\n this.logger.debug(\"Adapter initialized\", adapter.name);\n return result;\n }\n );\n await Promise.all(initPromises);\n\n this.initialized = true;\n this.logger.info(\"Chat instance initialized\", {\n adapters: Array.from(this.adapters.keys()),\n });\n }\n\n /**\n * Gracefully shut down the chat instance.\n */\n async shutdown(): Promise<void> {\n this.logger.info(\"Shutting down chat instance...\");\n await this._stateAdapter.disconnect();\n this.initialized = false;\n this.initPromise = null;\n this.logger.info(\"Chat instance shut down\");\n }\n\n /**\n * Initialize the chat instance and all adapters.\n * This is called automatically when handling webhooks, but can be called\n * manually for non-webhook use cases (e.g., Gateway listeners).\n */\n async initialize(): Promise<void> {\n await this.ensureInitialized();\n }\n\n /**\n * Register a handler for new @-mentions of the bot.\n *\n * **Important**: This handler is ONLY called for mentions in **unsubscribed** threads.\n * Once a thread is subscribed (via `thread.subscribe()`), subsequent messages\n * including @-mentions go to `onSubscribedMessage` handlers instead.\n *\n * To detect mentions in subscribed threads, check `message.isMention`:\n *\n * @example\n * ```typescript\n * // Handle new mentions (unsubscribed threads only)\n * chat.onNewMention(async (thread, message) => {\n * await thread.subscribe(); // Subscribe to follow-up messages\n * await thread.post(\"Hello! I'll be watching this thread.\");\n * });\n *\n * // Handle all messages in subscribed threads\n * chat.onSubscribedMessage(async (thread, message) => {\n * if (message.isMention) {\n * // User @-mentioned us in a thread we're already watching\n * await thread.post(\"You mentioned me again!\");\n * }\n * });\n * ```\n */\n onNewMention(handler: MentionHandler<TState>): void {\n this.mentionHandlers.push(handler);\n this.logger.debug(\"Registered mention handler\");\n }\n\n /**\n * Register a handler for messages matching a regex pattern.\n *\n * @param pattern - Regular expression to match against message text\n * @param handler - Handler called when pattern matches\n *\n * @example\n * ```typescript\n * // Match messages starting with \"!help\"\n * chat.onNewMessage(/^!help/, async (thread, message) => {\n * await thread.post(\"Available commands: !help, !status, !ping\");\n * });\n * ```\n */\n onNewMessage(pattern: RegExp, handler: MessageHandler<TState>): void {\n this.messagePatterns.push({ pattern, handler });\n this.logger.debug(\"Registered message pattern handler\", {\n pattern: pattern.toString(),\n });\n }\n\n /**\n * Register a handler for messages in subscribed threads.\n *\n * Called for all messages in threads that have been subscribed via `thread.subscribe()`.\n * This includes:\n * - Follow-up messages from users\n * - Messages that @-mention the bot (check `message.isMention`)\n *\n * Does NOT fire for:\n * - The message that triggered the subscription (e.g., the initial @mention)\n * - Messages sent by the bot itself\n *\n * @example\n * ```typescript\n * chat.onSubscribedMessage(async (thread, message) => {\n * // Handle all follow-up messages\n * if (message.isMention) {\n * // User @-mentioned us in a subscribed thread\n * }\n * await thread.post(`Got your message: ${message.text}`);\n * });\n * ```\n */\n onSubscribedMessage(handler: SubscribedMessageHandler<TState>): void {\n this.subscribedMessageHandlers.push(handler);\n this.logger.debug(\"Registered subscribed message handler\");\n }\n\n /**\n * Register a handler for reaction events.\n *\n * @example\n * ```typescript\n * // Handle specific emoji using EmojiValue objects (recommended)\n * chat.onReaction([emoji.thumbs_up, emoji.heart], async (event) => {\n * if (event.emoji === emoji.thumbs_up) {\n * console.log(\"Thumbs up!\");\n * }\n * });\n *\n * // Handle all reactions\n * chat.onReaction(async (event) => {\n * console.log(`${event.added ? \"Added\" : \"Removed\"} ${event.emoji.name}`);\n * });\n * ```\n *\n * @param emojiOrHandler - Either an array of emoji to filter (EmojiValue or string), or the handler\n * @param handler - The handler (if emoji filter is provided)\n */\n onReaction(handler: ReactionHandler): void;\n onReaction(emoji: EmojiFilter[], handler: ReactionHandler): void;\n onReaction(\n emojiOrHandler: EmojiFilter[] | ReactionHandler,\n handler?: ReactionHandler\n ): void {\n if (typeof emojiOrHandler === \"function\") {\n // No emoji filter - handle all reactions\n this.reactionHandlers.push({ emoji: [], handler: emojiOrHandler });\n this.logger.debug(\"Registered reaction handler for all emoji\");\n } else if (handler) {\n // Specific emoji filter\n this.reactionHandlers.push({ emoji: emojiOrHandler, handler });\n this.logger.debug(\"Registered reaction handler\", {\n emoji: emojiOrHandler.map((e) => (typeof e === \"string\" ? e : e.name)),\n });\n }\n }\n\n /**\n * Register a handler for action events (button clicks in cards).\n *\n * @example\n * ```typescript\n * // Handle specific action\n * chat.onAction(\"approve\", async (event) => {\n * await event.thread.post(\"Approved!\");\n * });\n *\n * // Handle multiple actions\n * chat.onAction([\"approve\", \"reject\"], async (event) => {\n * if (event.actionId === \"approve\") {\n * await event.thread.post(\"Approved!\");\n * } else {\n * await event.thread.post(\"Rejected!\");\n * }\n * });\n *\n * // Handle all actions (catch-all)\n * chat.onAction(async (event) => {\n * console.log(`Action: ${event.actionId}`);\n * });\n * ```\n *\n * @param actionIdOrHandler - Either an action ID, array of action IDs, or the handler\n * @param handler - The handler (if action ID filter is provided)\n */\n onAction(handler: ActionHandler): void;\n onAction(actionIds: string[] | string, handler: ActionHandler): void;\n onAction(\n actionIdOrHandler: string | string[] | ActionHandler,\n handler?: ActionHandler\n ): void {\n if (typeof actionIdOrHandler === \"function\") {\n // No action filter - handle all actions\n this.actionHandlers.push({ actionIds: [], handler: actionIdOrHandler });\n this.logger.debug(\"Registered action handler for all actions\");\n } else if (handler) {\n // Specific action ID(s) filter\n const actionIds = Array.isArray(actionIdOrHandler)\n ? actionIdOrHandler\n : [actionIdOrHandler];\n this.actionHandlers.push({ actionIds, handler });\n this.logger.debug(\"Registered action handler\", { actionIds });\n }\n }\n\n onModalSubmit(handler: ModalSubmitHandler): void;\n onModalSubmit(\n callbackIds: string[] | string,\n handler: ModalSubmitHandler\n ): void;\n onModalSubmit(\n callbackIdOrHandler: string | string[] | ModalSubmitHandler,\n handler?: ModalSubmitHandler\n ): void {\n if (typeof callbackIdOrHandler === \"function\") {\n this.modalSubmitHandlers.push({\n callbackIds: [],\n handler: callbackIdOrHandler,\n });\n this.logger.debug(\"Registered modal submit handler for all modals\");\n } else if (handler) {\n const callbackIds = Array.isArray(callbackIdOrHandler)\n ? callbackIdOrHandler\n : [callbackIdOrHandler];\n this.modalSubmitHandlers.push({ callbackIds, handler });\n this.logger.debug(\"Registered modal submit handler\", { callbackIds });\n }\n }\n\n onModalClose(handler: ModalCloseHandler): void;\n onModalClose(\n callbackIds: string[] | string,\n handler: ModalCloseHandler\n ): void;\n onModalClose(\n callbackIdOrHandler: string | string[] | ModalCloseHandler,\n handler?: ModalCloseHandler\n ): void {\n if (typeof callbackIdOrHandler === \"function\") {\n this.modalCloseHandlers.push({\n callbackIds: [],\n handler: callbackIdOrHandler,\n });\n this.logger.debug(\"Registered modal close handler for all modals\");\n } else if (handler) {\n const callbackIds = Array.isArray(callbackIdOrHandler)\n ? callbackIdOrHandler\n : [callbackIdOrHandler];\n this.modalCloseHandlers.push({ callbackIds, handler });\n this.logger.debug(\"Registered modal close handler\", { callbackIds });\n }\n }\n\n /**\n * Register a handler for slash command events.\n *\n * Slash commands are triggered when a user types `/command` in the message composer.\n * Use `event.channel.post()` or `event.channel.postEphemeral()` to respond.\n *\n * @example\n * ```typescript\n * // Handle a specific command\n * chat.onSlashCommand(\"/help\", async (event) => {\n * await event.channel.post(\"Here are the available commands...\");\n * });\n *\n * // Handle multiple commands\n * chat.onSlashCommand([\"/status\", \"/health\"], async (event) => {\n * await event.channel.post(\"All systems operational!\");\n * });\n *\n * // Handle all commands (catch-all)\n * chat.onSlashCommand(async (event) => {\n * console.log(`Received command: ${event.command} ${event.text}`);\n * });\n *\n * // Open a modal from a slash command\n * chat.onSlashCommand(\"/feedback\", async (event) => {\n * await event.openModal({\n * callbackId: \"feedback_modal\",\n * title: \"Submit Feedback\",\n * inputs: [{ id: \"feedback\", type: \"text_input\", label: \"Your feedback\" }],\n * });\n * });\n * ```\n *\n * @param commandOrHandler - Either a command, array of commands, or the handler\n * @param handler - The handler (if command filter is provided)\n */\n onSlashCommand(handler: SlashCommandHandler<TState>): void;\n onSlashCommand(\n commands: string[] | string,\n handler: SlashCommandHandler<TState>\n ): void;\n onSlashCommand(\n commandOrHandler: string | string[] | SlashCommandHandler<TState>,\n handler?: SlashCommandHandler<TState>\n ): void {\n if (typeof commandOrHandler === \"function\") {\n this.slashCommandHandlers.push({\n commands: [],\n handler: commandOrHandler,\n });\n this.logger.debug(\"Registered slash command handler for all commands\");\n } else if (handler) {\n const commands = Array.isArray(commandOrHandler)\n ? commandOrHandler\n : [commandOrHandler];\n const normalizedCommands = commands.map((cmd) =>\n cmd.startsWith(\"/\") ? cmd : `/${cmd}`\n );\n this.slashCommandHandlers.push({ commands: normalizedCommands, handler });\n this.logger.debug(\"Registered slash command handler\", {\n commands: normalizedCommands,\n });\n }\n }\n\n onAssistantThreadStarted(handler: AssistantThreadStartedHandler): void {\n this.assistantThreadStartedHandlers.push(handler);\n this.logger.debug(\"Registered assistant thread started handler\");\n }\n\n onAssistantContextChanged(handler: AssistantContextChangedHandler): void {\n this.assistantContextChangedHandlers.push(handler);\n this.logger.debug(\"Registered assistant context changed handler\");\n }\n\n onAppHomeOpened(handler: AppHomeOpenedHandler): void {\n this.appHomeOpenedHandlers.push(handler);\n this.logger.debug(\"Registered app home opened handler\");\n }\n\n onMemberJoinedChannel(handler: MemberJoinedChannelHandler): void {\n this.memberJoinedChannelHandlers.push(handler);\n this.logger.debug(\"Registered member joined channel handler\");\n }\n\n /**\n * Get an adapter by name with type safety.\n */\n getAdapter<K extends keyof TAdapters>(name: K): TAdapters[K] {\n return this.adapters.get(name as string) as TAdapters[K];\n }\n\n /**\n * Get a JSON.parse reviver function that automatically deserializes\n * chat:Thread and chat:Message objects.\n *\n * Use this when parsing JSON that contains serialized Thread or Message objects\n * (e.g., from workflow engine payloads).\n *\n * @returns A reviver function for JSON.parse\n *\n * @example\n * ```typescript\n * // Parse workflow payload with automatic deserialization\n * const data = JSON.parse(payload, chat.reviver());\n *\n * // data.thread is now a ThreadImpl instance\n * // data.message is now a Message object with Date fields restored\n * await data.thread.post(\"Hello from workflow!\");\n * ```\n */\n reviver(): (key: string, value: unknown) => unknown {\n // Ensure this chat instance is registered as singleton for thread deserialization\n this.registerSingleton();\n return function reviver(_key: string, value: unknown): unknown {\n if (value && typeof value === \"object\" && \"_type\" in value) {\n const typed = value as { _type: string };\n if (typed._type === \"chat:Thread\") {\n return ThreadImpl.fromJSON(value as SerializedThread);\n }\n if (typed._type === \"chat:Channel\") {\n return ChannelImpl.fromJSON(value as SerializedChannel);\n }\n if (typed._type === \"chat:Message\") {\n return Message.fromJSON(value as SerializedMessage);\n }\n }\n return value;\n };\n }\n\n // ChatInstance interface implementations\n\n /**\n * Process an incoming message from an adapter.\n * Handles waitUntil registration and error catching internally.\n * Adapters should call this instead of handleIncomingMessage directly.\n */\n processMessage(\n adapter: Adapter,\n threadId: string,\n messageOrFactory: Message | (() => Promise<Message>),\n options?: WebhookOptions\n ): void {\n const task = (async () => {\n const message =\n typeof messageOrFactory === \"function\"\n ? await messageOrFactory()\n : messageOrFactory;\n await this.handleIncomingMessage(adapter, threadId, message);\n })().catch((err) => {\n this.logger.error(\"Message processing error\", { error: err, threadId });\n });\n\n if (options?.waitUntil) {\n options.waitUntil(task);\n }\n }\n\n /**\n * Process an incoming reaction event from an adapter.\n * Handles waitUntil registration and error catching internally.\n */\n processReaction(\n event: Omit<ReactionEvent, \"adapter\" | \"thread\"> & { adapter?: Adapter },\n options?: WebhookOptions\n ): void {\n const task = this.handleReactionEvent(event).catch((err) => {\n this.logger.error(\"Reaction processing error\", {\n error: err,\n emoji: event.emoji,\n messageId: event.messageId,\n });\n });\n\n if (options?.waitUntil) {\n options.waitUntil(task);\n }\n }\n\n /**\n * Process an incoming action event (button click) from an adapter.\n * Handles waitUntil registration and error catching internally.\n */\n processAction(\n event: Omit<ActionEvent, \"thread\" | \"openModal\"> & { adapter: Adapter },\n options?: WebhookOptions\n ): void {\n const task = this.handleActionEvent(event).catch((err) => {\n this.logger.error(\"Action processing error\", {\n error: err,\n actionId: event.actionId,\n messageId: event.messageId,\n });\n });\n\n if (options?.waitUntil) {\n options.waitUntil(task);\n }\n }\n\n async processModalSubmit(\n event: Omit<\n ModalSubmitEvent,\n \"relatedThread\" | \"relatedMessage\" | \"relatedChannel\"\n >,\n contextId?: string,\n _options?: WebhookOptions\n ): Promise<ModalResponse | undefined> {\n const { relatedThread, relatedMessage, relatedChannel } =\n await this.retrieveModalContext(event.adapter.name, contextId);\n\n const fullEvent: ModalSubmitEvent = {\n ...event,\n relatedThread,\n relatedMessage,\n relatedChannel,\n };\n\n for (const { callbackIds, handler } of this.modalSubmitHandlers) {\n if (callbackIds.length === 0 || callbackIds.includes(event.callbackId)) {\n try {\n const response = await handler(fullEvent);\n if (response) {\n return response;\n }\n } catch (err) {\n this.logger.error(\"Modal submit handler error\", {\n error: err,\n callbackId: event.callbackId,\n });\n }\n }\n }\n }\n\n processModalClose(\n event: Omit<\n ModalCloseEvent,\n \"relatedThread\" | \"relatedMessage\" | \"relatedChannel\"\n >,\n contextId?: string,\n options?: WebhookOptions\n ): void {\n const task = (async () => {\n const { relatedThread, relatedMessage, relatedChannel } =\n await this.retrieveModalContext(event.adapter.name, contextId);\n\n const fullEvent: ModalCloseEvent = {\n ...event,\n relatedThread,\n relatedMessage,\n relatedChannel,\n };\n\n for (const { callbackIds, handler } of this.modalCloseHandlers) {\n if (\n callbackIds.length === 0 ||\n callbackIds.includes(event.callbackId)\n ) {\n await handler(fullEvent);\n }\n }\n })().catch((err) => {\n this.logger.error(\"Modal close handler error\", {\n error: err,\n callbackId: event.callbackId,\n });\n });\n\n if (options?.waitUntil) {\n options.waitUntil(task);\n }\n }\n\n /**\n * Process an incoming slash command from an adapter.\n * Handles waitUntil registration and error catching internally.\n */\n processSlashCommand(\n event: Omit<SlashCommandEvent, \"channel\" | \"openModal\"> & {\n adapter: Adapter;\n channelId: string;\n },\n options?: WebhookOptions\n ): void {\n const task = this.handleSlashCommandEvent(event).catch((err) => {\n this.logger.error(\"Slash command processing error\", {\n error: err,\n command: event.command,\n text: event.text,\n });\n });\n\n if (options?.waitUntil) {\n options.waitUntil(task);\n }\n }\n\n processAssistantThreadStarted(\n event: AssistantThreadStartedEvent,\n options?: WebhookOptions\n ): void {\n const task = (async () => {\n for (const handler of this.assistantThreadStartedHandlers) {\n await handler(event);\n }\n })().catch((err) => {\n this.logger.error(\"Assistant thread started handler error\", {\n error: err,\n threadId: event.threadId,\n });\n });\n\n if (options?.waitUntil) {\n options.waitUntil(task);\n }\n }\n\n processAssistantContextChanged(\n event: AssistantContextChangedEvent,\n options?: WebhookOptions\n ): void {\n const task = (async () => {\n for (const handler of this.assistantContextChangedHandlers) {\n await handler(event);\n }\n })().catch((err) => {\n this.logger.error(\"Assistant context changed handler error\", {\n error: err,\n threadId: event.threadId,\n });\n });\n\n if (options?.waitUntil) {\n options.waitUntil(task);\n }\n }\n\n processAppHomeOpened(\n event: AppHomeOpenedEvent,\n options?: WebhookOptions\n ): void {\n const task = (async () => {\n for (const handler of this.appHomeOpenedHandlers) {\n await handler(event);\n }\n })().catch((err) => {\n this.logger.error(\"App home opened handler error\", {\n error: err,\n userId: event.userId,\n });\n });\n\n if (options?.waitUntil) {\n options.waitUntil(task);\n }\n }\n\n processMemberJoinedChannel(\n event: MemberJoinedChannelEvent,\n options?: WebhookOptions\n ): void {\n const task = (async () => {\n for (const handler of this.memberJoinedChannelHandlers) {\n await handler(event);\n }\n })().catch((err) => {\n this.logger.error(\"Member joined channel handler error\", {\n error: err,\n channelId: event.channelId,\n userId: event.userId,\n });\n });\n\n if (options?.waitUntil) {\n options.waitUntil(task);\n }\n }\n\n /**\n * Handle a slash command event internally.\n */\n private async handleSlashCommandEvent(\n event: Omit<SlashCommandEvent, \"channel\" | \"openModal\"> & {\n adapter: Adapter;\n channelId: string;\n }\n ): Promise<void> {\n this.logger.debug(\"Incoming slash command\", {\n adapter: event.adapter.name,\n command: event.command,\n text: event.text,\n user: event.user.userName,\n });\n if (event.user.isMe) {\n this.logger.debug(\"Skipping slash command from self\", {\n command: event.command,\n });\n return;\n }\n const channel = new ChannelImpl<TState>({\n id: event.channelId,\n adapter: event.adapter,\n stateAdapter: this._stateAdapter,\n });\n const fullEvent: SlashCommandEvent<TState> = {\n ...event,\n channel,\n openModal: async (modal) => {\n if (!event.triggerId) {\n this.logger.warn(\"Cannot open modal: no triggerId available\");\n return undefined;\n }\n if (!event.adapter.openModal) {\n this.logger.warn(\n `Cannot open modal: ${event.adapter.name} does not support modals`\n );\n return undefined;\n }\n let modalElement: ModalElement = modal as ModalElement;\n if (isJSX(modal)) {\n const converted = toModalElement(modal);\n if (!converted) {\n throw new Error(\"Invalid JSX element: must be a Modal element\");\n }\n modalElement = converted;\n }\n const contextId = crypto.randomUUID();\n this.storeModalContext(\n event.adapter.name,\n contextId,\n undefined,\n undefined,\n channel\n );\n return event.adapter.openModal(\n event.triggerId,\n modalElement,\n contextId\n );\n },\n };\n this.logger.debug(\"Checking slash command handlers\", {\n handlerCount: this.slashCommandHandlers.length,\n command: event.command,\n });\n for (const { commands, handler } of this.slashCommandHandlers) {\n if (commands.length === 0) {\n this.logger.debug(\"Running catch-all slash command handler\");\n await handler(fullEvent);\n continue;\n }\n if (commands.includes(event.command)) {\n this.logger.debug(\"Running matched slash command handler\", {\n command: event.command,\n });\n await handler(fullEvent);\n }\n }\n }\n\n /**\n * Store modal context server-side with a context ID.\n * Called when opening a modal to preserve thread/message/channel for the submit handler.\n */\n private storeModalContext(\n adapterName: string,\n contextId: string,\n thread?: ThreadImpl<TState>,\n message?: Message,\n channel?: ChannelImpl<TState>\n ): void {\n const key = `modal-context:${adapterName}:${contextId}`;\n const context: StoredModalContext = {\n thread: thread?.toJSON(),\n message: message?.toJSON(),\n channel: channel?.toJSON(),\n };\n this._stateAdapter.set(key, context, MODAL_CONTEXT_TTL_MS).catch((err) => {\n this.logger.error(\"Failed to store modal context\", {\n contextId,\n error: err,\n });\n });\n }\n\n /**\n * Retrieve and delete modal context from server-side storage.\n * Called when processing modal submit/close to reconstruct thread/message/channel.\n */\n private async retrieveModalContext(\n adapterName: string,\n contextId?: string\n ): Promise<{\n relatedThread: Thread | undefined;\n relatedMessage: SentMessage | undefined;\n relatedChannel: Channel | undefined;\n }> {\n if (!contextId) {\n return {\n relatedThread: undefined,\n relatedMessage: undefined,\n relatedChannel: undefined,\n };\n }\n\n const key = `modal-context:${adapterName}:${contextId}`;\n const stored = await this._stateAdapter.get<StoredModalContext>(key);\n\n if (!stored) {\n return {\n relatedThread: undefined,\n relatedMessage: undefined,\n relatedChannel: undefined,\n };\n }\n\n const adapter = this.adapters.get(adapterName);\n\n // Reconstruct thread with adapter directly (if present)\n let relatedThread: Thread | undefined;\n if (stored.thread) {\n relatedThread = ThreadImpl.fromJSON(stored.thread, adapter) as Thread;\n }\n\n // Reconstruct message if present\n let relatedMessage: SentMessage | undefined;\n if (stored.message && relatedThread) {\n const message = Message.fromJSON(stored.message);\n relatedMessage = (\n relatedThread as ThreadImpl<TState>\n ).createSentMessageFromMessage(message);\n }\n\n // Reconstruct channel if present\n let relatedChannel: Channel | undefined;\n if (stored.channel) {\n relatedChannel = ChannelImpl.fromJSON(stored.channel, adapter) as Channel;\n }\n\n return { relatedThread, relatedMessage, relatedChannel };\n }\n\n /**\n * Handle an action event internally.\n */\n private async handleActionEvent(\n event: Omit<ActionEvent, \"thread\" | \"openModal\"> & { adapter: Adapter }\n ): Promise<void> {\n this.logger.debug(\"Incoming action\", {\n adapter: event.adapter.name,\n actionId: event.actionId,\n value: event.value,\n user: event.user.userName,\n messageId: event.messageId,\n threadId: event.threadId,\n });\n\n // Skip actions from self (shouldn't happen, but be safe)\n if (event.user.isMe) {\n this.logger.debug(\"Skipping action from self\", {\n actionId: event.actionId,\n });\n return;\n }\n\n const isSubscribed = false;\n const messageForThread = event.messageId\n ? new Message({\n id: event.messageId,\n threadId: event.threadId,\n text: \"\",\n formatted: { type: \"root\", children: [] },\n raw: event.raw,\n author: event.user,\n metadata: { dateSent: new Date(), edited: false },\n attachments: [],\n })\n : ({} as Message);\n\n // Create thread for the action event (skip for view-based actions with no threadId)\n const thread = event.threadId\n ? await this.createThread(\n event.adapter,\n event.threadId,\n messageForThread,\n isSubscribed\n )\n : null;\n\n // Build full event with thread and openModal helper\n const fullEvent: ActionEvent = {\n ...event,\n thread,\n openModal: async (modal) => {\n if (!event.triggerId) {\n this.logger.warn(\"Cannot open modal: no triggerId available\");\n return undefined;\n }\n if (!event.adapter.openModal) {\n this.logger.warn(\n `Cannot open modal: ${event.adapter.name} does not support modals`\n );\n return undefined;\n }\n\n // Convert JSX to ModalElement if needed (same pattern as thread.post)\n let modalElement: ModalElement = modal as ModalElement;\n if (isJSX(modal)) {\n const converted = toModalElement(modal);\n if (!converted) {\n throw new Error(\"Invalid JSX element: must be a Modal element\");\n }\n modalElement = converted;\n }\n\n // Store context server-side and pass contextId to adapter\n let message: Message | undefined;\n if (thread) {\n const isEphemeralMessage = event.messageId?.startsWith(\"ephemeral:\");\n if (isEphemeralMessage) {\n const recentMessage = thread.recentMessages[0];\n if (recentMessage && typeof recentMessage.toJSON === \"function\") {\n message = recentMessage as Message;\n }\n } else if (event.messageId && event.adapter.fetchMessage) {\n const fetched = await event.adapter\n .fetchMessage(event.threadId, event.messageId)\n .catch(() => null);\n if (fetched) {\n message = new Message(fetched);\n } else {\n const recentMessage = thread.recentMessages[0];\n if (recentMessage && typeof recentMessage.toJSON === \"function\") {\n message = recentMessage as Message;\n }\n }\n }\n }\n const contextId = crypto.randomUUID();\n const channel = thread\n ? ((thread as ThreadImpl<TState>).channel as ChannelImpl<TState>)\n : undefined;\n this.storeModalContext(\n event.adapter.name,\n contextId,\n thread ? (thread as ThreadImpl<TState>) : undefined,\n message,\n channel\n );\n return event.adapter.openModal(\n event.triggerId,\n modalElement,\n contextId\n );\n },\n };\n\n // Run matching handlers\n this.logger.debug(\"Checking action handlers\", {\n handlerCount: this.actionHandlers.length,\n actionId: event.actionId,\n });\n\n for (const { actionIds, handler } of this.actionHandlers) {\n // If no action ID filter, run handler for all actions\n if (actionIds.length === 0) {\n this.logger.debug(\"Running catch-all action handler\");\n await handler(fullEvent);\n continue;\n }\n\n // Check if the action matches any of the specified action IDs\n if (actionIds.includes(event.actionId)) {\n this.logger.debug(\"Running matched action handler\", {\n actionId: event.actionId,\n });\n await handler(fullEvent);\n }\n }\n }\n\n /**\n * Handle a reaction event internally.\n */\n private async handleReactionEvent(\n event: Omit<ReactionEvent, \"adapter\" | \"thread\"> & { adapter?: Adapter }\n ): Promise<void> {\n this.logger.debug(\"Incoming reaction\", {\n adapter: event.adapter?.name,\n emoji: event.emoji,\n rawEmoji: event.rawEmoji,\n added: event.added,\n user: event.user.userName,\n messageId: event.messageId,\n threadId: event.threadId,\n });\n\n // Skip reactions from self\n if (event.user.isMe) {\n this.logger.debug(\"Skipping reaction from self\", {\n emoji: event.emoji,\n });\n return;\n }\n\n // Adapter is required for thread creation\n if (!event.adapter) {\n this.logger.error(\"Reaction event missing adapter\");\n return;\n }\n\n // Create thread for the reaction event\n const isSubscribed = await this._stateAdapter.isSubscribed(event.threadId);\n const thread = await this.createThread(\n event.adapter,\n event.threadId,\n event.message ?? ({} as Message),\n isSubscribed\n );\n\n // Build full event with thread and adapter\n const fullEvent: ReactionEvent = {\n ...event,\n adapter: event.adapter,\n thread,\n };\n\n // Run matching handlers\n this.logger.debug(\"Checking reaction handlers\", {\n handlerCount: this.reactionHandlers.length,\n emoji: event.emoji.name,\n rawEmoji: event.rawEmoji,\n });\n\n for (const { emoji: emojiFilter, handler } of this.reactionHandlers) {\n // If no emoji filter, run handler for all reactions\n if (emojiFilter.length === 0) {\n this.logger.debug(\"Running catch-all reaction handler\");\n await handler(fullEvent);\n continue;\n }\n\n // Check if the reaction matches any of the specified emoji\n const matches = emojiFilter.some((filter) => {\n // EmojiValue object identity comparison (recommended)\n if (filter === fullEvent.emoji) {\n return true;\n }\n\n // String comparison: check against emoji name or rawEmoji\n const filterName = typeof filter === \"string\" ? filter : filter.name;\n return (\n filterName === fullEvent.emoji.name ||\n filterName === fullEvent.rawEmoji\n );\n });\n\n this.logger.debug(\"Reaction filter check\", {\n filterEmoji: emojiFilter.map((e) =>\n typeof e === \"string\" ? e : e.name\n ),\n eventEmoji: fullEvent.emoji.name,\n matches,\n });\n\n if (matches) {\n this.logger.debug(\"Running matched reaction handler\");\n await handler(fullEvent);\n }\n }\n }\n\n getState(): StateAdapter {\n return this._stateAdapter;\n }\n\n getUserName(): string {\n return this.userName;\n }\n\n getLogger(prefix?: string): Logger {\n if (prefix) {\n return this.logger.child(prefix);\n }\n return this.logger;\n }\n\n /**\n * Open a direct message conversation with a user.\n *\n * Accepts either a user ID string or an Author object (from message.author or event.user).\n *\n * The adapter is automatically inferred from the userId format:\n * - Slack: `U...` (e.g., \"U00FAKEUSER1\")\n * - Teams: `29:...` (e.g., \"29:198PbJuw...\")\n * - Google Chat: `users/...` (e.g., \"users/100000000000000000001\")\n * - Discord: numeric snowflake (e.g., \"1033044521375764530\")\n *\n * @param user - Platform-specific user ID string, or an Author object\n * @returns A Thread that can be used to post messages\n *\n * @example\n * ```ts\n * // Using user ID directly\n * const dmThread = await chat.openDM(\"U123456\");\n * await dmThread.post(\"Hello via DM!\");\n *\n * // Using Author object from a message\n * chat.onSubscribedMessage(async (thread, message) => {\n * const dmThread = await chat.openDM(message.author);\n * await dmThread.post(\"Hello via DM!\");\n * });\n * ```\n */\n async openDM(user: string | Author): Promise<Thread<TState>> {\n const userId = typeof user === \"string\" ? user : user.userId;\n const adapter = this.inferAdapterFromUserId(userId);\n if (!adapter.openDM) {\n throw new ChatError(\n `Adapter \"${adapter.name}\" does not support openDM`,\n \"NOT_SUPPORTED\"\n );\n }\n\n const threadId = await adapter.openDM(userId);\n return this.createThread(adapter, threadId, {} as Message, false);\n }\n\n /**\n * Get a Channel by its channel ID.\n *\n * The adapter is automatically inferred from the channel ID prefix.\n *\n * @param channelId - Channel ID (e.g., \"slack:C123ABC\", \"gchat:spaces/ABC123\")\n * @returns A Channel that can be used to list threads, post messages, iterate messages, etc.\n *\n * @example\n * ```typescript\n * const channel = chat.channel(\"slack:C123ABC\");\n *\n * // Iterate messages newest first\n * for await (const msg of channel.messages) {\n * console.log(msg.text);\n * }\n *\n * // List threads\n * for await (const t of channel.threads()) {\n * console.log(t.rootMessage.text, t.replyCount);\n * }\n *\n * // Post to channel\n * await channel.post(\"Hello channel!\");\n * ```\n */\n channel(channelId: string): Channel<TState> {\n const adapterName = channelId.split(\":\")[0];\n if (!adapterName) {\n throw new ChatError(\n `Invalid channel ID: ${channelId}`,\n \"INVALID_CHANNEL_ID\"\n );\n }\n\n const adapter = this.adapters.get(adapterName);\n if (!adapter) {\n throw new ChatError(\n `Adapter \"${adapterName}\" not found for channel ID \"${channelId}\"`,\n \"ADAPTER_NOT_FOUND\"\n );\n }\n\n return new ChannelImpl<TState>({\n id: channelId,\n adapter,\n stateAdapter: this._stateAdapter,\n });\n }\n\n /**\n * Infer which adapter to use based on the userId format.\n */\n private inferAdapterFromUserId(userId: string): Adapter {\n // Google Chat: users/123456789\n if (userId.startsWith(\"users/\")) {\n const adapter = this.adapters.get(\"gchat\");\n if (adapter) {\n return adapter;\n }\n }\n\n // Teams: 29:base64string...\n if (userId.startsWith(\"29:\")) {\n const adapter = this.adapters.get(\"teams\");\n if (adapter) {\n return adapter;\n }\n }\n\n // Slack: U followed by alphanumeric (e.g., U00FAKEUSER1)\n if (SLACK_USER_ID_REGEX.test(userId)) {\n const adapter = this.adapters.get(\"slack\");\n if (adapter) {\n return adapter;\n }\n }\n\n // Discord: snowflake ID (17-19 digit number)\n if (DISCORD_SNOWFLAKE_REGEX.test(userId)) {\n const adapter = this.adapters.get(\"discord\");\n if (adapter) {\n return adapter;\n }\n }\n\n throw new ChatError(\n `Cannot infer adapter from userId \"${userId}\". Expected format: Slack (U...), Teams (29:...), Google Chat (users/...), or Discord (numeric snowflake).`,\n \"UNKNOWN_USER_ID_FORMAT\"\n );\n }\n\n /**\n * Handle an incoming message from an adapter.\n * This is called by adapters when they receive a webhook.\n *\n * The Chat class handles common concerns centrally:\n * - Deduplication: Same message may arrive multiple times (e.g., Slack sends\n * both `message` and `app_mention` events, GChat sends direct webhook + Pub/Sub)\n * - Bot filtering: Messages from the bot itself are skipped\n * - Locking: Only one instance processes a thread at a time\n */\n async handleIncomingMessage(\n adapter: Adapter,\n threadId: string,\n message: Message\n ): Promise<void> {\n this.logger.debug(\"Incoming message\", {\n adapter: adapter.name,\n threadId,\n messageId: message.id,\n text: message.text,\n author: message.author.userName,\n authorUserId: message.author.userId,\n isBot: message.author.isBot,\n isMe: message.author.isMe,\n });\n\n // Skip messages from self (bot's own messages)\n if (message.author.isMe) {\n this.logger.debug(\"Skipping message from self (isMe=true)\", {\n adapter: adapter.name,\n threadId,\n author: message.author.userName,\n });\n return;\n }\n\n // Deduplicate messages atomically - same message can arrive via multiple paths\n // (e.g., Slack message + app_mention events, GChat direct webhook + Pub/Sub)\n const dedupeKey = `dedupe:${adapter.name}:${message.id}`;\n const isFirstProcess = await this._stateAdapter.setIfNotExists(\n dedupeKey,\n true,\n this._dedupeTtlMs\n );\n if (!isFirstProcess) {\n this.logger.debug(\"Skipping duplicate message\", {\n adapter: adapter.name,\n messageId: message.id,\n });\n return;\n }\n\n // Try to acquire lock on thread\n const lock = await this._stateAdapter.acquireLock(\n threadId,\n DEFAULT_LOCK_TTL_MS\n );\n if (!lock) {\n this.logger.warn(\"Could not acquire lock on thread\", { threadId });\n throw new LockError(\n `Could not acquire lock on thread ${threadId}. Another instance may be processing.`\n );\n }\n\n this.logger.debug(\"Lock acquired\", { threadId, token: lock.token });\n\n try {\n // Set isMention on the message for handler access\n // Preserve existing isMention if already set (e.g., from Gateway detection)\n message.isMention =\n message.isMention || this.detectMention(adapter, message);\n\n // Check if this is a subscribed thread first\n const isSubscribed = await this._stateAdapter.isSubscribed(threadId);\n this.logger.debug(\"Subscription check\", {\n threadId,\n isSubscribed,\n subscribedHandlerCount: this.subscribedMessageHandlers.length,\n });\n\n // Create thread object (with subscription context for optimization)\n const thread = await this.createThread(\n adapter,\n threadId,\n message,\n isSubscribed\n );\n\n if (isSubscribed) {\n this.logger.debug(\"Message in subscribed thread - calling handlers\", {\n threadId,\n handlerCount: this.subscribedMessageHandlers.length,\n });\n await this.runHandlers(this.subscribedMessageHandlers, thread, message);\n return;\n }\n\n // Check for @-mention of bot\n if (message.isMention) {\n this.logger.debug(\"Bot mentioned\", {\n threadId,\n text: message.text.slice(0, 100),\n });\n await this.runHandlers(this.mentionHandlers, thread, message);\n return;\n }\n\n // Check message patterns\n this.logger.debug(\"Checking message patterns\", {\n patternCount: this.messagePatterns.length,\n patterns: this.messagePatterns.map((p) => p.pattern.toString()),\n messageText: message.text,\n });\n let matchedPattern = false;\n for (const { pattern, handler } of this.messagePatterns) {\n const matches = pattern.test(message.text);\n this.logger.debug(\"Pattern test\", {\n pattern: pattern.toString(),\n text: message.text,\n matches,\n });\n if (matches) {\n this.logger.debug(\"Message matched pattern - calling handler\", {\n pattern: pattern.toString(),\n });\n matchedPattern = true;\n await handler(thread, message);\n }\n }\n\n // Log if no handlers matched\n if (!matchedPattern) {\n this.logger.debug(\"No handlers matched message\", {\n threadId,\n text: message.text.slice(0, 100),\n });\n }\n } finally {\n await this._stateAdapter.releaseLock(lock);\n this.logger.debug(\"Lock released\", { threadId });\n }\n }\n\n private createThread(\n adapter: Adapter,\n threadId: string,\n initialMessage: Message,\n isSubscribedContext = false\n ): Thread<TState> {\n // Parse thread ID to get channel info\n // Format: \"adapter:channel:thread\"\n const parts = threadId.split(\":\");\n const channelId = parts[1] || \"\";\n\n // Check if this is a DM\n const isDM = adapter.isDM?.(threadId) ?? false;\n\n return new ThreadImpl<TState>({\n id: threadId,\n adapter,\n channelId,\n stateAdapter: this._stateAdapter,\n initialMessage,\n isSubscribedContext,\n isDM,\n currentMessage: initialMessage,\n streamingUpdateIntervalMs: this._streamingUpdateIntervalMs,\n fallbackStreamingPlaceholderText: this._fallbackStreamingPlaceholderText,\n });\n }\n\n /**\n * Detect if the bot was mentioned in the message.\n * All adapters normalize mentions to @name format, so we just check for @username.\n */\n private detectMention(adapter: Adapter, message: Message): boolean {\n const botUserName = adapter.userName || this.userName;\n const botUserId = adapter.botUserId;\n\n // Primary check: @username format (normalized by all adapters)\n const usernamePattern = new RegExp(\n `@${this.escapeRegex(botUserName)}\\\\b`,\n \"i\"\n );\n if (usernamePattern.test(message.text)) {\n return true;\n }\n\n // Fallback: check for user ID mention if available (e.g., @U_BOT_123)\n if (botUserId) {\n const userIdPattern = new RegExp(\n `@${this.escapeRegex(botUserId)}\\\\b`,\n \"i\"\n );\n if (userIdPattern.test(message.text)) {\n return true;\n }\n\n // Discord format: <@USER_ID> or <@!USER_ID>\n const discordPattern = new RegExp(\n `<@!?${this.escapeRegex(botUserId)}>`,\n \"i\"\n );\n if (discordPattern.test(message.text)) {\n return true;\n }\n }\n\n return false;\n }\n\n private escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n }\n\n private async runHandlers(\n handlers: Array<\n (thread: Thread<TState>, message: Message) => void | Promise<void>\n >,\n thread: Thread<TState>,\n message: Message\n ): Promise<void> {\n for (const handler of handlers) {\n await handler(thread, message);\n }\n }\n}\n","import type {\n CustomEmojiMap,\n EmojiFormats,\n EmojiMapConfig,\n EmojiValue,\n WellKnownEmoji,\n} from \"./types\";\n\n// Re-export EmojiValue for convenience\nexport type { EmojiValue } from \"./types\";\n\n// =============================================================================\n// EmojiValue - Immutable singleton emoji objects with object identity\n// =============================================================================\n\n/** Internal emoji registry for singleton instances */\nconst emojiRegistry = new Map<string, EmojiValue>();\n\n/**\n * Get or create an immutable singleton EmojiValue.\n *\n * Always returns the same frozen object for the same name,\n * enabling `===` comparison for emoji identity.\n *\n * @example\n * ```typescript\n * const e1 = getEmoji(\"thumbs_up\");\n * const e2 = getEmoji(\"thumbs_up\");\n * console.log(e1 === e2); // true - same object\n * ```\n */\nexport function getEmoji(name: string): EmojiValue {\n let emojiValue = emojiRegistry.get(name);\n if (!emojiValue) {\n emojiValue = Object.freeze({\n name,\n toString: () => `{{emoji:${name}}}`,\n toJSON: () => `{{emoji:${name}}}`,\n });\n emojiRegistry.set(name, emojiValue);\n }\n return emojiValue;\n}\n\n// =============================================================================\n// Emoji Map - Platform-specific formats\n// =============================================================================\n\n/**\n * Default emoji map for well-known emoji.\n * Maps normalized emoji names to platform-specific formats.\n */\nexport const DEFAULT_EMOJI_MAP: Record<string, EmojiFormats> = {\n // Reactions & Gestures\n thumbs_up: { slack: [\"+1\", \"thumbsup\"], gchat: \"👍\" },\n thumbs_down: { slack: [\"-1\", \"thumbsdown\"], gchat: \"👎\" },\n clap: { slack: \"clap\", gchat: \"👏\" },\n wave: { slack: \"wave\", gchat: \"👋\" },\n pray: { slack: \"pray\", gchat: \"🙏\" },\n muscle: { slack: \"muscle\", gchat: \"💪\" },\n ok_hand: { slack: \"ok_hand\", gchat: \"👌\" },\n point_up: { slack: \"point_up\", gchat: \"👆\" },\n point_down: { slack: \"point_down\", gchat: \"👇\" },\n point_left: { slack: \"point_left\", gchat: \"👈\" },\n point_right: { slack: \"point_right\", gchat: \"👉\" },\n raised_hands: { slack: \"raised_hands\", gchat: \"🙌\" },\n shrug: { slack: \"shrug\", gchat: \"🤷\" },\n facepalm: { slack: \"facepalm\", gchat: \"🤦\" },\n\n // Emotions & Faces\n heart: { slack: \"heart\", gchat: [\"❤️\", \"❤\"] },\n smile: { slack: [\"smile\", \"slightly_smiling_face\"], gchat: \"😊\" },\n laugh: { slack: [\"laughing\", \"satisfied\", \"joy\"], gchat: [\"😂\", \"😆\"] },\n thinking: { slack: \"thinking_face\", gchat: \"🤔\" },\n sad: { slack: [\"cry\", \"sad\", \"white_frowning_face\"], gchat: \"😢\" },\n cry: { slack: \"sob\", gchat: \"😭\" },\n angry: { slack: \"angry\", gchat: \"😠\" },\n love_eyes: { slack: \"heart_eyes\", gchat: \"😍\" },\n cool: { slack: \"sunglasses\", gchat: \"😎\" },\n wink: { slack: \"wink\", gchat: \"😉\" },\n surprised: { slack: \"open_mouth\", gchat: \"😮\" },\n worried: { slack: \"worried\", gchat: \"😟\" },\n confused: { slack: \"confused\", gchat: \"😕\" },\n neutral: { slack: \"neutral_face\", gchat: \"😐\" },\n sleeping: { slack: \"sleeping\", gchat: \"😴\" },\n sick: { slack: \"nauseated_face\", gchat: \"🤢\" },\n mind_blown: { slack: \"exploding_head\", gchat: \"🤯\" },\n relieved: { slack: \"relieved\", gchat: \"😌\" },\n grimace: { slack: \"grimacing\", gchat: \"😬\" },\n rolling_eyes: { slack: \"rolling_eyes\", gchat: \"🙄\" },\n hug: { slack: \"hugging_face\", gchat: \"🤗\" },\n zany: { slack: \"zany_face\", gchat: \"🤪\" },\n\n // Status & Symbols\n check: {\n slack: [\"white_check_mark\", \"heavy_check_mark\"],\n gchat: [\"✅\", \"✔️\"],\n },\n x: { slack: [\"x\", \"heavy_multiplication_x\"], gchat: [\"❌\", \"✖️\"] },\n question: { slack: \"question\", gchat: [\"❓\", \"?\"] },\n exclamation: { slack: \"exclamation\", gchat: \"❗\" },\n warning: { slack: \"warning\", gchat: \"⚠️\" },\n stop: { slack: \"octagonal_sign\", gchat: \"🛑\" },\n info: { slack: \"information_source\", gchat: \"ℹ️\" },\n \"100\": { slack: \"100\", gchat: \"💯\" },\n fire: { slack: \"fire\", gchat: \"🔥\" },\n star: { slack: \"star\", gchat: \"⭐\" },\n sparkles: { slack: \"sparkles\", gchat: \"✨\" },\n lightning: { slack: \"zap\", gchat: \"⚡\" },\n boom: { slack: \"boom\", gchat: \"💥\" },\n eyes: { slack: \"eyes\", gchat: \"👀\" },\n\n // Status Indicators (colored circles)\n green_circle: { slack: \"large_green_circle\", gchat: \"🟢\" },\n yellow_circle: { slack: \"large_yellow_circle\", gchat: \"🟡\" },\n red_circle: { slack: \"red_circle\", gchat: \"🔴\" },\n blue_circle: { slack: \"large_blue_circle\", gchat: \"🔵\" },\n white_circle: { slack: \"white_circle\", gchat: \"⚪\" },\n black_circle: { slack: \"black_circle\", gchat: \"⚫\" },\n\n // Objects & Tools\n rocket: { slack: \"rocket\", gchat: \"🚀\" },\n party: { slack: [\"tada\", \"partying_face\"], gchat: [\"🎉\", \"🥳\"] },\n confetti: { slack: \"confetti_ball\", gchat: \"🎊\" },\n balloon: { slack: \"balloon\", gchat: \"🎈\" },\n gift: { slack: \"gift\", gchat: \"🎁\" },\n trophy: { slack: \"trophy\", gchat: \"🏆\" },\n medal: { slack: \"first_place_medal\", gchat: \"🥇\" },\n lightbulb: { slack: \"bulb\", gchat: \"💡\" },\n gear: { slack: \"gear\", gchat: \"⚙️\" },\n wrench: { slack: \"wrench\", gchat: \"🔧\" },\n hammer: { slack: \"hammer\", gchat: \"🔨\" },\n bug: { slack: \"bug\", gchat: \"🐛\" },\n link: { slack: \"link\", gchat: \"🔗\" },\n lock: { slack: \"lock\", gchat: \"🔒\" },\n unlock: { slack: \"unlock\", gchat: \"🔓\" },\n key: { slack: \"key\", gchat: \"🔑\" },\n pin: { slack: \"pushpin\", gchat: \"📌\" },\n memo: { slack: \"memo\", gchat: \"📝\" },\n clipboard: { slack: \"clipboard\", gchat: \"📋\" },\n calendar: { slack: \"calendar\", gchat: \"📅\" },\n clock: { slack: \"clock1\", gchat: \"🕐\" },\n hourglass: { slack: \"hourglass\", gchat: \"⏳\" },\n bell: { slack: \"bell\", gchat: \"🔔\" },\n megaphone: { slack: \"mega\", gchat: \"📢\" },\n speech_bubble: { slack: \"speech_balloon\", gchat: \"💬\" },\n email: { slack: \"email\", gchat: \"📧\" },\n inbox: { slack: \"inbox_tray\", gchat: \"📥\" },\n outbox: { slack: \"outbox_tray\", gchat: \"📤\" },\n package: { slack: \"package\", gchat: \"📦\" },\n folder: { slack: \"file_folder\", gchat: \"📁\" },\n file: { slack: \"page_facing_up\", gchat: \"📄\" },\n chart_up: { slack: \"chart_with_upwards_trend\", gchat: \"📈\" },\n chart_down: { slack: \"chart_with_downwards_trend\", gchat: \"📉\" },\n coffee: { slack: \"coffee\", gchat: \"☕\" },\n pizza: { slack: \"pizza\", gchat: \"🍕\" },\n beer: { slack: \"beer\", gchat: \"🍺\" },\n\n // Arrows & Directions\n arrow_up: { slack: \"arrow_up\", gchat: \"⬆️\" },\n arrow_down: { slack: \"arrow_down\", gchat: \"⬇️\" },\n arrow_left: { slack: \"arrow_left\", gchat: \"⬅️\" },\n arrow_right: { slack: \"arrow_right\", gchat: \"➡️\" },\n refresh: { slack: \"arrows_counterclockwise\", gchat: \"🔄\" },\n\n // Nature & Weather\n sun: { slack: \"sunny\", gchat: \"☀️\" },\n cloud: { slack: \"cloud\", gchat: \"☁️\" },\n rain: { slack: \"rain_cloud\", gchat: \"🌧️\" },\n snow: { slack: \"snowflake\", gchat: \"❄️\" },\n rainbow: { slack: \"rainbow\", gchat: \"🌈\" },\n};\n\n/**\n * Emoji resolver that handles conversion between platform formats and normalized names.\n */\nexport class EmojiResolver {\n private readonly emojiMap: Record<string, EmojiFormats>;\n private readonly slackToNormalized: Map<string, string>;\n private readonly gchatToNormalized: Map<string, string>;\n\n constructor(customMap?: EmojiMapConfig) {\n this.emojiMap = { ...DEFAULT_EMOJI_MAP, ...customMap };\n this.slackToNormalized = new Map();\n this.gchatToNormalized = new Map();\n this.buildReverseMaps();\n }\n\n private buildReverseMaps(): void {\n for (const [normalized, formats] of Object.entries(this.emojiMap)) {\n // Build Slack reverse map\n const slackFormats = Array.isArray(formats.slack)\n ? formats.slack\n : [formats.slack];\n for (const slack of slackFormats) {\n this.slackToNormalized.set(slack.toLowerCase(), normalized);\n }\n\n // Build GChat reverse map\n const gchatFormats = Array.isArray(formats.gchat)\n ? formats.gchat\n : [formats.gchat];\n for (const gchat of gchatFormats) {\n this.gchatToNormalized.set(gchat, normalized);\n }\n }\n }\n\n /**\n * Convert a Slack emoji name to normalized EmojiValue.\n * Returns an EmojiValue for the raw emoji if no mapping exists.\n */\n fromSlack(slackEmoji: string): EmojiValue {\n // Remove colons if present (e.g., \":+1:\" -> \"+1\")\n const cleaned = slackEmoji.replace(/^:|:$/g, \"\").toLowerCase();\n const normalized = this.slackToNormalized.get(cleaned) ?? slackEmoji;\n return getEmoji(normalized);\n }\n\n /**\n * Convert a Google Chat unicode emoji to normalized EmojiValue.\n * Returns an EmojiValue for the raw emoji if no mapping exists.\n */\n fromGChat(gchatEmoji: string): EmojiValue {\n const normalized = this.gchatToNormalized.get(gchatEmoji) ?? gchatEmoji;\n return getEmoji(normalized);\n }\n\n /**\n * Convert a Teams reaction type to normalized EmojiValue.\n * Teams uses specific names: like, heart, laugh, surprised, sad, angry\n * Returns an EmojiValue for the raw reaction if no mapping exists.\n */\n fromTeams(teamsReaction: string): EmojiValue {\n const teamsMap: Record<string, string> = {\n like: \"thumbs_up\",\n heart: \"heart\",\n laugh: \"laugh\",\n surprised: \"surprised\",\n sad: \"sad\",\n angry: \"angry\",\n };\n const normalized = teamsMap[teamsReaction] ?? teamsReaction;\n return getEmoji(normalized);\n }\n\n /**\n * Convert a normalized emoji (or EmojiValue) to Slack format.\n * Returns the first Slack format if multiple exist.\n */\n toSlack(emoji: EmojiValue | string): string {\n const name = typeof emoji === \"string\" ? emoji : emoji.name;\n const formats = this.emojiMap[name];\n if (!formats) {\n return name;\n }\n return Array.isArray(formats.slack) ? formats.slack[0] : formats.slack;\n }\n\n /**\n * Convert a normalized emoji (or EmojiValue) to Google Chat format.\n * Returns the first GChat format if multiple exist.\n */\n toGChat(emoji: EmojiValue | string): string {\n const name = typeof emoji === \"string\" ? emoji : emoji.name;\n const formats = this.emojiMap[name];\n if (!formats) {\n return name;\n }\n return Array.isArray(formats.gchat) ? formats.gchat[0] : formats.gchat;\n }\n\n /**\n * Convert a normalized emoji (or EmojiValue) to Discord format (unicode).\n * Discord uses unicode emoji, same as Google Chat.\n */\n toDiscord(emoji: EmojiValue | string): string {\n // Discord uses unicode emoji like GChat\n return this.toGChat(emoji);\n }\n\n /**\n * Check if an emoji (in any format) matches a normalized emoji name or EmojiValue.\n */\n matches(rawEmoji: string, normalized: EmojiValue | string): boolean {\n const name = typeof normalized === \"string\" ? normalized : normalized.name;\n const formats = this.emojiMap[name];\n if (!formats) {\n return rawEmoji === name;\n }\n\n const slackFormats = Array.isArray(formats.slack)\n ? formats.slack\n : [formats.slack];\n const gchatFormats = Array.isArray(formats.gchat)\n ? formats.gchat\n : [formats.gchat];\n\n const cleanedRaw = rawEmoji.replace(/^:|:$/g, \"\").toLowerCase();\n\n return (\n slackFormats.some((s) => s.toLowerCase() === cleanedRaw) ||\n gchatFormats.includes(rawEmoji)\n );\n }\n\n /**\n * Add or override emoji mappings.\n */\n extend(customMap: EmojiMapConfig): void {\n Object.assign(this.emojiMap, customMap);\n this.buildReverseMaps();\n }\n}\n\n/**\n * Default emoji resolver instance.\n */\nexport const defaultEmojiResolver = new EmojiResolver();\n\n/** Placeholder pattern for emoji in text: {{emoji:name}} */\nconst EMOJI_PLACEHOLDER_REGEX = /\\{\\{emoji:([a-z0-9_]+)\\}\\}/gi;\n\n/**\n * Convert emoji placeholders in text to platform-specific format.\n *\n * @example\n * ```typescript\n * convertEmojiPlaceholders(\"Thanks! {{emoji:thumbs_up}}\", \"slack\");\n * // Returns: \"Thanks! :+1:\"\n *\n * convertEmojiPlaceholders(\"Thanks! {{emoji:thumbs_up}}\", \"gchat\");\n * // Returns: \"Thanks! 👍\"\n * ```\n */\nexport function convertEmojiPlaceholders(\n text: string,\n platform: \"slack\" | \"gchat\" | \"teams\" | \"discord\" | \"github\" | \"linear\",\n resolver: EmojiResolver = defaultEmojiResolver\n): string {\n return text.replace(EMOJI_PLACEHOLDER_REGEX, (_, emojiName: string) => {\n switch (platform) {\n case \"slack\":\n return `:${resolver.toSlack(emojiName)}:`;\n case \"gchat\":\n return resolver.toGChat(emojiName);\n case \"teams\":\n // Teams uses unicode emoji\n return resolver.toGChat(emojiName);\n case \"discord\":\n // Discord uses unicode emoji\n return resolver.toDiscord(emojiName);\n case \"github\":\n // GitHub uses unicode emoji\n return resolver.toGChat(emojiName);\n case \"linear\":\n // Linear uses unicode emoji\n return resolver.toGChat(emojiName);\n default:\n return resolver.toGChat(emojiName);\n }\n });\n}\n\n// =============================================================================\n// Emoji Helper Types\n// =============================================================================\n\n/** Base emoji object with well-known emoji as EmojiValue singletons */\ntype BaseEmojiHelper = {\n [K in WellKnownEmoji]: EmojiValue;\n} & {\n /** Create an EmojiValue for a custom emoji name */\n custom: (name: string) => EmojiValue;\n};\n\n/** Extended emoji object including custom emoji from module augmentation */\ntype ExtendedEmojiHelper = BaseEmojiHelper & {\n [K in keyof CustomEmojiMap]: EmojiValue;\n};\n\n/**\n * Create a type-safe emoji helper with custom emoji.\n *\n * Returns immutable singleton EmojiValue objects that support:\n * - Object identity comparison (`event.emoji === emoji.thumbs_up`)\n * - Template string interpolation (`${emoji.thumbs_up}` → \"{{emoji:thumbs_up}}\")\n *\n * Custom emoji are automatically registered with the default resolver,\n * so placeholders will convert correctly in messages.\n *\n * @example\n * ```typescript\n * // First, extend the CustomEmojiMap type (usually in a .d.ts file)\n * declare module \"chat\" {\n * interface CustomEmojiMap {\n * unicorn: EmojiFormats;\n * company_logo: EmojiFormats;\n * }\n * }\n *\n * // Then create the emoji helper with your custom emoji\n * const emoji = createEmoji({\n * unicorn: { slack: \"unicorn_face\", gchat: \"🦄\" },\n * company_logo: { slack: \"company\", gchat: \"🏢\" },\n * });\n *\n * // Object identity works for comparisons\n * if (event.emoji === emoji.unicorn) { ... }\n *\n * // Template strings work for messages\n * await thread.post(`${emoji.unicorn} Magic!`);\n * // Slack: \":unicorn_face: Magic!\"\n * // GChat: \"🦄 Magic!\"\n * ```\n */\nexport function createEmoji<\n T extends Record<\n string,\n { slack: string | string[]; gchat: string | string[] }\n >,\n>(customEmoji?: T): BaseEmojiHelper & { [K in keyof T]: EmojiValue } {\n // All well-known emoji names\n const wellKnownEmoji: WellKnownEmoji[] = [\n // Reactions & Gestures\n \"thumbs_up\",\n \"thumbs_down\",\n \"clap\",\n \"wave\",\n \"pray\",\n \"muscle\",\n \"ok_hand\",\n \"point_up\",\n \"point_down\",\n \"point_left\",\n \"point_right\",\n \"raised_hands\",\n \"shrug\",\n \"facepalm\",\n // Emotions & Faces\n \"heart\",\n \"smile\",\n \"laugh\",\n \"thinking\",\n \"sad\",\n \"cry\",\n \"angry\",\n \"love_eyes\",\n \"cool\",\n \"wink\",\n \"surprised\",\n \"worried\",\n \"confused\",\n \"neutral\",\n \"sleeping\",\n \"sick\",\n \"mind_blown\",\n \"relieved\",\n \"grimace\",\n \"rolling_eyes\",\n \"hug\",\n \"zany\",\n // Status & Symbols\n \"check\",\n \"x\",\n \"question\",\n \"exclamation\",\n \"warning\",\n \"stop\",\n \"info\",\n \"100\",\n \"fire\",\n \"star\",\n \"sparkles\",\n \"lightning\",\n \"boom\",\n \"eyes\",\n // Status Indicators\n \"green_circle\",\n \"yellow_circle\",\n \"red_circle\",\n \"blue_circle\",\n \"white_circle\",\n \"black_circle\",\n // Objects & Tools\n \"rocket\",\n \"party\",\n \"confetti\",\n \"balloon\",\n \"gift\",\n \"trophy\",\n \"medal\",\n \"lightbulb\",\n \"gear\",\n \"wrench\",\n \"hammer\",\n \"bug\",\n \"link\",\n \"lock\",\n \"unlock\",\n \"key\",\n \"pin\",\n \"memo\",\n \"clipboard\",\n \"calendar\",\n \"clock\",\n \"hourglass\",\n \"bell\",\n \"megaphone\",\n \"speech_bubble\",\n \"email\",\n \"inbox\",\n \"outbox\",\n \"package\",\n \"folder\",\n \"file\",\n \"chart_up\",\n \"chart_down\",\n \"coffee\",\n \"pizza\",\n \"beer\",\n // Arrows & Directions\n \"arrow_up\",\n \"arrow_down\",\n \"arrow_left\",\n \"arrow_right\",\n \"refresh\",\n // Nature & Weather\n \"sun\",\n \"cloud\",\n \"rain\",\n \"snow\",\n \"rainbow\",\n ];\n\n // Build the emoji helper object with EmojiValue singletons\n const helper: Record<string, EmojiValue | ((name: string) => EmojiValue)> = {\n custom: (name: string): EmojiValue => getEmoji(name),\n };\n\n // Add all well-known emoji\n for (const name of wellKnownEmoji) {\n helper[name] = getEmoji(name);\n }\n\n // Add custom emoji if provided\n if (customEmoji) {\n for (const key of Object.keys(customEmoji)) {\n helper[key] = getEmoji(key);\n }\n // Extend the default resolver so placeholders convert correctly\n defaultEmojiResolver.extend(customEmoji as EmojiMapConfig);\n }\n\n return helper as BaseEmojiHelper & {\n [K in keyof T]: EmojiValue;\n };\n}\n\n/**\n * Type-safe emoji helper for embedding emoji in messages.\n *\n * @example\n * ```typescript\n * import { emoji } from \"chat\";\n *\n * await thread.post(`Great job! ${emoji.thumbs_up} ${emoji.fire}`);\n * // Slack: \"Great job! :+1: :fire:\"\n * // GChat: \"Great job! 👍 🔥\"\n * ```\n *\n * For custom emoji, use `createEmoji()` with module augmentation:\n * @example\n * ```typescript\n * // types.d.ts\n * declare module \"chat\" {\n * interface CustomEmojiMap {\n * unicorn: EmojiFormats;\n * }\n * }\n *\n * // bot.ts\n * const emoji = createEmoji({ unicorn: { slack: \"unicorn\", gchat: \"🦄\" } });\n * await thread.post(`${emoji.unicorn} Magic!`);\n * ```\n */\nexport const emoji: ExtendedEmojiHelper = createEmoji() as ExtendedEmojiHelper;\n","// Main exports\n\nexport {\n ChannelImpl,\n deriveChannelId,\n type SerializedChannel,\n} from \"./channel\";\nexport { Chat } from \"./chat\";\nexport { fromFullStream } from \"./from-full-stream\";\nexport {\n Message,\n type MessageData,\n type SerializedMessage,\n} from \"./message\";\nexport { StreamingMarkdownRenderer } from \"./streaming-markdown\";\nexport { type SerializedThread, ThreadImpl } from \"./thread\";\n\n// Card builders - import then re-export to ensure values are properly exported\nimport {\n Actions as _Actions,\n Button as _Button,\n Card as _Card,\n CardLink as _CardLink,\n CardText as _CardText,\n cardChildToFallbackText as _cardChildToFallbackText,\n Divider as _Divider,\n Field as _Field,\n Fields as _Fields,\n fromReactElement as _fromReactElement,\n Image as _Image,\n isCardElement as _isCardElement,\n LinkButton as _LinkButton,\n Section as _Section,\n Table as _Table,\n} from \"./cards\";\nimport type {\n ActionsComponent,\n ButtonComponent,\n CardComponent,\n CardLinkComponent,\n DividerComponent,\n FieldComponent,\n FieldsComponent,\n ImageComponent,\n LinkButtonComponent,\n ModalComponent,\n RadioSelectComponent,\n SectionComponent,\n SelectComponent,\n SelectOptionComponent,\n TextComponent,\n TextInputComponent,\n} from \"./jsx-runtime\";\nimport {\n isJSX as _isJSX,\n toCardElement as _toCardElement,\n toModalElement as _toModalElement,\n} from \"./jsx-runtime\";\n\n// Cast to JSX-compatible overloaded types.\n// The `as unknown as` is safe — JSX never calls these directly; the jsx factory handles resolution.\nexport const Actions = _Actions as unknown as ActionsComponent;\nexport const Button = _Button as unknown as ButtonComponent;\nexport const Card = _Card as unknown as CardComponent;\nexport const cardChildToFallbackText = _cardChildToFallbackText;\nexport const CardLink = _CardLink as unknown as CardLinkComponent;\nexport const CardText = _CardText as unknown as TextComponent;\nexport const Divider = _Divider as unknown as DividerComponent;\nexport const Field = _Field as unknown as FieldComponent;\nexport const Fields = _Fields as unknown as FieldsComponent;\nexport const fromReactElement = _fromReactElement;\nexport const Image = _Image as unknown as ImageComponent;\nexport const isCardElement = _isCardElement;\nexport const isJSX = _isJSX;\nexport const LinkButton = _LinkButton as unknown as LinkButtonComponent;\nexport const Section = _Section as unknown as SectionComponent;\nexport const Table = _Table;\nexport const toCardElement = _toCardElement;\nexport const toModalElement = _toModalElement;\n\n// Modal builders\nimport {\n fromReactModalElement as _fromReactModalElement,\n isModalElement as _isModalElement,\n Modal as _Modal,\n RadioSelect as _RadioSelect,\n Select as _Select,\n SelectOption as _SelectOption,\n TextInput as _TextInput,\n} from \"./modals\";\nexport const fromReactModalElement = _fromReactModalElement;\nexport const isModalElement = _isModalElement;\nexport const Modal = _Modal as unknown as ModalComponent;\nexport const RadioSelect = _RadioSelect as unknown as RadioSelectComponent;\nexport const Select = _Select as unknown as SelectComponent;\nexport const SelectOption = _SelectOption as unknown as SelectOptionComponent;\nexport const TextInput = _TextInput as unknown as TextInputComponent;\n\n// Card types\nexport type {\n ActionsElement,\n ButtonElement,\n ButtonOptions,\n ButtonStyle,\n CardChild,\n CardElement,\n CardOptions,\n DividerElement,\n FieldElement,\n FieldsElement,\n ImageElement,\n LinkButtonElement,\n LinkButtonOptions,\n LinkElement,\n SectionElement,\n TableAlignment,\n TableElement,\n TableOptions,\n TextElement,\n TextStyle,\n} from \"./cards\";\n// Emoji utilities\nexport {\n convertEmojiPlaceholders,\n createEmoji,\n DEFAULT_EMOJI_MAP,\n defaultEmojiResolver,\n EmojiResolver,\n type EmojiValue,\n emoji,\n getEmoji,\n} from \"./emoji\";\n// JSX types\nexport type {\n ActionsComponent,\n ButtonComponent,\n ButtonProps,\n CardComponent,\n CardJSXElement,\n CardJSXProps,\n CardLinkComponent,\n CardLinkProps,\n CardProps,\n ChatElement,\n ContainerProps,\n DividerComponent,\n DividerProps,\n FieldComponent,\n FieldProps,\n FieldsComponent,\n ImageComponent,\n ImageProps,\n LinkButtonComponent,\n LinkButtonProps,\n ModalComponent,\n ModalProps,\n RadioSelectComponent,\n SectionComponent,\n SelectComponent,\n SelectOptionComponent,\n SelectOptionProps,\n SelectProps,\n TextComponent,\n TextInputComponent,\n TextInputProps,\n TextProps,\n} from \"./jsx-runtime\";\n// Re-export mdast types for adapters\nexport type {\n Blockquote,\n Code,\n Content,\n Delete,\n Emphasis,\n InlineCode,\n Link,\n List,\n ListItem,\n Paragraph,\n Root,\n Strong,\n Table as MdastTable,\n TableCell,\n TableRow,\n Text,\n} from \"./markdown\";\n// Markdown/AST utilities\nexport {\n // Format converter base class\n BaseFormatConverter,\n blockquote,\n codeBlock,\n emphasis,\n // Types\n type FormatConverter,\n // Type guards for mdast nodes\n getNodeChildren,\n getNodeValue,\n inlineCode,\n isBlockquoteNode,\n isCodeNode,\n isDeleteNode,\n isEmphasisNode,\n isInlineCodeNode,\n isLinkNode,\n isListItemNode,\n isListNode,\n isParagraphNode,\n isStrongNode,\n isTableCellNode,\n isTableNode,\n isTableRowNode,\n isTextNode,\n link,\n type MarkdownConverter,\n markdownToPlainText,\n paragraph,\n // Parsing and stringifying\n parseMarkdown,\n root,\n strikethrough,\n stringifyMarkdown,\n strong,\n tableElementToAscii,\n tableToAscii,\n // AST node builders\n text,\n toPlainText,\n walkAst,\n} from \"./markdown\";\n// Modal types\nexport type {\n ModalChild,\n ModalElement,\n ModalOptions,\n RadioSelectElement,\n RadioSelectOptions,\n SelectElement,\n SelectOptionElement,\n SelectOptions,\n TextInputElement,\n TextInputOptions,\n} from \"./modals\";\n// Types\nexport type {\n ActionEvent,\n ActionHandler,\n Adapter,\n AdapterPostableMessage,\n AppHomeOpenedEvent,\n AppHomeOpenedHandler,\n AssistantContextChangedEvent,\n AssistantContextChangedHandler,\n AssistantThreadStartedEvent,\n AssistantThreadStartedHandler,\n Attachment,\n Author,\n Channel,\n ChannelInfo,\n ChatConfig,\n ChatInstance,\n CustomEmojiMap,\n Emoji,\n EmojiFormats,\n EmojiMapConfig,\n EphemeralMessage,\n FetchDirection,\n FetchOptions,\n FetchResult,\n FileUpload,\n FormattedContent,\n ListThreadsOptions,\n ListThreadsResult,\n Lock,\n Logger,\n LogLevel,\n MarkdownTextChunk,\n MemberJoinedChannelEvent,\n MemberJoinedChannelHandler,\n MentionHandler,\n MessageHandler,\n MessageMetadata,\n ModalCloseEvent,\n ModalCloseHandler,\n ModalCloseResponse,\n ModalErrorsResponse,\n ModalPushResponse,\n ModalResponse,\n ModalSubmitEvent,\n ModalSubmitHandler,\n ModalUpdateResponse,\n PlanUpdateChunk,\n Postable,\n PostableAst,\n PostableCard,\n PostableMarkdown,\n PostableMessage,\n PostableRaw,\n PostEphemeralOptions,\n RawMessage,\n ReactionEvent,\n ReactionHandler,\n SentMessage,\n SlashCommandEvent,\n SlashCommandHandler,\n StateAdapter,\n StreamChunk,\n StreamEvent,\n StreamOptions,\n SubscribedMessageHandler,\n TaskUpdateChunk,\n Thread,\n ThreadInfo,\n ThreadSummary,\n WebhookOptions,\n WellKnownEmoji,\n} from \"./types\";\n// Errors and Logger\nexport {\n ChatError,\n ConsoleLogger,\n LockError,\n NotImplementedError,\n RateLimitError,\n THREAD_STATE_TTL_MS,\n} from \"./types\";\n"],"mappings":"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","names":["WORKFLOW_DESERIALIZE","WORKFLOW_SERIALIZE","WORKFLOW_SERIALIZE","WORKFLOW_DESERIALIZE","emoji","WORKFLOW_DESERIALIZE","WORKFLOW_SERIALIZE","text","isLazyConfig","isAsyncIterable","WORKFLOW_SERIALIZE","WORKFLOW_DESERIALIZE","extractMessageContent","postable","emoji","emoji","text","Actions","Button","Card","cardChildToFallbackText","CardLink","CardText","Divider","Field","Fields","fromReactElement","Image","isCardElement","isJSX","LinkButton","Section","Table","toCardElement","toModalElement","fromReactModalElement","isModalElement","Modal","RadioSelect","Select","SelectOption","TextInput"]}
|
|
1
|
+
{"version":3,"sources":["../src/channel.ts","../src/chat-singleton.ts","../src/message.ts","../src/errors.ts","../src/logger.ts","../src/types.ts","../src/thread.ts","../src/from-full-stream.ts","../src/streaming-markdown.ts","../src/chat.ts","../src/emoji.ts","../src/index.ts"],"sourcesContent":["import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from \"@workflow/serde\";\nimport { cardToFallbackText } from \"./cards\";\nimport { getChatSingleton } from \"./chat-singleton\";\nimport { type ChatElement, isJSX, toCardElement } from \"./jsx-runtime\";\nimport {\n paragraph,\n parseMarkdown,\n root,\n text as textNode,\n toPlainText,\n} from \"./markdown\";\nimport { Message } from \"./message\";\nimport type {\n Adapter,\n AdapterPostableMessage,\n Author,\n Channel,\n ChannelInfo,\n EphemeralMessage,\n PostableMessage,\n PostEphemeralOptions,\n ScheduledMessage,\n SentMessage,\n StateAdapter,\n ThreadSummary,\n} from \"./types\";\nimport { NotImplementedError, THREAD_STATE_TTL_MS } from \"./types\";\n\n/** State key prefix for channel state */\nconst CHANNEL_STATE_KEY_PREFIX = \"channel-state:\";\n\n/**\n * Serialized channel data for passing to external systems (e.g., workflow engines).\n */\nexport interface SerializedChannel {\n _type: \"chat:Channel\";\n adapterName: string;\n id: string;\n isDM: boolean;\n}\n\n/**\n * Config for creating a ChannelImpl with explicit adapter/state instances.\n */\ninterface ChannelImplConfigWithAdapter {\n adapter: Adapter;\n id: string;\n isDM?: boolean;\n stateAdapter: StateAdapter;\n}\n\n/**\n * Config for creating a ChannelImpl with lazy adapter resolution.\n */\ninterface ChannelImplConfigLazy {\n adapterName: string;\n id: string;\n isDM?: boolean;\n}\n\ntype ChannelImplConfig = ChannelImplConfigWithAdapter | ChannelImplConfigLazy;\n\nfunction isLazyConfig(\n config: ChannelImplConfig\n): config is ChannelImplConfigLazy {\n return \"adapterName\" in config && !(\"adapter\" in config);\n}\n\n/**\n * Check if a value is an AsyncIterable (like AI SDK's textStream).\n */\nfunction isAsyncIterable(value: unknown): value is AsyncIterable<string> {\n return (\n value !== null && typeof value === \"object\" && Symbol.asyncIterator in value\n );\n}\n\nexport class ChannelImpl<TState = Record<string, unknown>>\n implements Channel<TState>\n{\n readonly id: string;\n readonly isDM: boolean;\n\n private _adapter?: Adapter;\n private readonly _adapterName?: string;\n private _stateAdapterInstance?: StateAdapter;\n private _name: string | null = null;\n\n constructor(config: ChannelImplConfig) {\n this.id = config.id;\n this.isDM = config.isDM ?? false;\n\n if (isLazyConfig(config)) {\n this._adapterName = config.adapterName;\n } else {\n this._adapter = config.adapter;\n this._stateAdapterInstance = config.stateAdapter;\n }\n }\n\n get adapter(): Adapter {\n if (this._adapter) {\n return this._adapter;\n }\n\n if (!this._adapterName) {\n throw new Error(\"Channel has no adapter configured\");\n }\n\n const chat = getChatSingleton();\n const adapter = chat.getAdapter(this._adapterName);\n if (!adapter) {\n throw new Error(\n `Adapter \"${this._adapterName}\" not found in Chat singleton`\n );\n }\n\n this._adapter = adapter;\n return adapter;\n }\n\n private get _stateAdapter(): StateAdapter {\n if (this._stateAdapterInstance) {\n return this._stateAdapterInstance;\n }\n\n const chat = getChatSingleton();\n this._stateAdapterInstance = chat.getState();\n return this._stateAdapterInstance;\n }\n\n get name(): string | null {\n return this._name;\n }\n\n get state(): Promise<TState | null> {\n return this._stateAdapter.get<TState>(\n `${CHANNEL_STATE_KEY_PREFIX}${this.id}`\n );\n }\n\n async setState(\n newState: Partial<TState>,\n options?: { replace?: boolean }\n ): Promise<void> {\n const key = `${CHANNEL_STATE_KEY_PREFIX}${this.id}`;\n\n if (options?.replace) {\n await this._stateAdapter.set(key, newState, THREAD_STATE_TTL_MS);\n } else {\n const existing = await this._stateAdapter.get<TState>(key);\n const merged = { ...existing, ...newState };\n await this._stateAdapter.set(key, merged, THREAD_STATE_TTL_MS);\n }\n }\n\n /**\n * Iterate messages newest first (backward from most recent).\n * Uses adapter.fetchChannelMessages if available, otherwise falls back\n * to adapter.fetchMessages with the channel ID.\n */\n get messages(): AsyncIterable<Message> {\n const adapter = this.adapter;\n const channelId = this.id;\n\n return {\n async *[Symbol.asyncIterator]() {\n let cursor: string | undefined;\n\n while (true) {\n const fetchOptions = { cursor, direction: \"backward\" as const };\n const result = adapter.fetchChannelMessages\n ? await adapter.fetchChannelMessages(channelId, fetchOptions)\n : await adapter.fetchMessages(channelId, fetchOptions);\n\n // Messages within a page are chronological (oldest first),\n // but we want newest first, so reverse the page\n const reversed = [...result.messages].reverse();\n for (const message of reversed) {\n yield message;\n }\n\n if (!result.nextCursor || result.messages.length === 0) {\n break;\n }\n\n cursor = result.nextCursor;\n }\n },\n };\n }\n\n /**\n * Iterate threads in this channel, most recently active first.\n */\n threads(): AsyncIterable<ThreadSummary> {\n const adapter = this.adapter;\n const channelId = this.id;\n\n return {\n async *[Symbol.asyncIterator]() {\n if (!adapter.listThreads) {\n // Platform doesn't support threading — return empty\n return;\n }\n\n let cursor: string | undefined;\n\n while (true) {\n const result = await adapter.listThreads(channelId, {\n cursor,\n });\n\n for (const thread of result.threads) {\n yield thread;\n }\n\n if (!result.nextCursor || result.threads.length === 0) {\n break;\n }\n\n cursor = result.nextCursor;\n }\n },\n };\n }\n\n async fetchMetadata(): Promise<ChannelInfo> {\n if (this.adapter.fetchChannelInfo) {\n const info = await this.adapter.fetchChannelInfo(this.id);\n this._name = info.name ?? null;\n return info;\n }\n\n // Fallback: return basic info\n return {\n id: this.id,\n isDM: this.isDM,\n metadata: {},\n };\n }\n\n async post(\n message: string | PostableMessage | ChatElement\n ): Promise<SentMessage> {\n // Handle AsyncIterable (streaming) — not supported at channel level,\n // fall through to postMessage\n if (isAsyncIterable(message)) {\n // For channel-level streaming, accumulate and post as single message\n let accumulated = \"\";\n for await (const chunk of message) {\n accumulated += chunk;\n }\n return this.postSingleMessage(accumulated);\n }\n\n // Auto-convert JSX elements to CardElement\n let postable: string | AdapterPostableMessage = message as\n | string\n | AdapterPostableMessage;\n if (isJSX(message)) {\n const card = toCardElement(message);\n if (!card) {\n throw new Error(\"Invalid JSX element: must be a Card element\");\n }\n postable = card;\n }\n\n return this.postSingleMessage(postable);\n }\n\n private async postSingleMessage(\n postable: AdapterPostableMessage\n ): Promise<SentMessage> {\n const rawMessage = this.adapter.postChannelMessage\n ? await this.adapter.postChannelMessage(this.id, postable)\n : await this.adapter.postMessage(this.id, postable);\n\n return this.createSentMessage(rawMessage.id, postable, rawMessage.threadId);\n }\n\n async postEphemeral(\n user: string | Author,\n message: AdapterPostableMessage | ChatElement,\n options: PostEphemeralOptions\n ): Promise<EphemeralMessage | null> {\n const { fallbackToDM } = options;\n const userId = typeof user === \"string\" ? user : user.userId;\n\n let postable: AdapterPostableMessage;\n if (isJSX(message)) {\n const card = toCardElement(message);\n if (!card) {\n throw new Error(\"Invalid JSX element: must be a Card element\");\n }\n postable = card;\n } else {\n postable = message as AdapterPostableMessage;\n }\n\n if (this.adapter.postEphemeral) {\n return this.adapter.postEphemeral(this.id, userId, postable);\n }\n\n if (!fallbackToDM) {\n return null;\n }\n\n if (this.adapter.openDM) {\n const dmThreadId = await this.adapter.openDM(userId);\n const result = await this.adapter.postMessage(dmThreadId, postable);\n return {\n id: result.id,\n threadId: dmThreadId,\n usedFallback: true,\n raw: result.raw,\n };\n }\n\n return null;\n }\n\n async schedule(\n message: AdapterPostableMessage | ChatElement,\n options: { postAt: Date }\n ): Promise<ScheduledMessage> {\n let postable: AdapterPostableMessage;\n if (isJSX(message)) {\n const card = toCardElement(message);\n if (!card) {\n throw new Error(\"Invalid JSX element: must be a Card element\");\n }\n postable = card;\n } else {\n postable = message as AdapterPostableMessage;\n }\n\n if (!this.adapter.scheduleMessage) {\n throw new NotImplementedError(\n \"Scheduled messages are not supported by this adapter\",\n \"scheduling\"\n );\n }\n\n return this.adapter.scheduleMessage(this.id, postable, options);\n }\n\n async startTyping(status?: string): Promise<void> {\n await this.adapter.startTyping(this.id, status);\n }\n\n mentionUser(userId: string): string {\n return `<@${userId}>`;\n }\n\n toJSON(): SerializedChannel {\n return {\n _type: \"chat:Channel\",\n id: this.id,\n adapterName: this.adapter.name,\n isDM: this.isDM,\n };\n }\n\n static fromJSON<TState = Record<string, unknown>>(\n json: SerializedChannel,\n adapter?: Adapter\n ): ChannelImpl<TState> {\n const channel = new ChannelImpl<TState>({\n id: json.id,\n adapterName: json.adapterName,\n isDM: json.isDM,\n });\n if (adapter) {\n channel._adapter = adapter;\n }\n return channel;\n }\n\n static [WORKFLOW_SERIALIZE](instance: ChannelImpl): SerializedChannel {\n return instance.toJSON();\n }\n\n static [WORKFLOW_DESERIALIZE](data: SerializedChannel): ChannelImpl {\n return ChannelImpl.fromJSON(data);\n }\n\n private createSentMessage(\n messageId: string,\n postable: AdapterPostableMessage,\n threadIdOverride?: string\n ): SentMessage {\n const adapter = this.adapter;\n const threadId = threadIdOverride || this.id;\n const self = this;\n\n const { plainText, formatted, attachments } =\n extractMessageContent(postable);\n\n const sentMessage: SentMessage = {\n id: messageId,\n threadId,\n text: plainText,\n formatted,\n raw: null,\n author: {\n userId: \"self\",\n userName: adapter.userName,\n fullName: adapter.userName,\n isBot: true,\n isMe: true,\n },\n metadata: {\n dateSent: new Date(),\n edited: false,\n },\n attachments,\n\n toJSON() {\n return new Message(this).toJSON();\n },\n\n async edit(\n newContent: string | PostableMessage | ChatElement\n ): Promise<SentMessage> {\n let editPostable: string | AdapterPostableMessage = newContent as\n | string\n | AdapterPostableMessage;\n if (isJSX(newContent)) {\n const card = toCardElement(newContent);\n if (!card) {\n throw new Error(\"Invalid JSX element: must be a Card element\");\n }\n editPostable = card;\n }\n await adapter.editMessage(threadId, messageId, editPostable);\n return self.createSentMessage(messageId, editPostable);\n },\n\n async delete(): Promise<void> {\n await adapter.deleteMessage(threadId, messageId);\n },\n\n async addReaction(emoji: string): Promise<void> {\n await adapter.addReaction(threadId, messageId, emoji);\n },\n\n async removeReaction(emoji: string): Promise<void> {\n await adapter.removeReaction(threadId, messageId, emoji);\n },\n };\n\n return sentMessage;\n }\n}\n\n/**\n * Derive the channel ID from a thread ID.\n */\nexport function deriveChannelId(adapter: Adapter, threadId: string): string {\n return adapter.channelIdFromThreadId(threadId);\n}\n\n/**\n * Extract plain text, AST, and attachments from a message.\n */\nfunction extractMessageContent(message: AdapterPostableMessage): {\n plainText: string;\n formatted: import(\"mdast\").Root;\n attachments: import(\"./types\").Attachment[];\n} {\n if (typeof message === \"string\") {\n return {\n plainText: message,\n formatted: root([paragraph([textNode(message)])]),\n attachments: [],\n };\n }\n\n if (\"raw\" in message) {\n return {\n plainText: message.raw,\n formatted: root([paragraph([textNode(message.raw)])]),\n attachments: message.attachments || [],\n };\n }\n\n if (\"markdown\" in message) {\n const ast = parseMarkdown(message.markdown);\n return {\n plainText: toPlainText(ast),\n formatted: ast,\n attachments: message.attachments || [],\n };\n }\n\n if (\"ast\" in message) {\n return {\n plainText: toPlainText(message.ast),\n formatted: message.ast,\n attachments: message.attachments || [],\n };\n }\n\n if (\"card\" in message) {\n const fallbackText =\n message.fallbackText || cardToFallbackText(message.card);\n return {\n plainText: fallbackText,\n formatted: root([paragraph([textNode(fallbackText)])]),\n attachments: [],\n };\n }\n\n if (\"type\" in message && message.type === \"card\") {\n const fallbackText = cardToFallbackText(message);\n return {\n plainText: fallbackText,\n formatted: root([paragraph([textNode(fallbackText)])]),\n attachments: [],\n };\n }\n\n throw new Error(\"Invalid PostableMessage format\");\n}\n","/**\n * Singleton holder for Chat instance.\n * Separate module to avoid circular dependency between chat.ts and thread.ts.\n */\nimport type { Adapter, StateAdapter } from \"./types\";\n\n/**\n * Interface for the Chat singleton to avoid importing the full Chat class.\n */\nexport interface ChatSingleton {\n getAdapter(name: string): Adapter | undefined;\n getState(): StateAdapter;\n}\n\nlet _singleton: ChatSingleton | null = null;\n\n/**\n * Set the Chat singleton instance.\n * @internal Used by Chat.registerSingleton()\n */\nexport function setChatSingleton(chat: ChatSingleton): void {\n _singleton = chat;\n}\n\n/**\n * Get the Chat singleton instance.\n * @throws Error if no singleton has been registered\n */\nexport function getChatSingleton(): ChatSingleton {\n if (!_singleton) {\n throw new Error(\n \"No Chat singleton registered. Call chat.registerSingleton() first.\"\n );\n }\n return _singleton;\n}\n\n/**\n * Check if a Chat singleton has been registered.\n */\nexport function hasChatSingleton(): boolean {\n return _singleton !== null;\n}\n\n/**\n * Clear the Chat singleton (for testing).\n * @internal\n */\nexport function clearChatSingleton(): void {\n _singleton = null;\n}\n","/**\n * Message class with serialization support for workflow engines.\n */\n\nimport { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from \"@workflow/serde\";\nimport type { Root } from \"mdast\";\nimport type {\n Attachment,\n Author,\n FormattedContent,\n MessageMetadata,\n} from \"./types\";\n\n/**\n * Input data for creating a Message instance.\n * Use this interface when constructing Message objects.\n */\nexport interface MessageData<TRawMessage = unknown> {\n /** Attachments */\n attachments: Attachment[];\n /** Message author */\n author: Author;\n /** Structured formatting as an AST (mdast Root) */\n formatted: FormattedContent;\n /** Unique message ID */\n id: string;\n /** Whether the bot is @-mentioned in this message */\n isMention?: boolean;\n /** Message metadata */\n metadata: MessageMetadata;\n /** Platform-specific raw payload (escape hatch) */\n raw: TRawMessage;\n /** Plain text content (all formatting stripped) */\n text: string;\n /** Thread this message belongs to */\n threadId: string;\n}\n\n/**\n * Serialized message data for passing to external systems (e.g., workflow engines).\n * Dates are converted to ISO strings, and non-serializable fields are omitted.\n */\nexport interface SerializedMessage {\n _type: \"chat:Message\";\n attachments: Array<{\n type: \"image\" | \"file\" | \"video\" | \"audio\";\n url?: string;\n name?: string;\n mimeType?: string;\n size?: number;\n width?: number;\n height?: number;\n }>;\n author: {\n userId: string;\n userName: string;\n fullName: string;\n isBot: boolean | \"unknown\";\n isMe: boolean;\n };\n formatted: Root;\n id: string;\n isMention?: boolean;\n metadata: {\n dateSent: string; // ISO string\n edited: boolean;\n editedAt?: string; // ISO string\n };\n raw: unknown;\n text: string;\n threadId: string;\n}\n\n/**\n * A chat message with serialization support for workflow engines.\n *\n * @example\n * ```typescript\n * // Create a message\n * const message = new Message({\n * id: \"msg-1\",\n * threadId: \"slack:C123:1234.5678\",\n * text: \"Hello world\",\n * formatted: parseMarkdown(\"Hello world\"),\n * raw: {},\n * author: { userId: \"U123\", userName: \"user\", fullName: \"User\", isBot: false, isMe: false },\n * metadata: { dateSent: new Date(), edited: false },\n * attachments: [],\n * });\n *\n * // Serialize for workflow\n * const serialized = message.toJSON();\n * ```\n */\nexport class Message<TRawMessage = unknown> {\n /** Unique message ID */\n readonly id: string;\n /** Thread this message belongs to */\n readonly threadId: string;\n\n /** Plain text content (all formatting stripped) */\n text: string;\n /**\n * Structured formatting as an AST (mdast Root).\n * This is the canonical representation - use this for processing.\n * Use `stringifyMarkdown(message.formatted)` to get markdown string.\n */\n formatted: FormattedContent;\n /** Platform-specific raw payload (escape hatch) */\n raw: TRawMessage;\n\n /** Message author */\n author: Author;\n /** Message metadata */\n metadata: MessageMetadata;\n /** Attachments */\n attachments: Attachment[];\n\n /**\n * Whether the bot is @-mentioned in this message.\n *\n * This is set by the Chat SDK before passing the message to handlers.\n * It checks for `@username` in the message text using the adapter's\n * configured `userName` and optional `botUserId`.\n *\n * @example\n * ```typescript\n * chat.onSubscribedMessage(async (thread, message) => {\n * if (message.isMention) {\n * await thread.post(\"You mentioned me!\");\n * }\n * });\n * ```\n */\n isMention?: boolean;\n\n constructor(data: MessageData<TRawMessage>) {\n this.id = data.id;\n this.threadId = data.threadId;\n this.text = data.text;\n this.formatted = data.formatted;\n this.raw = data.raw;\n this.author = data.author;\n this.metadata = data.metadata;\n this.attachments = data.attachments;\n this.isMention = data.isMention;\n }\n\n /**\n * Serialize the message to a plain JSON object.\n * Use this to pass message data to external systems like workflow engines.\n *\n * Note: Attachment `data` (Buffer) and `fetchData` (function) are omitted\n * as they're not serializable.\n */\n toJSON(): SerializedMessage {\n return {\n _type: \"chat:Message\",\n id: this.id,\n threadId: this.threadId,\n text: this.text,\n formatted: this.formatted,\n raw: this.raw,\n author: {\n userId: this.author.userId,\n userName: this.author.userName,\n fullName: this.author.fullName,\n isBot: this.author.isBot,\n isMe: this.author.isMe,\n },\n metadata: {\n dateSent: this.metadata.dateSent.toISOString(),\n edited: this.metadata.edited,\n editedAt: this.metadata.editedAt?.toISOString(),\n },\n attachments: this.attachments.map((att) => ({\n type: att.type,\n url: att.url,\n name: att.name,\n mimeType: att.mimeType,\n size: att.size,\n width: att.width,\n height: att.height,\n })),\n isMention: this.isMention,\n };\n }\n\n /**\n * Reconstruct a Message from serialized JSON data.\n * Converts ISO date strings back to Date objects.\n */\n static fromJSON<TRawMessage = unknown>(\n json: SerializedMessage\n ): Message<TRawMessage> {\n return new Message<TRawMessage>({\n id: json.id,\n threadId: json.threadId,\n text: json.text,\n formatted: json.formatted,\n raw: json.raw as TRawMessage,\n author: json.author,\n metadata: {\n dateSent: new Date(json.metadata.dateSent),\n edited: json.metadata.edited,\n editedAt: json.metadata.editedAt\n ? new Date(json.metadata.editedAt)\n : undefined,\n },\n attachments: json.attachments,\n isMention: json.isMention,\n });\n }\n\n /**\n * Serialize a Message instance for @workflow/serde.\n * This static method is automatically called by workflow serialization.\n */\n static [WORKFLOW_SERIALIZE](instance: Message): SerializedMessage {\n return instance.toJSON();\n }\n\n /**\n * Deserialize a Message from @workflow/serde.\n * This static method is automatically called by workflow deserialization.\n */\n static [WORKFLOW_DESERIALIZE](data: SerializedMessage): Message {\n return Message.fromJSON(data);\n }\n}\n","/**\n * Error types for chat-sdk\n */\n\nexport class ChatError extends Error {\n readonly code: string;\n override readonly cause?: unknown;\n\n constructor(message: string, code: string, cause?: unknown) {\n super(message);\n this.name = \"ChatError\";\n this.code = code;\n this.cause = cause;\n }\n}\n\nexport class RateLimitError extends ChatError {\n readonly retryAfterMs?: number;\n\n constructor(message: string, retryAfterMs?: number, cause?: unknown) {\n super(message, \"RATE_LIMITED\", cause);\n this.name = \"RateLimitError\";\n this.retryAfterMs = retryAfterMs;\n }\n}\n\nexport class LockError extends ChatError {\n constructor(message: string, cause?: unknown) {\n super(message, \"LOCK_FAILED\", cause);\n this.name = \"LockError\";\n }\n}\n\nexport class NotImplementedError extends ChatError {\n readonly feature?: string;\n\n constructor(message: string, feature?: string, cause?: unknown) {\n super(message, \"NOT_IMPLEMENTED\", cause);\n this.name = \"NotImplementedError\";\n this.feature = feature;\n }\n}\n","/**\n * Logger types and implementations for chat-sdk\n */\n\nexport type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\" | \"silent\";\n\nexport interface Logger {\n /** Create a sub-logger with a prefix */\n child(prefix: string): Logger;\n debug(message: string, ...args: unknown[]): void;\n error(message: string, ...args: unknown[]): void;\n info(message: string, ...args: unknown[]): void;\n warn(message: string, ...args: unknown[]): void;\n}\n\n/**\n * Default console logger implementation.\n */\nexport class ConsoleLogger implements Logger {\n private readonly prefix: string;\n\n private readonly level: LogLevel;\n\n constructor(level: LogLevel = \"info\", prefix = \"chat-sdk\") {\n this.level = level;\n this.prefix = prefix;\n }\n\n private shouldLog(level: LogLevel): boolean {\n const levels: LogLevel[] = [\"debug\", \"info\", \"warn\", \"error\", \"silent\"];\n return levels.indexOf(level) >= levels.indexOf(this.level);\n }\n\n child(prefix: string): Logger {\n return new ConsoleLogger(this.level, `${this.prefix}:${prefix}`);\n }\n\n // eslint-disable-next-line no-console\n debug(message: string, ...args: unknown[]): void {\n if (this.shouldLog(\"debug\")) {\n console.debug(`[${this.prefix}] ${message}`, ...args);\n }\n }\n\n // eslint-disable-next-line no-console\n info(message: string, ...args: unknown[]): void {\n if (this.shouldLog(\"info\")) {\n console.info(`[${this.prefix}] ${message}`, ...args);\n }\n }\n\n // eslint-disable-next-line no-console\n warn(message: string, ...args: unknown[]): void {\n if (this.shouldLog(\"warn\")) {\n console.warn(`[${this.prefix}] ${message}`, ...args);\n }\n }\n\n // eslint-disable-next-line no-console\n error(message: string, ...args: unknown[]): void {\n if (this.shouldLog(\"error\")) {\n console.error(`[${this.prefix}] ${message}`, ...args);\n }\n }\n}\n","/**\n * Core types for chat-sdk\n */\n\nimport type { Root } from \"mdast\";\nimport type { CardElement } from \"./cards\";\nimport type { ChatElement } from \"./jsx-runtime\";\nimport type { Logger, LogLevel } from \"./logger\";\nimport type { Message } from \"./message\";\nimport type { ModalElement } from \"./modals\";\n\n// =============================================================================\n// Re-exports from extracted modules\n// =============================================================================\n\nexport {\n ChatError,\n LockError,\n NotImplementedError,\n RateLimitError,\n} from \"./errors\";\nexport type { Logger, LogLevel } from \"./logger\";\nexport { ConsoleLogger } from \"./logger\";\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\n/**\n * Chat configuration with type-safe adapter inference.\n * @template TAdapters - Record of adapter name to adapter instance\n */\nexport interface ChatConfig<\n TAdapters extends Record<string, Adapter> = Record<string, Adapter>,\n> {\n /** Map of adapter name to adapter instance */\n adapters: TAdapters;\n /**\n * TTL for message deduplication entries in milliseconds.\n * Defaults to 300000 (5 minutes). Increase if your webhook cold starts\n * cause platform retries that arrive after the default TTL expires.\n */\n dedupeTtlMs?: number;\n /**\n * Placeholder text for fallback streaming (post + edit) adapters.\n * Defaults to `\"...\"`.\n *\n * Set to `null` to avoid posting an initial placeholder message and instead\n * wait until some real text has been streamed before creating the message.\n */\n fallbackStreamingPlaceholderText?: string | null;\n /**\n * Logger instance or log level.\n * Pass \"silent\" to disable all logging.\n */\n logger?: Logger | LogLevel;\n /**\n * Behavior when a thread lock cannot be acquired (another handler is processing).\n * - `'drop'` (default) — throw `LockError`, preserving current behavior\n * - `'force'` — force-release the existing lock and re-acquire\n * - callback — custom logic receiving `(threadId, message)`, return `'force'` or `'drop'`\n *\n * When `'force'` is used, the previous handler continues executing — only the lock\n * is released, not the handler itself. This means two handlers may run concurrently\n * on the same thread. The old handler's `releaseLock()` call becomes a no-op since\n * the token no longer matches.\n */\n onLockConflict?:\n | \"force\"\n | \"drop\"\n | ((\n threadId: string,\n message: Message\n ) => \"force\" | \"drop\" | Promise<\"force\" | \"drop\">);\n /** State adapter for subscriptions and locking */\n state: StateAdapter;\n /**\n * Update interval for fallback streaming (post + edit) in milliseconds.\n * Defaults to 500ms. Lower values provide smoother updates but may hit rate limits.\n */\n streamingUpdateIntervalMs?: number;\n /** Default bot username across all adapters */\n userName: string;\n}\n\n/**\n * Options for webhook handling.\n */\nexport interface WebhookOptions {\n /**\n * Function to run message handling in the background.\n * Use this to ensure fast webhook responses while processing continues.\n *\n * @example\n * // Next.js App Router\n * import { after } from \"next/server\";\n * chat.webhooks.slack(request, { waitUntil: (p) => after(() => p) });\n *\n * @example\n * // Vercel Functions\n * import { waitUntil } from \"@vercel/functions\";\n * chat.webhooks.slack(request, { waitUntil });\n */\n waitUntil?: (task: Promise<unknown>) => void;\n}\n\n// =============================================================================\n// Adapter Interface\n// =============================================================================\n\n/**\n * Adapter interface with generics for platform-specific types.\n * @template TThreadId - Platform-specific thread ID data type\n * @template TRawMessage - Platform-specific raw message type\n */\nexport interface Adapter<TThreadId = unknown, TRawMessage = unknown> {\n /** Add a reaction to a message */\n addReaction(\n threadId: string,\n messageId: string,\n emoji: EmojiValue | string\n ): Promise<void>;\n /** Bot user ID for platforms that use IDs in mentions (e.g., Slack's <@U123>) */\n readonly botUserId?: string;\n\n /**\n * Derive channel ID from a thread ID.\n * Default fallback: first two colon-separated parts (e.g., \"slack:C123\").\n * Adapters with different structures should override this.\n */\n channelIdFromThreadId(threadId: string): string;\n\n /** Decode thread ID string back to platform-specific data */\n decodeThreadId(threadId: string): TThreadId;\n\n /** Delete a message */\n deleteMessage(threadId: string, messageId: string): Promise<void>;\n\n /** Edit an existing message */\n editMessage(\n threadId: string,\n messageId: string,\n message: AdapterPostableMessage\n ): Promise<RawMessage<TRawMessage>>;\n\n /** Encode platform-specific data into a thread ID string */\n encodeThreadId(platformData: TThreadId): string;\n\n /**\n * Fetch channel info/metadata.\n */\n fetchChannelInfo?(channelId: string): Promise<ChannelInfo>;\n\n /**\n * Fetch channel-level messages (top-level, not thread replies).\n * For example, Slack's conversations.history vs conversations.replies.\n */\n fetchChannelMessages?(\n channelId: string,\n options?: FetchOptions\n ): Promise<FetchResult<TRawMessage>>;\n\n /**\n * Fetch a single message by ID.\n * Optional - adapters that don't implement this will return null.\n *\n * @param threadId - The thread ID containing the message\n * @param messageId - The platform-specific message ID\n * @returns The message, or null if not found/not supported\n */\n fetchMessage?(\n threadId: string,\n messageId: string\n ): Promise<Message<TRawMessage> | null>;\n\n /**\n * Fetch messages from a thread.\n *\n * **Direction behavior:**\n * - `backward` (default): Fetches the most recent messages. Use this for loading\n * a chat view. The `nextCursor` points to older messages.\n * - `forward`: Fetches the oldest messages first. Use this for iterating through\n * message history. The `nextCursor` points to newer messages.\n *\n * **Message ordering:**\n * Messages within each page are always returned in chronological order (oldest first),\n * regardless of direction. This is the natural reading order for chat messages.\n *\n * @example\n * ```typescript\n * // Load most recent 50 messages for display\n * const recent = await adapter.fetchMessages(threadId, { limit: 50 });\n * // recent.messages: [older, ..., newest] in chronological order\n *\n * // Paginate backward to load older messages\n * const older = await adapter.fetchMessages(threadId, {\n * limit: 50,\n * cursor: recent.nextCursor,\n * });\n *\n * // Iterate through all history from the beginning\n * const history = await adapter.fetchMessages(threadId, {\n * limit: 100,\n * direction: 'forward',\n * });\n * ```\n */\n fetchMessages(\n threadId: string,\n options?: FetchOptions\n ): Promise<FetchResult<TRawMessage>>;\n\n /** Fetch thread metadata */\n fetchThread(threadId: string): Promise<ThreadInfo>;\n\n /** Handle incoming webhook request */\n handleWebhook(request: Request, options?: WebhookOptions): Promise<Response>;\n\n /** Called when Chat instance is created (internal use) */\n initialize(chat: ChatInstance): Promise<void>;\n\n /**\n * Check if a thread is a direct message conversation.\n *\n * @param threadId - The thread ID to check\n * @returns True if the thread is a DM, false otherwise\n */\n isDM?(threadId: string): boolean;\n\n /**\n * List threads in a channel.\n */\n listThreads?(\n channelId: string,\n options?: ListThreadsOptions\n ): Promise<ListThreadsResult<TRawMessage>>;\n /** Unique name for this adapter (e.g., \"slack\", \"teams\") */\n readonly name: string;\n\n /**\n * Optional hook called when a thread is subscribed to.\n * Adapters can use this to set up platform-specific subscriptions\n * (e.g., Google Chat Workspace Events).\n */\n onThreadSubscribe?(threadId: string): Promise<void>;\n\n /**\n * Open a direct message conversation with a user.\n *\n * @param userId - The platform-specific user ID\n * @returns The thread ID for the DM conversation\n *\n * @example\n * ```typescript\n * const dmThreadId = await adapter.openDM(\"U123456\");\n * await adapter.postMessage(dmThreadId, \"Hello!\");\n * ```\n */\n openDM?(userId: string): Promise<string>;\n\n /**\n * Open a modal/dialog form.\n *\n * @param triggerId - Platform-specific trigger ID from the action event\n * @param modal - The modal element to display\n * @param contextId - Optional context ID for server-side stored thread/message context\n * @returns The view/dialog ID\n */\n openModal?(\n triggerId: string,\n modal: ModalElement,\n contextId?: string\n ): Promise<{ viewId: string }>;\n\n /** Parse platform message format to normalized format */\n parseMessage(raw: TRawMessage): Message<TRawMessage>;\n\n /**\n * Post a message to channel top-level (not in a thread).\n */\n postChannelMessage?(\n channelId: string,\n message: AdapterPostableMessage\n ): Promise<RawMessage<TRawMessage>>;\n\n /**\n * Post an ephemeral message visible only to a specific user.\n *\n * This is optional - if not implemented, Thread.postEphemeral will\n * fall back to openDM + postMessage when fallbackToDM is true.\n *\n * @param threadId - The thread to post in\n * @param userId - The user who should see the message\n * @param message - The message content\n * @returns EphemeralMessage with usedFallback: false\n */\n postEphemeral?(\n threadId: string,\n userId: string,\n message: AdapterPostableMessage\n ): Promise<EphemeralMessage<TRawMessage>>;\n\n /** Post a message to a thread */\n postMessage(\n threadId: string,\n message: AdapterPostableMessage\n ): Promise<RawMessage<TRawMessage>>;\n\n /** Remove a reaction from a message */\n removeReaction(\n threadId: string,\n messageId: string,\n emoji: EmojiValue | string\n ): Promise<void>;\n\n /** Render formatted content to platform-specific string */\n renderFormatted(content: FormattedContent): string;\n\n /**\n * Schedule a message for future delivery.\n *\n * Optional — only supported by adapters with native scheduling APIs (e.g., Slack).\n * Thread.schedule() will throw NotImplementedError if this method is absent.\n *\n * @param threadId - The thread to post in\n * @param message - The message content\n * @param options - Scheduling options including the target delivery time\n * @returns A ScheduledMessage with cancel() capability\n */\n scheduleMessage?(\n threadId: string,\n message: AdapterPostableMessage,\n options: { postAt: Date }\n ): Promise<ScheduledMessage<TRawMessage>>;\n\n /** Show typing indicator */\n startTyping(threadId: string, status?: string): Promise<void>;\n\n /**\n * Stream a message using platform-native streaming APIs.\n *\n * The adapter consumes the async iterable and handles the entire streaming lifecycle.\n * Only available on platforms with native streaming support (e.g., Slack).\n *\n * The stream can yield plain strings (text chunks) or {@link StreamChunk} objects\n * for rich content like task progress cards. Adapters that don't support structured\n * chunks will extract text from `markdown_text` chunks and ignore other types.\n *\n * @param threadId - The thread to stream to\n * @param textStream - Async iterable of text chunks or structured StreamChunk objects\n * @param options - Platform-specific streaming options\n * @returns The raw message after streaming completes\n */\n stream?(\n threadId: string,\n textStream: AsyncIterable<string | StreamChunk>,\n options?: StreamOptions\n ): Promise<RawMessage<TRawMessage>>;\n /** Bot username (can override global userName) */\n readonly userName: string;\n}\n\n/**\n * A structured streaming chunk for platform-native rich content.\n *\n * On Slack, these map directly to streaming chunk types:\n * - `markdown_text`: Streamed text content\n * - `task_update`: Tool/step progress cards (pending → in_progress → complete → error)\n * - `plan_update`: Plan title updates\n *\n * Adapters that don't support structured chunks will extract `text` from\n * `markdown_text` chunks and ignore other types gracefully.\n */\nexport type StreamChunk = MarkdownTextChunk | TaskUpdateChunk | PlanUpdateChunk;\n\nexport interface MarkdownTextChunk {\n text: string;\n type: \"markdown_text\";\n}\n\nexport interface TaskUpdateChunk {\n id: string;\n output?: string;\n status: \"pending\" | \"in_progress\" | \"complete\" | \"error\";\n title: string;\n type: \"task_update\";\n}\n\nexport interface PlanUpdateChunk {\n title: string;\n type: \"plan_update\";\n}\n\n/**\n * Options for streaming messages.\n * Platform-specific options are passed through to the adapter.\n */\nexport interface StreamOptions {\n /** Slack: The team/workspace ID */\n recipientTeamId?: string;\n /** Slack: The user ID to stream to (for AI assistant context) */\n recipientUserId?: string;\n /** Block Kit elements to attach when stopping the stream (Slack only, via chat.stopStream) */\n stopBlocks?: unknown[];\n /**\n * Slack: Controls how task_update chunks are displayed.\n * - `\"timeline\"` — individual task cards shown inline with text (default)\n * - `\"plan\"` — all tasks grouped into a single plan block\n */\n taskDisplayMode?: \"timeline\" | \"plan\";\n /** Minimum interval between updates in ms (default: 1000). Used for fallback mode (GChat/Teams). */\n updateIntervalMs?: number;\n}\n\n/** Internal interface for Chat instance passed to adapters */\nexport interface ChatInstance {\n /** Get the configured logger, optionally with a child prefix */\n getLogger(prefix?: string): Logger;\n\n getState(): StateAdapter;\n getUserName(): string;\n\n /**\n * @deprecated Use processMessage instead. This method is for internal use.\n */\n handleIncomingMessage(\n adapter: Adapter,\n threadId: string,\n message: Message\n ): Promise<void>;\n\n /**\n * Process an incoming action event (button click) from an adapter.\n * Handles waitUntil registration and error catching internally.\n *\n * @param event - The action event (without thread field, will be added)\n * @param options - Webhook options including waitUntil\n */\n processAction(\n event: Omit<ActionEvent, \"thread\" | \"openModal\"> & { adapter: Adapter },\n options?: WebhookOptions\n ): void;\n\n processAppHomeOpened(\n event: AppHomeOpenedEvent,\n options?: WebhookOptions\n ): void;\n\n processAssistantContextChanged(\n event: AssistantContextChangedEvent,\n options?: WebhookOptions\n ): void;\n\n processAssistantThreadStarted(\n event: AssistantThreadStartedEvent,\n options?: WebhookOptions\n ): void;\n\n processMemberJoinedChannel(\n event: MemberJoinedChannelEvent,\n options?: WebhookOptions\n ): void;\n /**\n * Process an incoming message from an adapter.\n * Handles waitUntil registration and error catching internally.\n *\n * @param adapter - The adapter that received the message\n * @param threadId - The thread ID\n * @param message - Either a parsed message, or a factory function for lazy async parsing\n * @param options - Webhook options including waitUntil\n */\n processMessage(\n adapter: Adapter,\n threadId: string,\n message: Message | (() => Promise<Message>),\n options?: WebhookOptions\n ): void;\n\n /**\n * Process a modal close event from an adapter.\n *\n * @param event - The modal close event (without relatedThread/relatedMessage/relatedChannel)\n * @param contextId - Context ID for retrieving stored thread/message/channel context\n * @param options - Webhook options\n */\n processModalClose(\n event: Omit<\n ModalCloseEvent,\n \"relatedThread\" | \"relatedMessage\" | \"relatedChannel\"\n >,\n contextId?: string,\n options?: WebhookOptions\n ): void;\n\n /**\n * Process a modal submit event from an adapter.\n *\n * @param event - The modal submit event (without relatedThread/relatedMessage/relatedChannel)\n * @param contextId - Context ID for retrieving stored thread/message/channel context\n * @param options - Webhook options\n */\n processModalSubmit(\n event: Omit<\n ModalSubmitEvent,\n \"relatedThread\" | \"relatedMessage\" | \"relatedChannel\"\n >,\n contextId?: string,\n options?: WebhookOptions\n ): Promise<ModalResponse | undefined>;\n\n /**\n * Process an incoming reaction event from an adapter.\n * Handles waitUntil registration and error catching internally.\n *\n * @param event - The reaction event (without adapter field, will be added)\n * @param options - Webhook options including waitUntil\n */\n processReaction(\n event: Omit<ReactionEvent, \"adapter\" | \"thread\"> & { adapter?: Adapter },\n options?: WebhookOptions\n ): void;\n\n /**\n * Process an incoming slash command from an adapter.\n * Handles waitUntil registration and error catching internally.\n *\n * @param event - The slash command event\n * @param options - Webhook options including waitUntil\n */\n processSlashCommand(\n event: Omit<SlashCommandEvent, \"channel\" | \"openModal\"> & {\n adapter: Adapter;\n channelId: string;\n },\n options?: WebhookOptions\n ): void;\n}\n\n// =============================================================================\n// State Adapter Interface\n// =============================================================================\n\nexport interface StateAdapter {\n /** Acquire a lock on a thread (returns null if already locked) */\n acquireLock(threadId: string, ttlMs: number): Promise<Lock | null>;\n /** Connect to the state backend */\n connect(): Promise<void>;\n\n /** Delete a cached value */\n delete(key: string): Promise<void>;\n\n /** Disconnect from the state backend */\n disconnect(): Promise<void>;\n\n /** Extend a lock's TTL */\n extendLock(lock: Lock, ttlMs: number): Promise<boolean>;\n\n /**\n * Force-release a lock on a thread, regardless of ownership token.\n * The previous lock holder's handler continues running — only the lock is released.\n * The old handler's `releaseLock()` becomes a no-op (token mismatch).\n */\n forceReleaseLock(threadId: string): Promise<void>;\n\n /** Get a cached value by key */\n get<T = unknown>(key: string): Promise<T | null>;\n\n /** Check if subscribed to a thread */\n isSubscribed(threadId: string): Promise<boolean>;\n\n /** Release a lock */\n releaseLock(lock: Lock): Promise<void>;\n\n /** Set a cached value with optional TTL in milliseconds */\n set<T = unknown>(key: string, value: T, ttlMs?: number): Promise<void>;\n\n /** Atomically set a value only if the key does not already exist. Returns true if set, false if key existed. */\n setIfNotExists(key: string, value: unknown, ttlMs?: number): Promise<boolean>;\n\n /** Subscribe to a thread (persists across restarts) */\n subscribe(threadId: string): Promise<void>;\n\n /** Unsubscribe from a thread */\n unsubscribe(threadId: string): Promise<void>;\n}\n\nexport interface Lock {\n expiresAt: number;\n threadId: string;\n token: string;\n}\n\n// =============================================================================\n// Postable (base interface for Thread and Channel)\n// =============================================================================\n\n/**\n * Base interface for entities that can receive messages.\n * Both Thread and Channel extend this interface.\n *\n * @template TState - Custom state type stored per entity\n * @template TRawMessage - Platform-specific raw message type\n */\nexport interface Postable<\n TState = Record<string, unknown>,\n TRawMessage = unknown,\n> {\n /** The adapter this entity belongs to */\n readonly adapter: Adapter;\n /** Unique ID */\n readonly id: string;\n /** Whether this is a direct message conversation */\n readonly isDM: boolean;\n\n /**\n * Get a platform-specific mention string for a user.\n */\n mentionUser(userId: string): string;\n\n /**\n * Iterate messages newest first (backward from most recent).\n * Auto-paginates lazily — only fetches pages as consumed.\n */\n readonly messages: AsyncIterable<Message<TRawMessage>>;\n\n /**\n * Post a message.\n */\n post(\n message: string | PostableMessage | ChatElement\n ): Promise<SentMessage<TRawMessage>>;\n\n /**\n * Post an ephemeral message visible only to a specific user.\n */\n postEphemeral(\n user: string | Author,\n message: AdapterPostableMessage | ChatElement,\n options: PostEphemeralOptions\n ): Promise<EphemeralMessage<TRawMessage> | null>;\n\n /**\n * Schedule a message for future delivery.\n *\n * Currently only supported by the Slack adapter. Other adapters\n * will throw NotImplementedError.\n *\n * @param message - The message content (streaming not supported)\n * @param options - Scheduling options including the target delivery time\n * @returns A ScheduledMessage with cancel() capability\n *\n * @example\n * ```typescript\n * const scheduled = await thread.schedule(\"Reminder: standup!\", {\n * postAt: new Date(\"2026-03-09T09:00:00Z\"),\n * });\n *\n * // Cancel before it's sent\n * await scheduled.cancel();\n * ```\n */\n schedule(\n message: AdapterPostableMessage | ChatElement,\n options: { postAt: Date }\n ): Promise<ScheduledMessage<TRawMessage>>;\n\n /**\n * Set the state. Merges with existing state by default.\n */\n setState(\n state: Partial<TState>,\n options?: { replace?: boolean }\n ): Promise<void>;\n\n /** Show typing indicator */\n startTyping(status?: string): Promise<void>;\n\n /**\n * Get the current state.\n * Returns null if no state has been set.\n */\n readonly state: Promise<TState | null>;\n}\n\n// =============================================================================\n// Channel\n// =============================================================================\n\n/**\n * Represents a channel/conversation container that holds threads.\n * Extends Postable for message posting capabilities.\n *\n * @template TState - Custom state type stored per channel\n * @template TRawMessage - Platform-specific raw message type\n */\nexport interface Channel<\n TState = Record<string, unknown>,\n TRawMessage = unknown,\n> extends Postable<TState, TRawMessage> {\n /** Fetch channel metadata from the platform */\n fetchMetadata(): Promise<ChannelInfo>;\n /** Channel name (e.g., \"#general\"). Null until fetchInfo() is called. */\n readonly name: string | null;\n\n /**\n * Iterate threads in this channel, most recently active first.\n * Returns ThreadSummary (lightweight) for efficiency.\n * Empty iterable on threadless platforms.\n */\n threads(): AsyncIterable<ThreadSummary<TRawMessage>>;\n}\n\n/**\n * Lightweight summary of a thread within a channel.\n */\nexport interface ThreadSummary<TRawMessage = unknown> {\n /** Full thread ID */\n id: string;\n /** Timestamp of most recent reply */\n lastReplyAt?: Date;\n /** Reply count (if available) */\n replyCount?: number;\n /** Root/first message of the thread */\n rootMessage: Message<TRawMessage>;\n}\n\n/**\n * Channel metadata returned by fetchInfo().\n */\nexport interface ChannelInfo {\n id: string;\n isDM?: boolean;\n memberCount?: number;\n metadata: Record<string, unknown>;\n name?: string;\n}\n\n/**\n * Options for listing threads in a channel.\n */\nexport interface ListThreadsOptions {\n cursor?: string;\n limit?: number;\n}\n\n/**\n * Result of listing threads in a channel.\n */\nexport interface ListThreadsResult<TRawMessage = unknown> {\n nextCursor?: string;\n threads: ThreadSummary<TRawMessage>[];\n}\n\n// =============================================================================\n// Thread\n// =============================================================================\n\n/** Default TTL for thread state (30 days in milliseconds) */\nexport const THREAD_STATE_TTL_MS = 30 * 24 * 60 * 60 * 1000;\n\n/**\n * Thread interface with support for custom state.\n * Extends Postable for shared message posting capabilities.\n *\n * @template TState - Custom state type stored per-thread (default: Record<string, unknown>)\n * @template TRawMessage - Platform-specific raw message type\n */\nexport interface Thread<TState = Record<string, unknown>, TRawMessage = unknown>\n extends Postable<TState, TRawMessage> {\n /**\n * Async iterator for all messages in the thread.\n * Messages are yielded in chronological order (oldest first).\n * Automatically handles pagination.\n */\n allMessages: AsyncIterable<Message<TRawMessage>>;\n\n /** Get the Channel containing this thread */\n readonly channel: Channel<TState, TRawMessage>;\n // Inherited from Postable: id, adapter, isDM, state, setState,\n // messages (newest first), post, postEphemeral, startTyping, mentionUser\n\n /** Channel/conversation ID */\n readonly channelId: string;\n\n /**\n * Wrap a Message object as a SentMessage with edit/delete capabilities.\n * Used internally for reconstructing messages from serialized data.\n */\n createSentMessageFromMessage(\n message: Message<TRawMessage>\n ): SentMessage<TRawMessage>;\n\n /**\n * Check if this thread is currently subscribed.\n *\n * In subscribed message handlers, this is optimized to return true immediately\n * without a state lookup, since we already know we're in a subscribed context.\n *\n * @returns Promise resolving to true if subscribed, false otherwise\n */\n isSubscribed(): Promise<boolean>;\n\n /**\n * Get a platform-specific mention string for a user.\n * Use this to @-mention a user in a message.\n * @example\n * await thread.post(`Hey ${thread.mentionUser(userId)}, check this out!`);\n */\n mentionUser(userId: string): string;\n\n /**\n * Post a message to this thread.\n *\n * Supports text, markdown, cards, and streaming from async iterables.\n * When posting a stream (e.g., from AI SDK), uses platform-native streaming\n * APIs when available (Slack), or falls back to post + edit with throttling.\n *\n * @param message - String, PostableMessage, JSX Card, or AsyncIterable<string>\n * @returns A SentMessage with methods to edit, delete, or add reactions\n *\n * @example\n * ```typescript\n * // Simple string\n * await thread.post(\"Hello!\");\n *\n * // Markdown\n * await thread.post({ markdown: \"**Bold** and _italic_\" });\n *\n * // With emoji\n * await thread.post(`${emoji.thumbs_up} Great job!`);\n *\n * // JSX Card (with @jsxImportSource chat)\n * await thread.post(\n * <Card title=\"Welcome!\">\n * <Text>Hello world</Text>\n * </Card>\n * );\n *\n * // Stream from AI SDK\n * const result = await agent.stream({ prompt: message.text });\n * await thread.post(result.textStream);\n * ```\n */\n post(\n message: string | PostableMessage | ChatElement\n ): Promise<SentMessage<TRawMessage>>;\n\n /**\n * Post an ephemeral message visible only to a specific user.\n *\n * **Platform Behavior:**\n * - **Slack**: Native ephemeral (session-dependent, disappears on reload)\n * - **Google Chat**: Native private message (persists, only target user sees it)\n * - **Discord**: No native support - requires fallbackToDM: true\n * - **Teams**: No native support - requires fallbackToDM: true\n *\n * @param user - User ID string or Author object (from message.author or event.user)\n * @param message - Message content (string, markdown, card, etc.). Streaming is not supported.\n * @param options.fallbackToDM - Required. If true, falls back to DM when native\n * ephemeral is not supported. If false, returns null when unsupported.\n * @returns EphemeralMessage with `usedFallback: true` if DM was used, or null\n * if native ephemeral not supported and fallbackToDM is false\n *\n * @example\n * ```typescript\n * // Always send (DM fallback on Discord/Teams)\n * await thread.postEphemeral(user, 'Only you can see this!', { fallbackToDM: true })\n *\n * // Only send if native ephemeral supported (Slack/GChat)\n * const result = await thread.postEphemeral(user, 'Secret!', { fallbackToDM: false })\n * if (!result) {\n * // Platform doesn't support native ephemeral - handle accordingly\n * }\n * ```\n */\n postEphemeral(\n user: string | Author,\n message: AdapterPostableMessage | ChatElement,\n options: PostEphemeralOptions\n ): Promise<EphemeralMessage<TRawMessage> | null>;\n\n /** Recently fetched messages (cached) */\n recentMessages: Message<TRawMessage>[];\n\n /**\n * Refresh `recentMessages` from the API.\n *\n * Fetches the latest 50 messages and updates `recentMessages`.\n */\n refresh(): Promise<void>;\n\n /**\n * Show typing indicator in the thread.\n *\n * Some platforms support persistent typing indicators, others just send once.\n * Optional status (e.g. \"Typing...\", \"Searching documents...\") is shown where supported.\n */\n startTyping(status?: string): Promise<void>;\n\n /**\n * Subscribe to future messages in this thread.\n *\n * Once subscribed, all messages in this thread will trigger `onSubscribedMessage` handlers.\n * The initial message that triggered subscription will NOT fire the handler.\n *\n * @example\n * ```typescript\n * chat.onNewMention(async (thread, message) => {\n * await thread.subscribe(); // Subscribe to follow-up messages\n * await thread.post(\"I'm now watching this thread!\");\n * });\n * ```\n */\n subscribe(): Promise<void>;\n\n /**\n * Unsubscribe from this thread.\n *\n * Future messages will no longer trigger `onSubscribedMessage` handlers.\n */\n unsubscribe(): Promise<void>;\n}\n\nexport interface ThreadInfo {\n channelId: string;\n channelName?: string;\n id: string;\n /** Whether this is a direct message conversation */\n isDM?: boolean;\n /** Platform-specific metadata */\n metadata: Record<string, unknown>;\n}\n\n/**\n * Direction for fetching messages.\n *\n * - `backward`: Fetch most recent messages first. Pagination moves toward older messages.\n * This is the default, suitable for loading a chat view (show latest messages first).\n *\n * - `forward`: Fetch oldest messages first. Pagination moves toward newer messages.\n * Suitable for iterating through message history from the beginning.\n *\n * In both directions, messages within each page are returned in chronological order\n * (oldest first), which is the natural reading order for chat messages.\n *\n * @example\n * ```typescript\n * // Load most recent 50 messages (default)\n * const recent = await adapter.fetchMessages(threadId, { limit: 50 });\n * // recent.messages: [older, ..., newest] (chronological within page)\n * // recent.nextCursor: points to older messages\n *\n * // Iterate through all history from beginning\n * const history = await adapter.fetchMessages(threadId, {\n * limit: 50,\n * direction: 'forward',\n * });\n * // history.messages: [oldest, ..., newer] (chronological within page)\n * // history.nextCursor: points to even newer messages\n * ```\n */\nexport type FetchDirection = \"forward\" | \"backward\";\n\n/**\n * Options for fetching messages from a thread.\n */\nexport interface FetchOptions {\n /**\n * Pagination cursor for fetching the next page of messages.\n * Pass the `nextCursor` from a previous `FetchResult`.\n */\n cursor?: string;\n /**\n * Direction to fetch messages.\n *\n * - `backward` (default): Fetch most recent messages. Cursor moves to older messages.\n * - `forward`: Fetch oldest messages. Cursor moves to newer messages.\n *\n * Messages within each page are always returned in chronological order (oldest first).\n */\n direction?: FetchDirection;\n /** Maximum number of messages to fetch. Default varies by adapter (50-100). */\n limit?: number;\n}\n\n/**\n * Result of fetching messages from a thread.\n */\nexport interface FetchResult<TRawMessage = unknown> {\n /**\n * Messages in chronological order (oldest first within this page).\n *\n * For `direction: 'backward'` (default): These are the N most recent messages.\n * For `direction: 'forward'`: These are the N oldest messages (or next N after cursor).\n */\n messages: Message<TRawMessage>[];\n /**\n * Cursor for fetching the next page.\n * Pass this as `cursor` in the next `fetchMessages` call.\n *\n * - For `direction: 'backward'`: Points to older messages.\n * - For `direction: 'forward'`: Points to newer messages.\n *\n * Undefined if there are no more messages in that direction.\n */\n nextCursor?: string;\n}\n\n// =============================================================================\n// Message\n// =============================================================================\n\n/**\n * Formatted content using mdast AST.\n * This is the canonical representation of message formatting.\n */\nexport type FormattedContent = Root;\n\n/** Raw message returned from adapter (before wrapping as SentMessage) */\nexport interface RawMessage<TRawMessage = unknown> {\n id: string;\n raw: TRawMessage;\n threadId: string;\n}\n\nexport interface Author {\n /** Display name */\n fullName: string;\n /** Whether the author is a bot */\n isBot: boolean | \"unknown\";\n /** Whether the author is this bot */\n isMe: boolean;\n /** Unique user ID */\n userId: string;\n /** Username/handle for @-mentions */\n userName: string;\n}\n\nexport interface MessageMetadata {\n /** When the message was sent */\n dateSent: Date;\n /** Whether the message has been edited */\n edited: boolean;\n /** When the message was last edited */\n editedAt?: Date;\n}\n\n// =============================================================================\n// Sent Message (returned from thread.post())\n// =============================================================================\n\nexport interface SentMessage<TRawMessage = unknown>\n extends Message<TRawMessage> {\n /** Add a reaction to this message */\n addReaction(emoji: EmojiValue | string): Promise<void>;\n /** Delete this message */\n delete(): Promise<void>;\n /** Edit this message with text, a PostableMessage, or a JSX Card element */\n edit(\n newContent: string | PostableMessage | ChatElement\n ): Promise<SentMessage<TRawMessage>>;\n /** Remove a reaction from this message */\n removeReaction(emoji: EmojiValue | string): Promise<void>;\n}\n\n// =============================================================================\n// Ephemeral Message (returned from thread.postEphemeral())\n// =============================================================================\n\n/**\n * Result of posting an ephemeral message.\n *\n * Ephemeral messages are visible only to a specific user and typically\n * cannot be edited or deleted (platform-dependent).\n */\nexport interface EphemeralMessage<TRawMessage = unknown> {\n /** Message ID (may be empty for some platforms) */\n id: string;\n /** Platform-specific raw response */\n raw: TRawMessage;\n /** Thread ID where message was sent (or DM thread if fallback was used) */\n threadId: string;\n /** Whether this used native ephemeral or fell back to DM */\n usedFallback: boolean;\n}\n\n/**\n * Options for posting ephemeral messages.\n */\nexport interface PostEphemeralOptions {\n /**\n * Controls behavior when native ephemeral is not supported by the platform.\n *\n * - `true`: Falls back to sending a DM to the user\n * - `false`: Returns `null` if native ephemeral is not supported\n */\n fallbackToDM: boolean;\n}\n\n// =============================================================================\n// Scheduled Message (returned from thread.schedule())\n// =============================================================================\n\n/**\n * Result of scheduling a message for future delivery.\n *\n * Currently only supported by the Slack adapter via `chat.scheduleMessage`.\n * Other adapters will throw `NotImplementedError` when `schedule()` is called.\n */\nexport interface ScheduledMessage<TRawMessage = unknown> {\n /** Cancel the scheduled message before it's sent */\n cancel(): Promise<void>;\n /** Channel ID where the message will be posted */\n channelId: string;\n /** When the message will be sent */\n postAt: Date;\n /** Platform-specific raw response */\n raw: TRawMessage;\n /** Platform-specific scheduled message ID */\n scheduledMessageId: string;\n}\n\n// =============================================================================\n// Postable Message\n// =============================================================================\n\n/**\n * Input type for adapter postMessage/editMessage methods.\n * This excludes streams since adapters handle content synchronously.\n */\nexport type AdapterPostableMessage =\n | string\n | PostableRaw\n | PostableMarkdown\n | PostableAst\n | PostableCard\n | CardElement;\n\n/**\n * A message that can be posted to a thread.\n *\n * - `string` - Raw text, passed through as-is to the platform\n * - `{ raw: string }` - Explicit raw text, passed through as-is\n * - `{ markdown: string }` - Markdown text, converted to platform format\n * - `{ ast: Root }` - mdast AST, converted to platform format\n * - `{ card: CardElement }` - Rich card with buttons (Block Kit / Adaptive Cards / GChat Cards)\n * - `CardElement` - Direct card element\n * - `AsyncIterable<string>` - Streaming text (e.g., from AI SDK's textStream)\n * - `AsyncIterable<string | StreamEvent>` - AI SDK fullStream (auto-detected, extracts text with step separators)\n */\nexport type PostableMessage =\n | AdapterPostableMessage\n | AsyncIterable<string | StreamChunk | StreamEvent>;\n\n/**\n * Duck-typed stream event compatible with AI SDK's `fullStream`.\n * - `text-delta` events are extracted as text output.\n * - `step-finish` events trigger paragraph separators between steps.\n * - All other event types (tool-call, tool-result, etc.) are silently skipped.\n */\nexport type StreamEvent =\n | { textDelta: string; type: \"text-delta\" }\n | { type: \"step-finish\" }\n | { type: string };\n\nexport interface PostableRaw {\n /** File/image attachments */\n attachments?: Attachment[];\n /** Files to upload */\n files?: FileUpload[];\n /** Raw text passed through as-is to the platform */\n raw: string;\n}\n\nexport interface PostableMarkdown {\n /** File/image attachments */\n attachments?: Attachment[];\n /** Files to upload */\n files?: FileUpload[];\n /** Markdown text, converted to platform format */\n markdown: string;\n}\n\nexport interface PostableAst {\n /** mdast AST, converted to platform format */\n ast: Root;\n /** File/image attachments */\n attachments?: Attachment[];\n /** Files to upload */\n files?: FileUpload[];\n}\n\nexport interface PostableCard {\n /** Rich card element */\n card: CardElement;\n /** Fallback text for platforms/clients that can't render cards */\n fallbackText?: string;\n /** Files to upload */\n files?: FileUpload[];\n}\n\nexport interface Attachment {\n /** Binary data (for uploading or if already fetched) */\n data?: Buffer | Blob;\n /**\n * Fetch the attachment data.\n * For platforms that require authentication (like Slack private URLs),\n * this method handles the auth automatically.\n */\n fetchData?: () => Promise<Buffer>;\n /** Image/video height (if applicable) */\n height?: number;\n /** MIME type */\n mimeType?: string;\n /** Filename */\n name?: string;\n /** File size in bytes */\n size?: number;\n /** Type of attachment */\n type: \"image\" | \"file\" | \"video\" | \"audio\";\n /** URL to the file (for linking/downloading) */\n url?: string;\n /** Image/video width (if applicable) */\n width?: number;\n}\n\n/**\n * File to upload with a message.\n */\nexport interface FileUpload {\n /** Binary data */\n data: Buffer | Blob | ArrayBuffer;\n /** Filename */\n filename: string;\n /** MIME type (optional, will be inferred from filename if not provided) */\n mimeType?: string;\n}\n\n// =============================================================================\n// Event Handlers\n// =============================================================================\n\n/**\n * Handler for new @-mentions of the bot.\n *\n * **Important**: This handler is ONLY called for mentions in **unsubscribed** threads.\n * Once a thread is subscribed (via `thread.subscribe()`), subsequent messages\n * including @-mentions go to `onSubscribedMessage` handlers instead.\n *\n * To detect mentions in subscribed threads, check `message.isMention`:\n *\n * @example\n * ```typescript\n * // Handle new mentions (unsubscribed threads only)\n * chat.onNewMention(async (thread, message) => {\n * await thread.subscribe(); // Subscribe to follow-up messages\n * await thread.post(\"Hello! I'll be watching this thread.\");\n * });\n *\n * // Handle all messages in subscribed threads\n * chat.onSubscribedMessage(async (thread, message) => {\n * if (message.isMention) {\n * // User @-mentioned us in a thread we're already watching\n * await thread.post(\"You mentioned me again!\");\n * }\n * });\n * ```\n */\nexport type MentionHandler<TState = Record<string, unknown>> = (\n thread: Thread<TState>,\n message: Message\n) => void | Promise<void>;\n\n/**\n * Handler for messages matching a regex pattern.\n *\n * Registered via `chat.onNewMessage(pattern, handler)`. Called when a message\n * matches the pattern in an unsubscribed thread.\n */\nexport type MessageHandler<TState = Record<string, unknown>> = (\n thread: Thread<TState>,\n message: Message\n) => void | Promise<void>;\n\n/**\n * Handler for messages in subscribed threads.\n *\n * Called for all messages in threads that have been subscribed via `thread.subscribe()`.\n * This includes:\n * - Follow-up messages from users\n * - Messages that @-mention the bot (check `message.isMention`)\n *\n * Does NOT fire for:\n * - The message that triggered the subscription (e.g., the initial @mention)\n * - Messages sent by the bot itself\n *\n * @example\n * ```typescript\n * chat.onSubscribedMessage(async (thread, message) => {\n * // Handle all follow-up messages\n * if (message.isMention) {\n * // User @-mentioned us in a subscribed thread\n * }\n * await thread.post(`Got your message: ${message.text}`);\n * });\n * ```\n */\nexport type SubscribedMessageHandler<TState = Record<string, unknown>> = (\n thread: Thread<TState>,\n message: Message\n) => void | Promise<void>;\n\n// =============================================================================\n// Reactions / Emoji\n// =============================================================================\n\n/**\n * Well-known emoji that work across platforms (Slack and Google Chat).\n * These are normalized to a common format regardless of platform.\n */\nexport type WellKnownEmoji =\n // Reactions & Gestures\n | \"thumbs_up\"\n | \"thumbs_down\"\n | \"clap\"\n | \"wave\"\n | \"pray\"\n | \"muscle\"\n | \"ok_hand\"\n | \"point_up\"\n | \"point_down\"\n | \"point_left\"\n | \"point_right\"\n | \"raised_hands\"\n | \"shrug\"\n | \"facepalm\"\n // Emotions & Faces\n | \"heart\"\n | \"smile\"\n | \"laugh\"\n | \"thinking\"\n | \"sad\"\n | \"cry\"\n | \"angry\"\n | \"love_eyes\"\n | \"cool\"\n | \"wink\"\n | \"surprised\"\n | \"worried\"\n | \"confused\"\n | \"neutral\"\n | \"sleeping\"\n | \"sick\"\n | \"mind_blown\"\n | \"relieved\"\n | \"grimace\"\n | \"rolling_eyes\"\n | \"hug\"\n | \"zany\"\n // Status & Symbols\n | \"check\"\n | \"x\"\n | \"question\"\n | \"exclamation\"\n | \"warning\"\n | \"stop\"\n | \"info\"\n | \"100\"\n | \"fire\"\n | \"star\"\n | \"sparkles\"\n | \"lightning\"\n | \"boom\"\n | \"eyes\"\n // Status Indicators\n | \"green_circle\"\n | \"yellow_circle\"\n | \"red_circle\"\n | \"blue_circle\"\n | \"white_circle\"\n | \"black_circle\"\n // Objects & Tools\n | \"rocket\"\n | \"party\"\n | \"confetti\"\n | \"balloon\"\n | \"gift\"\n | \"trophy\"\n | \"medal\"\n | \"lightbulb\"\n | \"gear\"\n | \"wrench\"\n | \"hammer\"\n | \"bug\"\n | \"link\"\n | \"lock\"\n | \"unlock\"\n | \"key\"\n | \"pin\"\n | \"memo\"\n | \"clipboard\"\n | \"calendar\"\n | \"clock\"\n | \"hourglass\"\n | \"bell\"\n | \"megaphone\"\n | \"speech_bubble\"\n | \"email\"\n | \"inbox\"\n | \"outbox\"\n | \"package\"\n | \"folder\"\n | \"file\"\n | \"chart_up\"\n | \"chart_down\"\n | \"coffee\"\n | \"pizza\"\n | \"beer\"\n // Arrows & Directions\n | \"arrow_up\"\n | \"arrow_down\"\n | \"arrow_left\"\n | \"arrow_right\"\n | \"refresh\"\n // Nature & Weather\n | \"sun\"\n | \"cloud\"\n | \"rain\"\n | \"snow\"\n | \"rainbow\";\n\n/**\n * Platform-specific emoji formats for a single emoji.\n */\nexport interface EmojiFormats {\n /** Google Chat unicode emoji, e.g., \"👍\", \"❤️\" */\n gchat: string | string[];\n /** Slack emoji name (without colons), e.g., \"+1\", \"heart\" */\n slack: string | string[];\n}\n\n/**\n * Emoji map type - can be extended by users to add custom emoji.\n *\n * @example\n * ```typescript\n * // Extend with custom emoji\n * declare module \"chat\" {\n * interface CustomEmojiMap {\n * \"custom_emoji\": EmojiFormats;\n * }\n * }\n *\n * const myEmojiMap: EmojiMapConfig = {\n * custom_emoji: { slack: \"custom\", gchat: \"🎯\" },\n * };\n * ```\n */\n// biome-ignore lint/suspicious/noEmptyInterface: Required for TypeScript module augmentation\nexport interface CustomEmojiMap {}\n\n/**\n * Full emoji type including well-known and custom emoji.\n */\nexport type Emoji = WellKnownEmoji | keyof CustomEmojiMap;\n\n/**\n * Configuration for emoji mapping.\n */\nexport type EmojiMapConfig = Partial<Record<Emoji, EmojiFormats>>;\n\n/**\n * Immutable emoji value object with object identity.\n *\n * These objects are singletons - the same emoji name always returns\n * the same frozen object instance, enabling `===` comparison.\n *\n * @example\n * ```typescript\n * // Object identity comparison works\n * if (event.emoji === emoji.thumbs_up) {\n * console.log(\"User gave a thumbs up!\");\n * }\n *\n * // Works in template strings via toString()\n * await thread.post(`${emoji.thumbs_up} Great job!`);\n * ```\n */\nexport interface EmojiValue {\n /** The normalized emoji name (e.g., \"thumbs_up\") */\n readonly name: string;\n /** Returns the placeholder string (for JSON.stringify) */\n toJSON(): string;\n /** Returns the placeholder string for message formatting */\n toString(): string;\n}\n\n/**\n * Reaction event fired when a user adds or removes a reaction.\n */\nexport interface ReactionEvent<TRawMessage = unknown> {\n /** The adapter that received the event */\n adapter: Adapter;\n /** Whether the reaction was added (true) or removed (false) */\n added: boolean;\n /** The normalized emoji as an EmojiValue singleton (enables `===` comparison) */\n emoji: EmojiValue;\n /** The message that was reacted to (if available) */\n message?: Message<TRawMessage>;\n /** The message ID that was reacted to */\n messageId: string;\n /** Platform-specific raw event data */\n raw: unknown;\n /** The raw platform-specific emoji (e.g., \"+1\" for Slack, \"👍\" for GChat) */\n rawEmoji: string;\n /**\n * The thread where the reaction occurred.\n * Use this to post replies or check subscription status.\n *\n * @example\n * ```typescript\n * chat.onReaction([emoji.thumbs_up], async (event) => {\n * await event.thread.post(`Thanks for the ${event.emoji}!`);\n * });\n * ```\n */\n thread: Thread<TRawMessage>;\n /** The thread ID */\n threadId: string;\n /** The user who added/removed the reaction */\n user: Author;\n}\n\n/**\n * Handler for reaction events.\n *\n * @example\n * ```typescript\n * // Handle specific emoji\n * chat.onReaction([\"thumbs_up\", \"heart\"], async (event) => {\n * console.log(`${event.user.userName} ${event.added ? \"added\" : \"removed\"} ${event.emoji}`);\n * });\n *\n * // Handle all reactions\n * chat.onReaction(async (event) => {\n * // ...\n * });\n * ```\n */\nexport type ReactionHandler = (event: ReactionEvent) => void | Promise<void>;\n\n// =============================================================================\n// Action Events (Button Clicks)\n// =============================================================================\n\n/**\n * Action event fired when a user clicks a button in a card.\n *\n * @example\n * ```typescript\n * chat.onAction(\"approve\", async (event) => {\n * await event.thread.post(`Order ${event.value} approved by ${event.user.userName}`);\n * });\n * ```\n */\nexport interface ActionEvent<TRawMessage = unknown> {\n /** The action ID from the button (matches Button's `id` prop) */\n actionId: string;\n /** The adapter that received the event */\n adapter: Adapter;\n /** The message ID containing the card */\n messageId: string;\n /**\n * Open a modal/dialog form in response to this action.\n *\n * @param modal - The modal element to display (JSX or ModalElement)\n * @returns The view/dialog ID, or undefined if modals are not supported\n */\n openModal(\n modal: ModalElement | ChatElement\n ): Promise<{ viewId: string } | undefined>;\n /** Platform-specific raw event data */\n raw: unknown;\n /** The thread where the action occurred (null for view-based actions like home tab buttons) */\n thread: Thread<TRawMessage> | null;\n /** The thread ID */\n threadId: string;\n /** Trigger ID for opening modals (required by some platforms, may expire quickly) */\n triggerId?: string;\n /** User who clicked the button */\n user: Author;\n /** Optional value/payload from the button */\n value?: string;\n}\n\n/**\n * Handler for action events (button clicks in cards).\n *\n * @example\n * ```typescript\n * // Handle specific action\n * chat.onAction(\"approve\", async (event) => {\n * await event.thread.post(\"Approved!\");\n * });\n *\n * // Handle multiple actions\n * chat.onAction([\"approve\", \"reject\"], async (event) => {\n * if (event.actionId === \"approve\") {\n * // ...\n * }\n * });\n *\n * // Handle all actions (catch-all)\n * chat.onAction(async (event) => {\n * console.log(`Action: ${event.actionId}`);\n * });\n * ```\n */\nexport type ActionHandler = (event: ActionEvent) => void | Promise<void>;\n\n// =============================================================================\n// Modal Events (Form Submissions)\n// =============================================================================\n\n/**\n * Event emitted when a user submits a modal form.\n */\nexport interface ModalSubmitEvent<TRawMessage = unknown> {\n /** The adapter that received this event */\n adapter: Adapter;\n /** The callback ID specified when creating the modal */\n callbackId: string;\n /**\n * The private metadata string set when the modal was created.\n * Use this to pass arbitrary context (e.g., JSON) through the modal lifecycle.\n */\n privateMetadata?: string;\n /** Raw platform-specific payload */\n raw: unknown;\n /**\n * The channel where the modal was originally triggered from.\n * Available when the modal was opened via SlashCommandEvent.openModal().\n */\n relatedChannel?: Channel<Record<string, unknown>, TRawMessage>;\n /**\n * The message that contained the action which opened the modal.\n * Available when the modal was opened from a message action via ActionEvent.openModal().\n * This is a SentMessage with edit/delete capabilities.\n */\n relatedMessage?: SentMessage<TRawMessage>;\n /**\n * The thread where the modal was originally triggered from.\n * Available when the modal was opened via ActionEvent.openModal().\n */\n relatedThread?: Thread<Record<string, unknown>, TRawMessage>;\n /** The user who submitted the modal */\n user: Author;\n /** Form field values keyed by input ID */\n values: Record<string, string>;\n /** Platform-specific view/dialog ID */\n viewId: string;\n}\n\n/**\n * Event emitted when a user closes/cancels a modal (requires notifyOnClose).\n */\nexport interface ModalCloseEvent<TRawMessage = unknown> {\n /** The adapter that received this event */\n adapter: Adapter;\n /** The callback ID specified when creating the modal */\n callbackId: string;\n /**\n * The private metadata string set when the modal was created.\n * Use this to pass arbitrary context (e.g., JSON) through the modal lifecycle.\n */\n privateMetadata?: string;\n /** Raw platform-specific payload */\n raw: unknown;\n /**\n * The channel where the modal was originally triggered from.\n * Available when the modal was opened via SlashCommandEvent.openModal().\n */\n relatedChannel?: Channel<Record<string, unknown>, TRawMessage>;\n /**\n * The message that contained the action which opened the modal.\n * Available when the modal was opened from a message action via ActionEvent.openModal().\n * This is a SentMessage with edit/delete capabilities.\n */\n relatedMessage?: SentMessage<TRawMessage>;\n /**\n * The thread where the modal was originally triggered from.\n * Available when the modal was opened via ActionEvent.openModal().\n */\n relatedThread?: Thread<Record<string, unknown>, TRawMessage>;\n /** The user who closed the modal */\n user: Author;\n /** Platform-specific view/dialog ID */\n viewId: string;\n}\n\nexport interface ModalErrorsResponse {\n action: \"errors\";\n errors: Record<string, string>;\n}\n\nexport interface ModalUpdateResponse {\n action: \"update\";\n modal: import(\"./modals\").ModalElement;\n}\n\nexport interface ModalPushResponse {\n action: \"push\";\n modal: import(\"./modals\").ModalElement;\n}\n\nexport interface ModalCloseResponse {\n action: \"close\";\n}\n\nexport type ModalResponse =\n | ModalCloseResponse\n | ModalErrorsResponse\n | ModalUpdateResponse\n | ModalPushResponse;\n\nexport type ModalSubmitHandler = (\n event: ModalSubmitEvent\n // biome-ignore lint/suspicious/noConfusingVoidType: void is needed for sync handlers that return nothing\n) => void | Promise<ModalResponse | undefined>;\n\nexport type ModalCloseHandler = (\n event: ModalCloseEvent\n) => void | Promise<void>;\n\n// =============================================================================\n// Slash Command Events\n// =============================================================================\n\n/**\n * Event emitted when a user invokes a slash command.\n *\n * Slash commands are triggered when a user types `/command` in the message composer.\n * The event provides access to the channel where the command was invoked, allowing\n * you to post responses using standard SDK methods.\n *\n * @example\n * ```typescript\n * chat.onSlashCommand(\"/help\", async (event) => {\n * // Post visible to everyone in the channel\n * await event.channel.post(\"Here are the available commands...\");\n * });\n *\n * chat.onSlashCommand(\"/secret\", async (event) => {\n * // Post ephemeral (only the invoking user sees it)\n * await event.channel.postEphemeral(\n * event.user,\n * \"This is just for you!\",\n * { fallbackToDM: false }\n * );\n * });\n *\n * chat.onSlashCommand(\"/feedback\", async (event) => {\n * // Open a modal\n * await event.openModal({\n * type: \"modal\",\n * callbackId: \"feedback_modal\",\n * title: \"Submit Feedback\",\n * children: [{ type: \"text_input\", id: \"feedback\", label: \"Your feedback\" }],\n * });\n * });\n * ```\n */\nexport interface SlashCommandEvent<TState = Record<string, unknown>> {\n /** The adapter that received this event */\n adapter: Adapter;\n\n /** The channel where the command was invoked */\n channel: Channel<TState>;\n /** The slash command name (e.g., \"/help\") */\n command: string;\n\n /**\n * Open a modal/dialog form in response to this slash command.\n *\n * @param modal - The modal element to display (JSX or ModalElement)\n * @returns The view/dialog ID, or undefined if modals are not supported\n */\n openModal(\n modal: ModalElement | ChatElement\n ): Promise<{ viewId: string } | undefined>;\n\n /** Platform-specific raw payload */\n raw: unknown;\n\n /** Arguments text after the command (e.g., \"topic search\" from \"/help topic search\") */\n text: string;\n\n /** Trigger ID for opening modals (time-limited, typically ~3 seconds) */\n triggerId?: string;\n\n /** The user who invoked the command */\n user: Author;\n}\n\n/**\n * Handler for slash command events.\n *\n * @example\n * ```typescript\n * // Handle a specific command\n * chat.onSlashCommand(\"/status\", async (event) => {\n * await event.channel.post(\"All systems operational!\");\n * });\n *\n * // Handle multiple commands\n * chat.onSlashCommand([\"/help\", \"/info\"], async (event) => {\n * await event.channel.post(`You invoked ${event.command}`);\n * });\n *\n * // Catch-all handler\n * chat.onSlashCommand(async (event) => {\n * console.log(`Command: ${event.command}, Args: ${event.text}`);\n * });\n * ```\n */\nexport type SlashCommandHandler<TState = Record<string, unknown>> = (\n event: SlashCommandEvent<TState>\n) => void | Promise<void>;\n\n// =============================================================================\n// Assistant Events (Slack Assistants API / AI Apps)\n// =============================================================================\n\nexport interface AssistantThreadStartedEvent {\n adapter: Adapter;\n channelId: string;\n context: {\n channelId?: string;\n teamId?: string;\n enterpriseId?: string;\n threadEntryPoint?: string;\n forceSearch?: boolean;\n };\n threadId: string;\n threadTs: string;\n userId: string;\n}\n\nexport type AssistantThreadStartedHandler = (\n event: AssistantThreadStartedEvent\n) => void | Promise<void>;\n\nexport interface AssistantContextChangedEvent {\n adapter: Adapter;\n channelId: string;\n context: {\n channelId?: string;\n teamId?: string;\n enterpriseId?: string;\n threadEntryPoint?: string;\n forceSearch?: boolean;\n };\n threadId: string;\n threadTs: string;\n userId: string;\n}\n\nexport type AssistantContextChangedHandler = (\n event: AssistantContextChangedEvent\n) => void | Promise<void>;\n\nexport interface AppHomeOpenedEvent {\n adapter: Adapter;\n channelId: string;\n userId: string;\n}\n\nexport type AppHomeOpenedHandler = (\n event: AppHomeOpenedEvent\n) => void | Promise<void>;\n\nexport interface MemberJoinedChannelEvent {\n adapter: Adapter;\n channelId: string;\n inviterId?: string;\n userId: string;\n}\n\nexport type MemberJoinedChannelHandler = (\n event: MemberJoinedChannelEvent\n) => void | Promise<void>;\n","import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from \"@workflow/serde\";\nimport type { Root } from \"mdast\";\nimport { cardToFallbackText } from \"./cards\";\nimport { ChannelImpl, deriveChannelId } from \"./channel\";\nimport { getChatSingleton } from \"./chat-singleton\";\nimport { fromFullStream } from \"./from-full-stream\";\nimport { type ChatElement, isJSX, toCardElement } from \"./jsx-runtime\";\nimport {\n paragraph,\n parseMarkdown,\n root,\n text as textNode,\n toPlainText,\n} from \"./markdown\";\nimport { Message, type SerializedMessage } from \"./message\";\nimport { StreamingMarkdownRenderer } from \"./streaming-markdown\";\nimport type {\n Adapter,\n AdapterPostableMessage,\n Attachment,\n Author,\n Channel,\n EphemeralMessage,\n PostableMessage,\n PostEphemeralOptions,\n ScheduledMessage,\n SentMessage,\n StateAdapter,\n StreamChunk,\n StreamEvent,\n StreamOptions,\n Thread,\n} from \"./types\";\nimport { NotImplementedError, THREAD_STATE_TTL_MS } from \"./types\";\n\n/**\n * Serialized thread data for passing to external systems (e.g., workflow engines).\n */\nexport interface SerializedThread {\n _type: \"chat:Thread\";\n adapterName: string;\n channelId: string;\n currentMessage?: SerializedMessage;\n id: string;\n isDM: boolean;\n}\n\n/**\n * Config for creating a ThreadImpl with explicit adapter/state instances.\n */\ninterface ThreadImplConfigWithAdapter {\n adapter: Adapter;\n channelId: string;\n currentMessage?: Message;\n fallbackStreamingPlaceholderText?: string | null;\n id: string;\n initialMessage?: Message;\n isDM?: boolean;\n isSubscribedContext?: boolean;\n stateAdapter: StateAdapter;\n streamingUpdateIntervalMs?: number;\n}\n\n/**\n * Config for creating a ThreadImpl with lazy adapter resolution.\n * The adapter will be looked up from the Chat singleton on first access.\n */\ninterface ThreadImplConfigLazy {\n adapterName: string;\n channelId: string;\n currentMessage?: Message;\n fallbackStreamingPlaceholderText?: string | null;\n id: string;\n initialMessage?: Message;\n isDM?: boolean;\n isSubscribedContext?: boolean;\n streamingUpdateIntervalMs?: number;\n}\n\ntype ThreadImplConfig = ThreadImplConfigWithAdapter | ThreadImplConfigLazy;\n\nfunction isLazyConfig(\n config: ThreadImplConfig\n): config is ThreadImplConfigLazy {\n return \"adapterName\" in config && !(\"adapter\" in config);\n}\n\n/** State key prefix for thread state */\nconst THREAD_STATE_KEY_PREFIX = \"thread-state:\";\n\n/**\n * Check if a value is an AsyncIterable (like AI SDK's textStream or fullStream).\n */\nfunction isAsyncIterable(\n value: unknown\n): value is AsyncIterable<string | StreamChunk | StreamEvent> {\n return (\n value !== null && typeof value === \"object\" && Symbol.asyncIterator in value\n );\n}\n\nexport class ThreadImpl<TState = Record<string, unknown>>\n implements Thread<TState>\n{\n readonly id: string;\n readonly channelId: string;\n readonly isDM: boolean;\n\n /** Direct adapter instance (if provided) */\n private _adapter?: Adapter;\n /** Adapter name for lazy resolution */\n private readonly _adapterName?: string;\n /** Direct state adapter instance (if provided) */\n private _stateAdapterInstance?: StateAdapter;\n private _recentMessages: Message[] = [];\n private readonly _isSubscribedContext: boolean;\n /** Current message context for streaming - provides userId/teamId */\n private readonly _currentMessage?: Message;\n /** Update interval for fallback streaming */\n private readonly _streamingUpdateIntervalMs: number;\n /** Placeholder text for fallback streaming (post + edit) */\n private readonly _fallbackStreamingPlaceholderText: string | null;\n /** Cached channel instance */\n private _channel?: Channel<TState>;\n\n constructor(config: ThreadImplConfig) {\n this.id = config.id;\n this.channelId = config.channelId;\n this.isDM = config.isDM ?? false;\n this._isSubscribedContext = config.isSubscribedContext ?? false;\n this._currentMessage = config.currentMessage;\n this._streamingUpdateIntervalMs = config.streamingUpdateIntervalMs ?? 500;\n this._fallbackStreamingPlaceholderText =\n config.fallbackStreamingPlaceholderText !== undefined\n ? config.fallbackStreamingPlaceholderText\n : \"...\";\n\n if (isLazyConfig(config)) {\n // Lazy resolution mode - store adapter name for later lookup\n this._adapterName = config.adapterName;\n } else {\n // Direct mode - store adapter and state instances\n this._adapter = config.adapter;\n this._stateAdapterInstance = config.stateAdapter;\n }\n\n if (config.initialMessage) {\n this._recentMessages = [config.initialMessage];\n }\n }\n\n /**\n * Get the adapter for this thread.\n * If created with lazy config, resolves from Chat singleton on first access.\n */\n get adapter(): Adapter {\n if (this._adapter) {\n return this._adapter;\n }\n\n if (!this._adapterName) {\n throw new Error(\"Thread has no adapter configured\");\n }\n\n // Lazy resolution from singleton\n const chat = getChatSingleton();\n const adapter = chat.getAdapter(this._adapterName);\n if (!adapter) {\n throw new Error(\n `Adapter \"${this._adapterName}\" not found in Chat singleton`\n );\n }\n\n // Cache for subsequent accesses\n this._adapter = adapter;\n return adapter;\n }\n\n /**\n * Get the state adapter for this thread.\n * If created with lazy config, resolves from Chat singleton on first access.\n */\n private get _stateAdapter(): StateAdapter {\n if (this._stateAdapterInstance) {\n return this._stateAdapterInstance;\n }\n\n // Lazy resolution from singleton\n const chat = getChatSingleton();\n this._stateAdapterInstance = chat.getState();\n return this._stateAdapterInstance;\n }\n\n get recentMessages(): Message[] {\n return this._recentMessages;\n }\n\n set recentMessages(messages: Message[]) {\n this._recentMessages = messages;\n }\n\n /**\n * Get the current thread state.\n * Returns null if no state has been set.\n */\n get state(): Promise<TState | null> {\n return this._stateAdapter.get<TState>(\n `${THREAD_STATE_KEY_PREFIX}${this.id}`\n );\n }\n\n /**\n * Set the thread state. Merges with existing state by default.\n * State is persisted for 30 days.\n */\n async setState(\n newState: Partial<TState>,\n options?: { replace?: boolean }\n ): Promise<void> {\n const key = `${THREAD_STATE_KEY_PREFIX}${this.id}`;\n\n if (options?.replace) {\n // Replace entire state\n await this._stateAdapter.set(key, newState, THREAD_STATE_TTL_MS);\n } else {\n // Merge with existing state\n const existing = await this._stateAdapter.get<TState>(key);\n const merged = { ...existing, ...newState };\n await this._stateAdapter.set(key, merged, THREAD_STATE_TTL_MS);\n }\n }\n\n /**\n * Get the Channel containing this thread.\n * Lazy-created and cached.\n */\n get channel(): Channel<TState> {\n if (!this._channel) {\n const channelId = deriveChannelId(this.adapter, this.id);\n this._channel = new ChannelImpl<TState>({\n id: channelId,\n adapter: this.adapter,\n stateAdapter: this._stateAdapter,\n isDM: this.isDM,\n });\n }\n return this._channel;\n }\n\n /**\n * Iterate messages newest first (backward from most recent).\n * Auto-paginates lazily.\n */\n get messages(): AsyncIterable<Message> {\n const adapter = this.adapter;\n const threadId = this.id;\n\n return {\n async *[Symbol.asyncIterator]() {\n let cursor: string | undefined;\n\n while (true) {\n const result = await adapter.fetchMessages(threadId, {\n cursor,\n direction: \"backward\",\n });\n\n // Messages within a page are chronological (oldest first),\n // but we want newest first, so reverse the page\n const reversed = [...result.messages].reverse();\n for (const message of reversed) {\n yield message;\n }\n\n if (!result.nextCursor || result.messages.length === 0) {\n break;\n }\n\n cursor = result.nextCursor;\n }\n },\n };\n }\n\n get allMessages(): AsyncIterable<Message> {\n const adapter = this.adapter;\n const threadId = this.id;\n\n return {\n async *[Symbol.asyncIterator]() {\n let cursor: string | undefined;\n\n while (true) {\n // Use forward direction to iterate from oldest to newest\n const result = await adapter.fetchMessages(threadId, {\n limit: 100,\n cursor,\n direction: \"forward\",\n });\n\n for (const message of result.messages) {\n yield message;\n }\n\n // No more pages if no nextCursor or no messages returned\n if (!result.nextCursor || result.messages.length === 0) {\n break;\n }\n\n cursor = result.nextCursor;\n }\n },\n };\n }\n\n async isSubscribed(): Promise<boolean> {\n // Short-circuit if we know we're in a subscribed context\n if (this._isSubscribedContext) {\n return true;\n }\n return this._stateAdapter.isSubscribed(this.id);\n }\n\n async subscribe(): Promise<void> {\n await this._stateAdapter.subscribe(this.id);\n // Allow adapters to set up platform-specific subscriptions\n if (this.adapter.onThreadSubscribe) {\n await this.adapter.onThreadSubscribe(this.id);\n }\n }\n\n async unsubscribe(): Promise<void> {\n await this._stateAdapter.unsubscribe(this.id);\n }\n\n async post(\n message: string | PostableMessage | ChatElement\n ): Promise<SentMessage> {\n // Handle AsyncIterable (streaming)\n if (isAsyncIterable(message)) {\n return this.handleStream(message);\n }\n\n // After filtering out streams, we have an AdapterPostableMessage\n // Auto-convert JSX elements to CardElement\n let postable: string | AdapterPostableMessage = message as\n | string\n | AdapterPostableMessage;\n if (isJSX(message)) {\n const card = toCardElement(message);\n if (!card) {\n throw new Error(\"Invalid JSX element: must be a Card element\");\n }\n postable = card;\n }\n\n const rawMessage = await this.adapter.postMessage(this.id, postable);\n\n // Create a SentMessage with edit/delete capabilities\n const result = this.createSentMessage(\n rawMessage.id,\n postable,\n rawMessage.threadId\n );\n return result;\n }\n\n async postEphemeral(\n user: string | Author,\n message: AdapterPostableMessage | ChatElement,\n options: PostEphemeralOptions\n ): Promise<EphemeralMessage | null> {\n const { fallbackToDM } = options;\n const userId = typeof user === \"string\" ? user : user.userId;\n\n // Convert JSX to card if needed\n let postable: AdapterPostableMessage;\n if (isJSX(message)) {\n const card = toCardElement(message);\n if (!card) {\n throw new Error(\"Invalid JSX element: must be a Card element\");\n }\n postable = card;\n } else {\n // Safe cast: if not JSX, it must be AdapterPostableMessage\n postable = message as AdapterPostableMessage;\n }\n\n // Try native ephemeral if adapter supports it\n if (this.adapter.postEphemeral) {\n return this.adapter.postEphemeral(this.id, userId, postable);\n }\n\n // No native support - either fallback to DM or return null\n if (!fallbackToDM) {\n return null;\n }\n\n // Fallback: send via DM\n if (this.adapter.openDM) {\n const dmThreadId = await this.adapter.openDM(userId);\n const result = await this.adapter.postMessage(dmThreadId, postable);\n return {\n id: result.id,\n threadId: dmThreadId,\n usedFallback: true,\n raw: result.raw,\n };\n }\n\n // No DM support either - return null\n return null;\n }\n\n async schedule(\n message: AdapterPostableMessage | ChatElement,\n options: { postAt: Date }\n ): Promise<ScheduledMessage> {\n // Convert JSX to card if needed\n let postable: AdapterPostableMessage;\n if (isJSX(message)) {\n const card = toCardElement(message);\n if (!card) {\n throw new Error(\"Invalid JSX element: must be a Card element\");\n }\n postable = card;\n } else {\n postable = message as AdapterPostableMessage;\n }\n\n if (!this.adapter.scheduleMessage) {\n throw new NotImplementedError(\n \"Scheduled messages are not supported by this adapter\",\n \"scheduling\"\n );\n }\n\n return this.adapter.scheduleMessage(this.id, postable, options);\n }\n\n /**\n * Handle streaming from an AsyncIterable.\n * Normalizes the stream (supports both textStream and fullStream from AI SDK),\n * then uses adapter's native streaming if available, otherwise falls back to post+edit.\n */\n private async handleStream(\n rawStream: AsyncIterable<string | StreamChunk | StreamEvent>\n ): Promise<SentMessage> {\n // Normalize: handles plain strings, AI SDK fullStream events, and StreamChunk objects\n const textStream = fromFullStream(rawStream);\n // Build streaming options from current message context\n const options: StreamOptions = {};\n if (this._currentMessage) {\n options.recipientUserId = this._currentMessage.author.userId;\n // Extract teamId from raw Slack payload\n const raw = this._currentMessage.raw as {\n team_id?: string;\n team?: string;\n };\n options.recipientTeamId = raw?.team_id ?? raw?.team;\n }\n\n // Use native streaming if adapter supports it\n if (this.adapter.stream) {\n // Wrap stream to collect accumulated text while passing through to adapter.\n // StreamChunk objects are passed through; only plain strings are accumulated.\n let accumulated = \"\";\n const wrappedStream: AsyncIterable<string | StreamChunk> = {\n [Symbol.asyncIterator]: () => {\n const iterator = textStream[Symbol.asyncIterator]();\n return {\n async next() {\n const result = await iterator.next();\n if (!result.done) {\n const value = result.value;\n if (typeof value === \"string\") {\n accumulated += value;\n } else if (value.type === \"markdown_text\") {\n accumulated += value.text;\n }\n // task_update and plan_update chunks don't contribute to accumulated text\n }\n return result;\n },\n };\n },\n };\n\n const raw = await this.adapter.stream(this.id, wrappedStream, options);\n return this.createSentMessage(\n raw.id,\n { markdown: accumulated },\n raw.threadId\n );\n }\n\n // Fallback: post + edit with throttling.\n // Extract only text content from the mixed stream for adapters without native streaming.\n const textOnlyStream: AsyncIterable<string> = {\n [Symbol.asyncIterator]: () => {\n const iterator = textStream[Symbol.asyncIterator]();\n return {\n async next(): Promise<IteratorResult<string>> {\n while (true) {\n const result = await iterator.next();\n if (result.done) {\n return { value: undefined as unknown as string, done: true };\n }\n const value = result.value;\n if (typeof value === \"string\") {\n return { value, done: false };\n }\n if (value.type === \"markdown_text\") {\n return { value: value.text, done: false };\n }\n // Skip non-text chunks (task_update, plan_update) in fallback mode\n }\n },\n };\n },\n };\n return this.fallbackStream(textOnlyStream, options);\n }\n\n async startTyping(status?: string): Promise<void> {\n await this.adapter.startTyping(this.id, status);\n }\n\n /**\n * Fallback streaming implementation using post + edit.\n * Used when adapter doesn't support native streaming.\n * Uses recursive setTimeout to send updates every intervalMs (default 500ms).\n * Schedules next update only after current edit completes to avoid overwhelming slow services.\n */\n private async fallbackStream(\n textStream: AsyncIterable<string>,\n options?: StreamOptions\n ): Promise<SentMessage> {\n const intervalMs =\n options?.updateIntervalMs ?? this._streamingUpdateIntervalMs;\n const placeholderText = this._fallbackStreamingPlaceholderText;\n let msg: { id: string; threadId: string; raw: unknown } | null =\n placeholderText === null\n ? null\n : await this.adapter.postMessage(this.id, placeholderText);\n let threadIdForEdits = this.id;\n const renderer = new StreamingMarkdownRenderer();\n let lastEditContent = \"\";\n let stopped = false;\n let pendingEdit: Promise<void> | null = null;\n let timerId: ReturnType<typeof setTimeout> | null = null;\n\n if (msg) {\n threadIdForEdits = msg.threadId || this.id;\n lastEditContent = placeholderText ?? \"\";\n }\n\n const scheduleNextEdit = (): void => {\n timerId = setTimeout(() => {\n pendingEdit = doEditAndReschedule();\n }, intervalMs);\n };\n\n const doEditAndReschedule = async (): Promise<void> => {\n if (stopped || !msg) {\n return;\n }\n\n const content = renderer.render();\n if (content !== lastEditContent) {\n try {\n await this.adapter.editMessage(threadIdForEdits, msg.id, {\n markdown: content,\n });\n lastEditContent = content;\n } catch {\n // Ignore errors, continue\n }\n }\n\n // Schedule next check after intervalMs (only after edit completes)\n if (!stopped) {\n scheduleNextEdit();\n }\n };\n\n if (msg) {\n scheduleNextEdit();\n }\n\n try {\n for await (const chunk of textStream) {\n renderer.push(chunk);\n if (!msg) {\n const content = renderer.render();\n msg = await this.adapter.postMessage(this.id, {\n markdown: content,\n });\n threadIdForEdits = msg.threadId || this.id;\n lastEditContent = content;\n scheduleNextEdit();\n }\n }\n } finally {\n stopped = true;\n if (timerId) {\n clearTimeout(timerId);\n timerId = null;\n }\n }\n\n // Wait for any pending edit to complete\n if (pendingEdit) {\n await pendingEdit;\n }\n\n const accumulated = renderer.getText();\n const finalContent = renderer.finish();\n\n if (!msg) {\n msg = await this.adapter.postMessage(this.id, {\n markdown: accumulated,\n });\n threadIdForEdits = msg.threadId || this.id;\n lastEditContent = accumulated;\n }\n\n if (finalContent !== lastEditContent) {\n await this.adapter.editMessage(threadIdForEdits, msg.id, {\n markdown: accumulated,\n });\n }\n\n return this.createSentMessage(\n msg.id,\n { markdown: accumulated },\n threadIdForEdits\n );\n }\n\n async refresh(): Promise<void> {\n const result = await this.adapter.fetchMessages(this.id, { limit: 50 });\n this._recentMessages = result.messages;\n }\n\n mentionUser(userId: string): string {\n return `<@${userId}>`;\n }\n\n /**\n * Serialize the thread to a plain JSON object.\n * Use this to pass thread data to external systems like workflow engines.\n *\n * @example\n * ```typescript\n * // Pass to a workflow\n * await workflow.start(\"my-workflow\", {\n * thread: thread.toJSON(),\n * message: serializeMessage(message),\n * });\n * ```\n */\n toJSON(): SerializedThread {\n return {\n _type: \"chat:Thread\",\n id: this.id,\n channelId: this.channelId,\n currentMessage: this._currentMessage?.toJSON(),\n isDM: this.isDM,\n adapterName: this.adapter.name,\n };\n }\n\n /**\n * Reconstruct a Thread from serialized JSON data.\n *\n * Reconstructs a ThreadImpl from serialized data.\n * Uses lazy resolution from Chat.getSingleton() for adapter and state.\n *\n * @param json - Serialized thread data\n * @requires Call `chat.registerSingleton()` before deserializing threads\n *\n * @example\n * ```typescript\n * const thread = ThreadImpl.fromJSON(serializedThread);\n * ```\n */\n static fromJSON<TState = Record<string, unknown>>(\n json: SerializedThread,\n adapter?: Adapter\n ): ThreadImpl<TState> {\n const thread = new ThreadImpl<TState>({\n id: json.id,\n adapterName: json.adapterName,\n channelId: json.channelId,\n currentMessage: json.currentMessage\n ? Message.fromJSON(json.currentMessage)\n : undefined,\n isDM: json.isDM,\n });\n if (adapter) {\n thread._adapter = adapter;\n }\n return thread;\n }\n\n /**\n * Serialize a ThreadImpl instance for @workflow/serde.\n * This static method is automatically called by workflow serialization.\n */\n static [WORKFLOW_SERIALIZE](instance: ThreadImpl): SerializedThread {\n return instance.toJSON();\n }\n\n /**\n * Deserialize a ThreadImpl from @workflow/serde.\n * Uses lazy adapter resolution from Chat.getSingleton().\n * Requires chat.registerSingleton() to have been called.\n */\n static [WORKFLOW_DESERIALIZE](data: SerializedThread): ThreadImpl {\n return ThreadImpl.fromJSON(data);\n }\n\n private createSentMessage(\n messageId: string,\n postable: AdapterPostableMessage,\n threadIdOverride?: string\n ): SentMessage {\n const adapter = this.adapter;\n // Use the threadId returned by postMessage if available (may differ after thread creation)\n const threadId = threadIdOverride || this.id;\n const self = this;\n\n // Extract text and AST from the PostableMessage\n const { plainText, formatted, attachments } =\n extractMessageContent(postable);\n\n const sentMessage: SentMessage = {\n id: messageId,\n threadId,\n text: plainText,\n formatted,\n raw: null, // Will be populated if needed\n author: {\n userId: \"self\",\n userName: adapter.userName,\n fullName: adapter.userName,\n isBot: true,\n isMe: true,\n },\n metadata: {\n dateSent: new Date(),\n edited: false,\n },\n attachments,\n\n toJSON() {\n return new Message(this).toJSON();\n },\n\n async edit(\n newContent: string | PostableMessage | ChatElement\n ): Promise<SentMessage> {\n // Auto-convert JSX elements to CardElement\n // edit doesn't support streaming, so use AdapterPostableMessage\n let postable: string | AdapterPostableMessage = newContent as\n | string\n | AdapterPostableMessage;\n if (isJSX(newContent)) {\n const card = toCardElement(newContent);\n if (!card) {\n throw new Error(\"Invalid JSX element: must be a Card element\");\n }\n postable = card;\n }\n await adapter.editMessage(threadId, messageId, postable);\n return self.createSentMessage(messageId, postable);\n },\n\n async delete(): Promise<void> {\n await adapter.deleteMessage(threadId, messageId);\n },\n\n async addReaction(emoji: string): Promise<void> {\n await adapter.addReaction(threadId, messageId, emoji);\n },\n\n async removeReaction(emoji: string): Promise<void> {\n await adapter.removeReaction(threadId, messageId, emoji);\n },\n };\n\n return sentMessage;\n }\n\n createSentMessageFromMessage(message: Message): SentMessage {\n const adapter = this.adapter;\n const threadId = this.id;\n const messageId = message.id;\n const self = this;\n\n return {\n id: message.id,\n threadId: message.threadId,\n text: message.text,\n formatted: message.formatted,\n raw: message.raw,\n author: message.author,\n metadata: message.metadata,\n attachments: message.attachments,\n isMention: message.isMention,\n\n toJSON() {\n return message.toJSON();\n },\n\n async edit(\n newContent: string | PostableMessage | ChatElement\n ): Promise<SentMessage> {\n let postable: string | AdapterPostableMessage = newContent as\n | string\n | AdapterPostableMessage;\n if (isJSX(newContent)) {\n const card = toCardElement(newContent);\n if (!card) {\n throw new Error(\"Invalid JSX element: must be a Card element\");\n }\n postable = card;\n }\n await adapter.editMessage(threadId, messageId, postable);\n return self.createSentMessage(messageId, postable, threadId);\n },\n\n async delete(): Promise<void> {\n await adapter.deleteMessage(threadId, messageId);\n },\n\n async addReaction(emoji: string): Promise<void> {\n await adapter.addReaction(threadId, messageId, emoji);\n },\n\n async removeReaction(emoji: string): Promise<void> {\n await adapter.removeReaction(threadId, messageId, emoji);\n },\n };\n }\n}\n\n/**\n * Extract plain text, AST, and attachments from a message.\n */\nfunction extractMessageContent(message: AdapterPostableMessage): {\n plainText: string;\n formatted: Root;\n attachments: Attachment[];\n} {\n if (typeof message === \"string\") {\n // Raw string - create simple AST\n return {\n plainText: message,\n formatted: root([paragraph([textNode(message)])]),\n attachments: [],\n };\n }\n\n if (\"raw\" in message) {\n // Raw text - create simple AST\n return {\n plainText: message.raw,\n formatted: root([paragraph([textNode(message.raw)])]),\n attachments: message.attachments || [],\n };\n }\n\n if (\"markdown\" in message) {\n // Markdown - parse to AST\n const ast = parseMarkdown(message.markdown);\n return {\n plainText: toPlainText(ast),\n formatted: ast,\n attachments: message.attachments || [],\n };\n }\n\n if (\"ast\" in message) {\n // AST provided directly\n return {\n plainText: toPlainText(message.ast),\n formatted: message.ast,\n attachments: message.attachments || [],\n };\n }\n\n if (\"card\" in message) {\n // PostableCard - generate fallback text from card\n const fallbackText =\n message.fallbackText || cardToFallbackText(message.card);\n return {\n plainText: fallbackText,\n formatted: root([paragraph([textNode(fallbackText)])]),\n attachments: [],\n };\n }\n\n if (\"type\" in message && message.type === \"card\") {\n // Direct CardElement\n const fallbackText = cardToFallbackText(message);\n return {\n plainText: fallbackText,\n formatted: root([paragraph([textNode(fallbackText)])]),\n attachments: [],\n };\n }\n\n // Should never reach here with proper typing\n throw new Error(\"Invalid PostableMessage format\");\n}\n","import type { StreamChunk } from \"./types\";\n\nconst STREAM_CHUNK_TYPES = new Set([\n \"markdown_text\",\n \"task_update\",\n \"plan_update\",\n]);\n\n/**\n * Normalizes an async iterable stream for use with `thread.post()`.\n *\n * Handles three stream types automatically:\n * - **Text streams** (`AsyncIterable<string>`, e.g. AI SDK `textStream`) —\n * passed through as-is.\n * - **Full streams** (`AsyncIterable<object>`, e.g. AI SDK `fullStream`) —\n * extracts `text-delta` events and injects `\"\\n\\n\"` separators between\n * steps so that multi-step agent output reads naturally.\n * - **StreamChunk objects** (`task_update`, `plan_update`, `markdown_text`) —\n * passed through as-is for adapters with native structured chunk support.\n *\n * This is used internally by `thread.post()`, so you can pass either stream\n * directly:\n * ```ts\n * await thread.post(result.fullStream); // auto-detected\n * await thread.post(result.textStream); // still works\n * ```\n */\nexport async function* fromFullStream(\n stream: AsyncIterable<unknown>\n): AsyncIterable<string | StreamChunk> {\n let needsSeparator = false;\n let hasEmittedText = false;\n\n for await (const event of stream) {\n // Plain string chunk (e.g. from AI SDK textStream)\n if (typeof event === \"string\") {\n yield event;\n continue;\n }\n\n // Skip non-objects\n if (event === null || typeof event !== \"object\" || !(\"type\" in event)) {\n continue;\n }\n const typed = event as {\n delta?: unknown;\n text?: unknown;\n textDelta?: unknown;\n type: string;\n };\n\n // Pass through StreamChunk objects (task_update, plan_update, markdown_text)\n if (STREAM_CHUNK_TYPES.has(typed.type)) {\n yield event as StreamChunk;\n continue;\n }\n\n // AI SDK v5 uses `textDelta`, v6 uses `text`\n const textContent = typed.text ?? typed.delta ?? typed.textDelta;\n if (typed.type === \"text-delta\" && typeof textContent === \"string\") {\n if (needsSeparator && hasEmittedText) {\n yield \"\\n\\n\";\n }\n needsSeparator = false;\n hasEmittedText = true;\n yield textContent;\n } else if (typed.type === \"step-finish\") {\n needsSeparator = true;\n }\n }\n}\n","import remend from \"remend\";\n\n/**\n * A streaming markdown renderer that buffers potential table headers\n * until confirmed by a separator line, preventing tables from flashing\n * as raw pipe-delimited text during LLM streaming.\n *\n * Outputs markdown (not platform text). Format conversion still happens\n * in the adapter's editMessage → renderPostable → fromAst pipeline.\n */\nexport class StreamingMarkdownRenderer {\n private accumulated = \"\";\n private dirty = true;\n private cachedRender = \"\";\n private finished = false;\n /** Number of code fence toggles from completed lines (odd = inside). */\n private fenceToggles = 0;\n /** Incomplete trailing line buffer for incremental fence tracking. */\n private incompleteLine = \"\";\n\n /** Append a chunk from the LLM stream. */\n push(chunk: string): void {\n this.accumulated += chunk;\n this.dirty = true;\n\n // Incrementally track code fence state from completed lines\n this.incompleteLine += chunk;\n const parts = this.incompleteLine.split(\"\\n\");\n this.incompleteLine = parts.pop() ?? \"\";\n for (const line of parts) {\n const trimmed = line.trimStart();\n if (trimmed.startsWith(\"```\") || trimmed.startsWith(\"~~~\")) {\n this.fenceToggles++;\n }\n }\n }\n\n /** O(1) check if accumulated text is inside an unclosed code fence. */\n private isAccumulatedInsideFence(): boolean {\n let inside = this.fenceToggles % 2 === 1;\n const trimmed = this.incompleteLine.trimStart();\n if (trimmed.startsWith(\"```\") || trimmed.startsWith(\"~~~\")) {\n inside = !inside;\n }\n return inside;\n }\n\n /**\n * Get renderable markdown for an intermediate edit.\n * - Holds back trailing lines that look like a table header (|...|)\n * until a separator line (|---|---|) confirms or the next line denies.\n * - Applies remend() to close incomplete inline markers.\n * - Idempotent: returns cached result if no push() since last call.\n */\n render(): string {\n if (!this.dirty) {\n return this.cachedRender;\n }\n\n this.dirty = false;\n\n if (this.finished) {\n this.cachedRender = remend(this.accumulated);\n return this.cachedRender;\n }\n\n // If inside an unclosed code fence, don't buffer (pipes aren't tables)\n if (this.isAccumulatedInsideFence()) {\n this.cachedRender = remend(this.accumulated);\n return this.cachedRender;\n }\n\n const committable = getCommittablePrefix(this.accumulated);\n this.cachedRender = remend(committable);\n return this.cachedRender;\n }\n\n /**\n * Get text safe for append-only streaming (e.g. Slack native streaming).\n *\n * - Holds back unconfirmed table headers until separator arrives.\n * - Wraps confirmed tables in code fences so pipes render as literal\n * text (not broken mrkdwn). The code fence is left OPEN while\n * the table is still streaming, keeping output monotonic for deltas.\n * - Holds back unclosed inline markers (**, *, ~~, `, [).\n * - The final editMessage replaces everything with properly formatted text.\n */\n getCommittableText(): string {\n if (this.finished) {\n return wrapTablesForAppend(this.accumulated, true);\n }\n\n // Strip incomplete last line (no trailing newline) to prevent committing\n // content that might change semantics when completed — e.g. \"| A\" could\n // become \"| A | B |\" which is a table row that should be held back.\n let text = this.accumulated;\n if (text.length > 0 && !text.endsWith(\"\\n\")) {\n const lastNewline = text.lastIndexOf(\"\\n\");\n const withoutIncompleteLine =\n lastNewline >= 0 ? text.slice(0, lastNewline + 1) : \"\";\n\n // If stripping puts us inside a code fence, keep the incomplete line\n // (it's likely the closing fence being typed — content is literal).\n if (isInsideCodeFence(withoutIncompleteLine)) {\n // Still wrap preceding tables for consistent coordinate space.\n return wrapTablesForAppend(text);\n }\n\n text = withoutIncompleteLine;\n }\n\n // Inside a user code fence: skip table holding and inline marker buffering\n // (pipes and markers are literal inside fences), but still wrap preceding\n // confirmed tables for consistent coordinate space.\n if (isInsideCodeFence(text)) {\n return wrapTablesForAppend(text);\n }\n\n const committed = getCommittablePrefix(text);\n const wrapped = wrapTablesForAppend(committed);\n\n // If text ends inside an open table code fence,\n // skip inline marker buffering — markers in code blocks are literal\n if (isInsideCodeFence(wrapped)) {\n return wrapped;\n }\n\n return findCleanPrefix(wrapped);\n }\n\n /** Raw accumulated text (no remend, no buffering). For the final edit. */\n getText(): string {\n return this.accumulated;\n }\n\n /** Signal stream end. Flushes held-back lines. Returns final render. */\n finish(): string {\n this.finished = true;\n this.dirty = true;\n return this.render();\n }\n}\n\n/**\n * Characters that can open an inline markdown construct.\n * Used to find the cut point when text has unclosed markers.\n */\nconst INLINE_MARKER_CHARS = new Set([\"*\", \"~\", \"`\", \"[\"]);\n\n/**\n * Check if text is \"clean\" — remend doesn't add any closing markers.\n * Uses length comparison because remend may trim trailing whitespace\n * from otherwise clean text (which is harmless for streaming).\n */\nfunction isClean(text: string): boolean {\n return remend(text).length <= text.length;\n}\n\n/**\n * Returns the longest prefix of `text` where all inline markers are balanced\n * (i.e. remend would not add closing markers). Scans backward from the end\n * for potential opening markers, grouping consecutive same characters to\n * handle multi-char markers like ** and ~~.\n *\n * Typically resolves in 1-3 remend calls since unclosed markers are\n * almost always near the end of the text.\n */\nfunction findCleanPrefix(text: string): string {\n if (text.length === 0 || isClean(text)) {\n return text;\n }\n\n for (let i = text.length - 1; i >= 0; i--) {\n if (INLINE_MARKER_CHARS.has(text[i])) {\n // Group consecutive same characters (e.g., ** or ~~)\n while (i > 0 && text[i - 1] === text[i]) {\n i--;\n }\n const candidate = text.slice(0, i);\n if (isClean(candidate)) {\n return candidate;\n }\n }\n }\n\n return \"\";\n}\n\nconst TABLE_ROW_RE = /^\\|.*\\|$/;\nconst TABLE_SEPARATOR_RE = /^\\|[\\s:]*-{1,}[\\s:]*(\\|[\\s:]*-{1,}[\\s:]*)*\\|$/;\n\n/**\n * Check if the text ends inside an unclosed code fence.\n * Counts the number of ``` (or ~~~) fence openers/closers.\n */\nfunction isInsideCodeFence(text: string): boolean {\n let inside = false;\n for (const line of text.split(\"\\n\")) {\n const trimmed = line.trimStart();\n if (trimmed.startsWith(\"```\") || trimmed.startsWith(\"~~~\")) {\n inside = !inside;\n }\n }\n return inside;\n}\n\n/**\n * Returns the prefix of `text` that can be safely rendered,\n * holding back trailing lines that look like an unconfirmed table.\n *\n * A table is \"confirmed\" when a separator line (|---|---|) appears\n * after a header row. Until then, potential table rows at the end\n * of the text are withheld.\n */\nfunction getCommittablePrefix(text: string): string {\n // Split into lines, keeping track of whether the text ends with a newline\n const endsWithNewline = text.endsWith(\"\\n\");\n const lines = text.split(\"\\n\");\n\n // If the text doesn't end with newline, the last line is still being\n // written to. Remove it from consideration for table detection.\n if (!endsWithNewline && lines.length > 0) {\n lines.pop();\n }\n\n // Remove trailing empty string from split (if text ends with \\n)\n if (endsWithNewline && lines.length > 0 && lines.at(-1) === \"\") {\n lines.pop();\n }\n\n // Walk backward to find consecutive table-like lines at the end\n let heldCount = 0;\n let separatorFound = false;\n\n for (let i = lines.length - 1; i >= 0; i--) {\n const trimmed = lines[i].trim();\n\n // Empty line breaks a table block\n if (trimmed === \"\") {\n break;\n }\n\n if (TABLE_SEPARATOR_RE.test(trimmed)) {\n // Separator found — table is confirmed from here upward\n separatorFound = true;\n break;\n }\n\n if (TABLE_ROW_RE.test(trimmed)) {\n heldCount++;\n } else {\n // Non-table line breaks the run\n break;\n }\n }\n\n if (separatorFound || heldCount === 0) {\n // Table confirmed or no table-like lines — commit everything\n return text;\n }\n\n // Hold back the trailing table-like lines\n const commitLineCount = lines.length - heldCount;\n const committedLines = lines.slice(0, commitLineCount);\n\n // Reconstruct: committed lines + trailing newline\n let result = committedLines.join(\"\\n\");\n // Preserve a trailing newline if there were committed lines\n if (committedLines.length > 0) {\n result += \"\\n\";\n }\n\n return result;\n}\n\n/**\n * Wraps confirmed GFM table blocks in code fences for append-only streaming.\n *\n * Append-only APIs (e.g. Slack streaming) can't render GFM tables natively.\n * Wrapping in a code fence makes pipes display as readable literal text.\n *\n * The code fence is left OPEN if the table is ongoing (no closing ```)\n * so that output remains monotonic — each new row just extends the block.\n * The fence is closed when a non-table line follows.\n */\nfunction wrapTablesForAppend(text: string, closeFences = false): string {\n const hadTrailingNewline = text.endsWith(\"\\n\");\n const lines = text.split(\"\\n\");\n\n // Remove trailing empty string from split (artifact of trailing newline)\n if (hadTrailingNewline && lines.length > 0 && lines.at(-1) === \"\") {\n lines.pop();\n }\n\n const result: string[] = [];\n let inTable = false;\n let inUserCodeFence = false;\n\n for (let i = 0; i < lines.length; i++) {\n const trimmed = lines[i].trim();\n\n // Track existing code fences in the source markdown.\n // Don't detect tables inside user code fences.\n if (!inTable && (trimmed.startsWith(\"```\") || trimmed.startsWith(\"~~~\"))) {\n inUserCodeFence = !inUserCodeFence;\n result.push(lines[i]);\n continue;\n }\n\n if (inUserCodeFence) {\n result.push(lines[i]);\n continue;\n }\n\n const isTableLine =\n trimmed !== \"\" &&\n (TABLE_ROW_RE.test(trimmed) || TABLE_SEPARATOR_RE.test(trimmed));\n\n if (isTableLine && !inTable) {\n // Only wrap if this block has a separator (confirmed table)\n let hasSeparator = false;\n for (let j = i; j < lines.length; j++) {\n const t = lines[j].trim();\n if (TABLE_SEPARATOR_RE.test(t)) {\n hasSeparator = true;\n break;\n }\n if (t === \"\" || !TABLE_ROW_RE.test(t)) {\n break;\n }\n }\n if (hasSeparator) {\n result.push(\"```\");\n inTable = true;\n }\n } else if (!isTableLine && inTable) {\n result.push(\"```\");\n inTable = false;\n }\n\n result.push(lines[i]);\n }\n\n // Close the fence if requested (e.g. after stream finishes)\n if (inTable && closeFences) {\n result.push(\"```\");\n }\n // Otherwise if inTable is true, the code fence is intentionally left OPEN\n // for monotonic appending — it'll be closed when the table ends.\n\n let output = result.join(\"\\n\");\n if (hadTrailingNewline) {\n output += \"\\n\";\n }\n return output;\n}\n","import { ChannelImpl, type SerializedChannel } from \"./channel\";\nimport {\n getChatSingleton,\n hasChatSingleton,\n setChatSingleton,\n} from \"./chat-singleton\";\nimport { isJSX, toModalElement } from \"./jsx-runtime\";\nimport { Message, type SerializedMessage } from \"./message\";\nimport type { ModalElement } from \"./modals\";\nimport { type SerializedThread, ThreadImpl } from \"./thread\";\nimport type {\n ActionEvent,\n ActionHandler,\n Adapter,\n AppHomeOpenedEvent,\n AppHomeOpenedHandler,\n AssistantContextChangedEvent,\n AssistantContextChangedHandler,\n AssistantThreadStartedEvent,\n AssistantThreadStartedHandler,\n Author,\n Channel,\n ChatConfig,\n ChatInstance,\n EmojiValue,\n Logger,\n LogLevel,\n MemberJoinedChannelEvent,\n MemberJoinedChannelHandler,\n MentionHandler,\n MessageHandler,\n ModalCloseEvent,\n ModalCloseHandler,\n ModalResponse,\n ModalSubmitEvent,\n ModalSubmitHandler,\n ReactionEvent,\n ReactionHandler,\n SentMessage,\n SlashCommandEvent,\n SlashCommandHandler,\n StateAdapter,\n SubscribedMessageHandler,\n Thread,\n WebhookOptions,\n} from \"./types\";\nimport { ChatError, ConsoleLogger, LockError } from \"./types\";\n\nconst DEFAULT_LOCK_TTL_MS = 30_000; // 30 seconds\nconst SLACK_USER_ID_REGEX = /^U[A-Z0-9]+$/i;\nconst DISCORD_SNOWFLAKE_REGEX = /^\\d{17,19}$/;\n/** TTL for message deduplication entries */\nconst DEDUPE_TTL_MS = 5 * 60 * 1000; // 5 minutes\nconst MODAL_CONTEXT_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours\n\n/** Server-side stored modal context */\ninterface StoredModalContext {\n channel?: SerializedChannel;\n message?: SerializedMessage;\n thread?: SerializedThread;\n}\n\ninterface MessagePattern<TState = Record<string, unknown>> {\n handler: MessageHandler<TState>;\n pattern: RegExp;\n}\n\n/** Filter can be EmojiValue objects, emoji names, or raw emoji formats */\ntype EmojiFilter = EmojiValue | string;\n\ninterface ReactionPattern {\n /** If specified, only these emoji trigger the handler. Empty means all emoji. */\n emoji: EmojiFilter[];\n handler: ReactionHandler;\n}\n\ninterface ActionPattern {\n /** If specified, only these action IDs trigger the handler. Empty means all actions. */\n actionIds: string[];\n handler: ActionHandler;\n}\n\ninterface ModalSubmitPattern {\n callbackIds: string[];\n handler: ModalSubmitHandler;\n}\n\ninterface ModalClosePattern {\n callbackIds: string[];\n handler: ModalCloseHandler;\n}\n\ninterface SlashCommandPattern<TState = Record<string, unknown>> {\n /** If specified, only these commands trigger the handler. Empty means all commands. */\n commands: string[];\n handler: SlashCommandHandler<TState>;\n}\n\n/**\n * Type-safe webhook handler that is available for each adapter.\n */\ntype WebhookHandler = (\n request: Request,\n options?: WebhookOptions\n) => Promise<Response>;\n\n/**\n * Creates a type-safe webhooks object based on the adapter names.\n */\ntype Webhooks<TAdapters extends Record<string, Adapter>> = {\n [K in keyof TAdapters]: WebhookHandler;\n};\n\n/**\n * Main Chat class with type-safe adapter inference and custom thread state.\n *\n * @template TAdapters - Map of adapter names to Adapter instances\n * @template TState - Custom state type stored per-thread (default: Record<string, unknown>)\n *\n * @example\n * // Define custom thread state type\n * interface MyThreadState {\n * aiMode?: boolean;\n * userName?: string;\n * }\n *\n * const chat = new Chat<typeof adapters, MyThreadState>({\n * userName: \"mybot\",\n * adapters: {\n * slack: createSlackAdapter({ ... }),\n * teams: createTeamsAdapter({ ... }),\n * },\n * state: createMemoryState(),\n * });\n *\n * // Type-safe thread state\n * chat.onNewMention(async (thread, message) => {\n * await thread.setState({ aiMode: true });\n * const state = await thread.state; // Type: MyThreadState | null\n * });\n */\nexport class Chat<\n TAdapters extends Record<string, Adapter> = Record<string, Adapter>,\n TState = Record<string, unknown>,\n> implements ChatInstance\n{\n /**\n * Register this Chat instance as the global singleton.\n * Required for Thread deserialization via @workflow/serde.\n *\n * @example\n * ```typescript\n * const chat = new Chat({ ... });\n * chat.registerSingleton();\n *\n * // Now threads can be deserialized without passing chat explicitly\n * const thread = ThreadImpl.fromJSON(serializedThread);\n * ```\n */\n registerSingleton(): this {\n setChatSingleton(this);\n return this;\n }\n\n /**\n * Get the registered singleton Chat instance.\n * Throws if no singleton has been registered.\n */\n static getSingleton(): Chat {\n return getChatSingleton() as Chat;\n }\n\n /**\n * Check if a singleton has been registered.\n */\n static hasSingleton(): boolean {\n return hasChatSingleton();\n }\n\n private readonly adapters: Map<string, Adapter>;\n private readonly _stateAdapter: StateAdapter;\n private readonly userName: string;\n private readonly logger: Logger;\n private readonly _streamingUpdateIntervalMs: number;\n private readonly _fallbackStreamingPlaceholderText: string | null;\n private readonly _dedupeTtlMs: number;\n private readonly _onLockConflict: ChatConfig[\"onLockConflict\"];\n\n private readonly mentionHandlers: MentionHandler<TState>[] = [];\n private readonly messagePatterns: MessagePattern<TState>[] = [];\n private readonly subscribedMessageHandlers: SubscribedMessageHandler<TState>[] =\n [];\n private readonly reactionHandlers: ReactionPattern[] = [];\n private readonly actionHandlers: ActionPattern[] = [];\n private readonly modalSubmitHandlers: ModalSubmitPattern[] = [];\n private readonly modalCloseHandlers: ModalClosePattern[] = [];\n private readonly slashCommandHandlers: SlashCommandPattern<TState>[] = [];\n private readonly assistantThreadStartedHandlers: AssistantThreadStartedHandler[] =\n [];\n private readonly assistantContextChangedHandlers: AssistantContextChangedHandler[] =\n [];\n private readonly appHomeOpenedHandlers: AppHomeOpenedHandler[] = [];\n private readonly memberJoinedChannelHandlers: MemberJoinedChannelHandler[] =\n [];\n\n /** Initialization state */\n private initPromise: Promise<void> | null = null;\n private initialized = false;\n\n /**\n * Type-safe webhook handlers keyed by adapter name.\n * @example\n * chat.webhooks.slack(request, { backgroundTask: waitUntil });\n */\n readonly webhooks: Webhooks<TAdapters>;\n\n constructor(config: ChatConfig<TAdapters>) {\n this.userName = config.userName;\n this._stateAdapter = config.state;\n this.adapters = new Map();\n this._streamingUpdateIntervalMs = config.streamingUpdateIntervalMs ?? 500;\n this._fallbackStreamingPlaceholderText =\n config.fallbackStreamingPlaceholderText !== undefined\n ? config.fallbackStreamingPlaceholderText\n : \"...\";\n this._dedupeTtlMs = config.dedupeTtlMs ?? DEDUPE_TTL_MS;\n this._onLockConflict = config.onLockConflict;\n\n // Initialize logger\n if (typeof config.logger === \"string\") {\n this.logger = new ConsoleLogger(config.logger as LogLevel);\n } else {\n this.logger = config.logger || new ConsoleLogger(\"info\");\n }\n\n // Register adapters and create webhook handlers\n const webhooks = {} as Record<string, WebhookHandler>;\n for (const [name, adapter] of Object.entries(config.adapters)) {\n this.adapters.set(name, adapter);\n // Create webhook handler for each adapter\n webhooks[name] = (request: Request, options?: WebhookOptions) =>\n this.handleWebhook(name, request, options);\n }\n this.webhooks = webhooks as Webhooks<TAdapters>;\n\n this.logger.debug(\"Chat instance created\", {\n adapters: Object.keys(config.adapters),\n });\n }\n\n /**\n * Handle a webhook request for a specific adapter.\n * Automatically initializes adapters on first call.\n */\n private async handleWebhook(\n adapterName: string,\n request: Request,\n options?: WebhookOptions\n ): Promise<Response> {\n // Ensure initialization\n await this.ensureInitialized();\n\n const adapter = this.adapters.get(adapterName);\n if (!adapter) {\n return new Response(`Unknown adapter: ${adapterName}`, { status: 404 });\n }\n\n return adapter.handleWebhook(request, options);\n }\n\n /**\n * Ensure the chat instance is initialized.\n * This is called automatically before handling webhooks.\n */\n private async ensureInitialized(): Promise<void> {\n if (this.initialized) {\n return;\n }\n\n // Avoid concurrent initialization\n if (!this.initPromise) {\n this.initPromise = this.doInitialize();\n }\n\n await this.initPromise;\n }\n\n private async doInitialize(): Promise<void> {\n this.logger.info(\"Initializing chat instance...\");\n await this._stateAdapter.connect();\n this.logger.debug(\"State connected\");\n\n const initPromises = Array.from(this.adapters.values()).map(\n async (adapter) => {\n this.logger.debug(\"Initializing adapter\", adapter.name);\n const result = await adapter.initialize(this);\n this.logger.debug(\"Adapter initialized\", adapter.name);\n return result;\n }\n );\n await Promise.all(initPromises);\n\n this.initialized = true;\n this.logger.info(\"Chat instance initialized\", {\n adapters: Array.from(this.adapters.keys()),\n });\n }\n\n /**\n * Gracefully shut down the chat instance.\n */\n async shutdown(): Promise<void> {\n this.logger.info(\"Shutting down chat instance...\");\n await this._stateAdapter.disconnect();\n this.initialized = false;\n this.initPromise = null;\n this.logger.info(\"Chat instance shut down\");\n }\n\n /**\n * Initialize the chat instance and all adapters.\n * This is called automatically when handling webhooks, but can be called\n * manually for non-webhook use cases (e.g., Gateway listeners).\n */\n async initialize(): Promise<void> {\n await this.ensureInitialized();\n }\n\n /**\n * Register a handler for new @-mentions of the bot.\n *\n * **Important**: This handler is ONLY called for mentions in **unsubscribed** threads.\n * Once a thread is subscribed (via `thread.subscribe()`), subsequent messages\n * including @-mentions go to `onSubscribedMessage` handlers instead.\n *\n * To detect mentions in subscribed threads, check `message.isMention`:\n *\n * @example\n * ```typescript\n * // Handle new mentions (unsubscribed threads only)\n * chat.onNewMention(async (thread, message) => {\n * await thread.subscribe(); // Subscribe to follow-up messages\n * await thread.post(\"Hello! I'll be watching this thread.\");\n * });\n *\n * // Handle all messages in subscribed threads\n * chat.onSubscribedMessage(async (thread, message) => {\n * if (message.isMention) {\n * // User @-mentioned us in a thread we're already watching\n * await thread.post(\"You mentioned me again!\");\n * }\n * });\n * ```\n */\n onNewMention(handler: MentionHandler<TState>): void {\n this.mentionHandlers.push(handler);\n this.logger.debug(\"Registered mention handler\");\n }\n\n /**\n * Register a handler for messages matching a regex pattern.\n *\n * @param pattern - Regular expression to match against message text\n * @param handler - Handler called when pattern matches\n *\n * @example\n * ```typescript\n * // Match messages starting with \"!help\"\n * chat.onNewMessage(/^!help/, async (thread, message) => {\n * await thread.post(\"Available commands: !help, !status, !ping\");\n * });\n * ```\n */\n onNewMessage(pattern: RegExp, handler: MessageHandler<TState>): void {\n this.messagePatterns.push({ pattern, handler });\n this.logger.debug(\"Registered message pattern handler\", {\n pattern: pattern.toString(),\n });\n }\n\n /**\n * Register a handler for messages in subscribed threads.\n *\n * Called for all messages in threads that have been subscribed via `thread.subscribe()`.\n * This includes:\n * - Follow-up messages from users\n * - Messages that @-mention the bot (check `message.isMention`)\n *\n * Does NOT fire for:\n * - The message that triggered the subscription (e.g., the initial @mention)\n * - Messages sent by the bot itself\n *\n * @example\n * ```typescript\n * chat.onSubscribedMessage(async (thread, message) => {\n * // Handle all follow-up messages\n * if (message.isMention) {\n * // User @-mentioned us in a subscribed thread\n * }\n * await thread.post(`Got your message: ${message.text}`);\n * });\n * ```\n */\n onSubscribedMessage(handler: SubscribedMessageHandler<TState>): void {\n this.subscribedMessageHandlers.push(handler);\n this.logger.debug(\"Registered subscribed message handler\");\n }\n\n /**\n * Register a handler for reaction events.\n *\n * @example\n * ```typescript\n * // Handle specific emoji using EmojiValue objects (recommended)\n * chat.onReaction([emoji.thumbs_up, emoji.heart], async (event) => {\n * if (event.emoji === emoji.thumbs_up) {\n * console.log(\"Thumbs up!\");\n * }\n * });\n *\n * // Handle all reactions\n * chat.onReaction(async (event) => {\n * console.log(`${event.added ? \"Added\" : \"Removed\"} ${event.emoji.name}`);\n * });\n * ```\n *\n * @param emojiOrHandler - Either an array of emoji to filter (EmojiValue or string), or the handler\n * @param handler - The handler (if emoji filter is provided)\n */\n onReaction(handler: ReactionHandler): void;\n onReaction(emoji: EmojiFilter[], handler: ReactionHandler): void;\n onReaction(\n emojiOrHandler: EmojiFilter[] | ReactionHandler,\n handler?: ReactionHandler\n ): void {\n if (typeof emojiOrHandler === \"function\") {\n // No emoji filter - handle all reactions\n this.reactionHandlers.push({ emoji: [], handler: emojiOrHandler });\n this.logger.debug(\"Registered reaction handler for all emoji\");\n } else if (handler) {\n // Specific emoji filter\n this.reactionHandlers.push({ emoji: emojiOrHandler, handler });\n this.logger.debug(\"Registered reaction handler\", {\n emoji: emojiOrHandler.map((e) => (typeof e === \"string\" ? e : e.name)),\n });\n }\n }\n\n /**\n * Register a handler for action events (button clicks in cards).\n *\n * @example\n * ```typescript\n * // Handle specific action\n * chat.onAction(\"approve\", async (event) => {\n * await event.thread.post(\"Approved!\");\n * });\n *\n * // Handle multiple actions\n * chat.onAction([\"approve\", \"reject\"], async (event) => {\n * if (event.actionId === \"approve\") {\n * await event.thread.post(\"Approved!\");\n * } else {\n * await event.thread.post(\"Rejected!\");\n * }\n * });\n *\n * // Handle all actions (catch-all)\n * chat.onAction(async (event) => {\n * console.log(`Action: ${event.actionId}`);\n * });\n * ```\n *\n * @param actionIdOrHandler - Either an action ID, array of action IDs, or the handler\n * @param handler - The handler (if action ID filter is provided)\n */\n onAction(handler: ActionHandler): void;\n onAction(actionIds: string[] | string, handler: ActionHandler): void;\n onAction(\n actionIdOrHandler: string | string[] | ActionHandler,\n handler?: ActionHandler\n ): void {\n if (typeof actionIdOrHandler === \"function\") {\n // No action filter - handle all actions\n this.actionHandlers.push({ actionIds: [], handler: actionIdOrHandler });\n this.logger.debug(\"Registered action handler for all actions\");\n } else if (handler) {\n // Specific action ID(s) filter\n const actionIds = Array.isArray(actionIdOrHandler)\n ? actionIdOrHandler\n : [actionIdOrHandler];\n this.actionHandlers.push({ actionIds, handler });\n this.logger.debug(\"Registered action handler\", { actionIds });\n }\n }\n\n onModalSubmit(handler: ModalSubmitHandler): void;\n onModalSubmit(\n callbackIds: string[] | string,\n handler: ModalSubmitHandler\n ): void;\n onModalSubmit(\n callbackIdOrHandler: string | string[] | ModalSubmitHandler,\n handler?: ModalSubmitHandler\n ): void {\n if (typeof callbackIdOrHandler === \"function\") {\n this.modalSubmitHandlers.push({\n callbackIds: [],\n handler: callbackIdOrHandler,\n });\n this.logger.debug(\"Registered modal submit handler for all modals\");\n } else if (handler) {\n const callbackIds = Array.isArray(callbackIdOrHandler)\n ? callbackIdOrHandler\n : [callbackIdOrHandler];\n this.modalSubmitHandlers.push({ callbackIds, handler });\n this.logger.debug(\"Registered modal submit handler\", { callbackIds });\n }\n }\n\n onModalClose(handler: ModalCloseHandler): void;\n onModalClose(\n callbackIds: string[] | string,\n handler: ModalCloseHandler\n ): void;\n onModalClose(\n callbackIdOrHandler: string | string[] | ModalCloseHandler,\n handler?: ModalCloseHandler\n ): void {\n if (typeof callbackIdOrHandler === \"function\") {\n this.modalCloseHandlers.push({\n callbackIds: [],\n handler: callbackIdOrHandler,\n });\n this.logger.debug(\"Registered modal close handler for all modals\");\n } else if (handler) {\n const callbackIds = Array.isArray(callbackIdOrHandler)\n ? callbackIdOrHandler\n : [callbackIdOrHandler];\n this.modalCloseHandlers.push({ callbackIds, handler });\n this.logger.debug(\"Registered modal close handler\", { callbackIds });\n }\n }\n\n /**\n * Register a handler for slash command events.\n *\n * Slash commands are triggered when a user types `/command` in the message composer.\n * Use `event.channel.post()` or `event.channel.postEphemeral()` to respond.\n *\n * @example\n * ```typescript\n * // Handle a specific command\n * chat.onSlashCommand(\"/help\", async (event) => {\n * await event.channel.post(\"Here are the available commands...\");\n * });\n *\n * // Handle multiple commands\n * chat.onSlashCommand([\"/status\", \"/health\"], async (event) => {\n * await event.channel.post(\"All systems operational!\");\n * });\n *\n * // Handle all commands (catch-all)\n * chat.onSlashCommand(async (event) => {\n * console.log(`Received command: ${event.command} ${event.text}`);\n * });\n *\n * // Open a modal from a slash command\n * chat.onSlashCommand(\"/feedback\", async (event) => {\n * await event.openModal({\n * callbackId: \"feedback_modal\",\n * title: \"Submit Feedback\",\n * inputs: [{ id: \"feedback\", type: \"text_input\", label: \"Your feedback\" }],\n * });\n * });\n * ```\n *\n * @param commandOrHandler - Either a command, array of commands, or the handler\n * @param handler - The handler (if command filter is provided)\n */\n onSlashCommand(handler: SlashCommandHandler<TState>): void;\n onSlashCommand(\n commands: string[] | string,\n handler: SlashCommandHandler<TState>\n ): void;\n onSlashCommand(\n commandOrHandler: string | string[] | SlashCommandHandler<TState>,\n handler?: SlashCommandHandler<TState>\n ): void {\n if (typeof commandOrHandler === \"function\") {\n this.slashCommandHandlers.push({\n commands: [],\n handler: commandOrHandler,\n });\n this.logger.debug(\"Registered slash command handler for all commands\");\n } else if (handler) {\n const commands = Array.isArray(commandOrHandler)\n ? commandOrHandler\n : [commandOrHandler];\n const normalizedCommands = commands.map((cmd) =>\n cmd.startsWith(\"/\") ? cmd : `/${cmd}`\n );\n this.slashCommandHandlers.push({ commands: normalizedCommands, handler });\n this.logger.debug(\"Registered slash command handler\", {\n commands: normalizedCommands,\n });\n }\n }\n\n onAssistantThreadStarted(handler: AssistantThreadStartedHandler): void {\n this.assistantThreadStartedHandlers.push(handler);\n this.logger.debug(\"Registered assistant thread started handler\");\n }\n\n onAssistantContextChanged(handler: AssistantContextChangedHandler): void {\n this.assistantContextChangedHandlers.push(handler);\n this.logger.debug(\"Registered assistant context changed handler\");\n }\n\n onAppHomeOpened(handler: AppHomeOpenedHandler): void {\n this.appHomeOpenedHandlers.push(handler);\n this.logger.debug(\"Registered app home opened handler\");\n }\n\n onMemberJoinedChannel(handler: MemberJoinedChannelHandler): void {\n this.memberJoinedChannelHandlers.push(handler);\n this.logger.debug(\"Registered member joined channel handler\");\n }\n\n /**\n * Get an adapter by name with type safety.\n */\n getAdapter<K extends keyof TAdapters>(name: K): TAdapters[K] {\n return this.adapters.get(name as string) as TAdapters[K];\n }\n\n /**\n * Get a JSON.parse reviver function that automatically deserializes\n * chat:Thread and chat:Message objects.\n *\n * Use this when parsing JSON that contains serialized Thread or Message objects\n * (e.g., from workflow engine payloads).\n *\n * @returns A reviver function for JSON.parse\n *\n * @example\n * ```typescript\n * // Parse workflow payload with automatic deserialization\n * const data = JSON.parse(payload, chat.reviver());\n *\n * // data.thread is now a ThreadImpl instance\n * // data.message is now a Message object with Date fields restored\n * await data.thread.post(\"Hello from workflow!\");\n * ```\n */\n reviver(): (key: string, value: unknown) => unknown {\n // Ensure this chat instance is registered as singleton for thread deserialization\n this.registerSingleton();\n return function reviver(_key: string, value: unknown): unknown {\n if (value && typeof value === \"object\" && \"_type\" in value) {\n const typed = value as { _type: string };\n if (typed._type === \"chat:Thread\") {\n return ThreadImpl.fromJSON(value as SerializedThread);\n }\n if (typed._type === \"chat:Channel\") {\n return ChannelImpl.fromJSON(value as SerializedChannel);\n }\n if (typed._type === \"chat:Message\") {\n return Message.fromJSON(value as SerializedMessage);\n }\n }\n return value;\n };\n }\n\n // ChatInstance interface implementations\n\n /**\n * Process an incoming message from an adapter.\n * Handles waitUntil registration and error catching internally.\n * Adapters should call this instead of handleIncomingMessage directly.\n */\n processMessage(\n adapter: Adapter,\n threadId: string,\n messageOrFactory: Message | (() => Promise<Message>),\n options?: WebhookOptions\n ): void {\n const task = (async () => {\n const message =\n typeof messageOrFactory === \"function\"\n ? await messageOrFactory()\n : messageOrFactory;\n await this.handleIncomingMessage(adapter, threadId, message);\n })().catch((err) => {\n this.logger.error(\"Message processing error\", { error: err, threadId });\n });\n\n if (options?.waitUntil) {\n options.waitUntil(task);\n }\n }\n\n /**\n * Process an incoming reaction event from an adapter.\n * Handles waitUntil registration and error catching internally.\n */\n processReaction(\n event: Omit<ReactionEvent, \"adapter\" | \"thread\"> & { adapter?: Adapter },\n options?: WebhookOptions\n ): void {\n const task = this.handleReactionEvent(event).catch((err) => {\n this.logger.error(\"Reaction processing error\", {\n error: err,\n emoji: event.emoji,\n messageId: event.messageId,\n });\n });\n\n if (options?.waitUntil) {\n options.waitUntil(task);\n }\n }\n\n /**\n * Process an incoming action event (button click) from an adapter.\n * Handles waitUntil registration and error catching internally.\n */\n processAction(\n event: Omit<ActionEvent, \"thread\" | \"openModal\"> & { adapter: Adapter },\n options?: WebhookOptions\n ): void {\n const task = this.handleActionEvent(event).catch((err) => {\n this.logger.error(\"Action processing error\", {\n error: err,\n actionId: event.actionId,\n messageId: event.messageId,\n });\n });\n\n if (options?.waitUntil) {\n options.waitUntil(task);\n }\n }\n\n async processModalSubmit(\n event: Omit<\n ModalSubmitEvent,\n \"relatedThread\" | \"relatedMessage\" | \"relatedChannel\"\n >,\n contextId?: string,\n _options?: WebhookOptions\n ): Promise<ModalResponse | undefined> {\n const { relatedThread, relatedMessage, relatedChannel } =\n await this.retrieveModalContext(event.adapter.name, contextId);\n\n const fullEvent: ModalSubmitEvent = {\n ...event,\n relatedThread,\n relatedMessage,\n relatedChannel,\n };\n\n for (const { callbackIds, handler } of this.modalSubmitHandlers) {\n if (callbackIds.length === 0 || callbackIds.includes(event.callbackId)) {\n try {\n const response = await handler(fullEvent);\n if (response) {\n return response;\n }\n } catch (err) {\n this.logger.error(\"Modal submit handler error\", {\n error: err,\n callbackId: event.callbackId,\n });\n }\n }\n }\n }\n\n processModalClose(\n event: Omit<\n ModalCloseEvent,\n \"relatedThread\" | \"relatedMessage\" | \"relatedChannel\"\n >,\n contextId?: string,\n options?: WebhookOptions\n ): void {\n const task = (async () => {\n const { relatedThread, relatedMessage, relatedChannel } =\n await this.retrieveModalContext(event.adapter.name, contextId);\n\n const fullEvent: ModalCloseEvent = {\n ...event,\n relatedThread,\n relatedMessage,\n relatedChannel,\n };\n\n for (const { callbackIds, handler } of this.modalCloseHandlers) {\n if (\n callbackIds.length === 0 ||\n callbackIds.includes(event.callbackId)\n ) {\n await handler(fullEvent);\n }\n }\n })().catch((err) => {\n this.logger.error(\"Modal close handler error\", {\n error: err,\n callbackId: event.callbackId,\n });\n });\n\n if (options?.waitUntil) {\n options.waitUntil(task);\n }\n }\n\n /**\n * Process an incoming slash command from an adapter.\n * Handles waitUntil registration and error catching internally.\n */\n processSlashCommand(\n event: Omit<SlashCommandEvent, \"channel\" | \"openModal\"> & {\n adapter: Adapter;\n channelId: string;\n },\n options?: WebhookOptions\n ): void {\n const task = this.handleSlashCommandEvent(event).catch((err) => {\n this.logger.error(\"Slash command processing error\", {\n error: err,\n command: event.command,\n text: event.text,\n });\n });\n\n if (options?.waitUntil) {\n options.waitUntil(task);\n }\n }\n\n processAssistantThreadStarted(\n event: AssistantThreadStartedEvent,\n options?: WebhookOptions\n ): void {\n const task = (async () => {\n for (const handler of this.assistantThreadStartedHandlers) {\n await handler(event);\n }\n })().catch((err) => {\n this.logger.error(\"Assistant thread started handler error\", {\n error: err,\n threadId: event.threadId,\n });\n });\n\n if (options?.waitUntil) {\n options.waitUntil(task);\n }\n }\n\n processAssistantContextChanged(\n event: AssistantContextChangedEvent,\n options?: WebhookOptions\n ): void {\n const task = (async () => {\n for (const handler of this.assistantContextChangedHandlers) {\n await handler(event);\n }\n })().catch((err) => {\n this.logger.error(\"Assistant context changed handler error\", {\n error: err,\n threadId: event.threadId,\n });\n });\n\n if (options?.waitUntil) {\n options.waitUntil(task);\n }\n }\n\n processAppHomeOpened(\n event: AppHomeOpenedEvent,\n options?: WebhookOptions\n ): void {\n const task = (async () => {\n for (const handler of this.appHomeOpenedHandlers) {\n await handler(event);\n }\n })().catch((err) => {\n this.logger.error(\"App home opened handler error\", {\n error: err,\n userId: event.userId,\n });\n });\n\n if (options?.waitUntil) {\n options.waitUntil(task);\n }\n }\n\n processMemberJoinedChannel(\n event: MemberJoinedChannelEvent,\n options?: WebhookOptions\n ): void {\n const task = (async () => {\n for (const handler of this.memberJoinedChannelHandlers) {\n await handler(event);\n }\n })().catch((err) => {\n this.logger.error(\"Member joined channel handler error\", {\n error: err,\n channelId: event.channelId,\n userId: event.userId,\n });\n });\n\n if (options?.waitUntil) {\n options.waitUntil(task);\n }\n }\n\n /**\n * Handle a slash command event internally.\n */\n private async handleSlashCommandEvent(\n event: Omit<SlashCommandEvent, \"channel\" | \"openModal\"> & {\n adapter: Adapter;\n channelId: string;\n }\n ): Promise<void> {\n this.logger.debug(\"Incoming slash command\", {\n adapter: event.adapter.name,\n command: event.command,\n text: event.text,\n user: event.user.userName,\n });\n if (event.user.isMe) {\n this.logger.debug(\"Skipping slash command from self\", {\n command: event.command,\n });\n return;\n }\n const channel = new ChannelImpl<TState>({\n id: event.channelId,\n adapter: event.adapter,\n stateAdapter: this._stateAdapter,\n });\n const fullEvent: SlashCommandEvent<TState> = {\n ...event,\n channel,\n openModal: async (modal) => {\n if (!event.triggerId) {\n this.logger.warn(\"Cannot open modal: no triggerId available\");\n return undefined;\n }\n if (!event.adapter.openModal) {\n this.logger.warn(\n `Cannot open modal: ${event.adapter.name} does not support modals`\n );\n return undefined;\n }\n let modalElement: ModalElement = modal as ModalElement;\n if (isJSX(modal)) {\n const converted = toModalElement(modal);\n if (!converted) {\n throw new Error(\"Invalid JSX element: must be a Modal element\");\n }\n modalElement = converted;\n }\n const contextId = crypto.randomUUID();\n this.storeModalContext(\n event.adapter.name,\n contextId,\n undefined,\n undefined,\n channel\n );\n return event.adapter.openModal(\n event.triggerId,\n modalElement,\n contextId\n );\n },\n };\n this.logger.debug(\"Checking slash command handlers\", {\n handlerCount: this.slashCommandHandlers.length,\n command: event.command,\n });\n for (const { commands, handler } of this.slashCommandHandlers) {\n if (commands.length === 0) {\n this.logger.debug(\"Running catch-all slash command handler\");\n await handler(fullEvent);\n continue;\n }\n if (commands.includes(event.command)) {\n this.logger.debug(\"Running matched slash command handler\", {\n command: event.command,\n });\n await handler(fullEvent);\n }\n }\n }\n\n /**\n * Store modal context server-side with a context ID.\n * Called when opening a modal to preserve thread/message/channel for the submit handler.\n */\n private storeModalContext(\n adapterName: string,\n contextId: string,\n thread?: ThreadImpl<TState>,\n message?: Message,\n channel?: ChannelImpl<TState>\n ): void {\n const key = `modal-context:${adapterName}:${contextId}`;\n const context: StoredModalContext = {\n thread: thread?.toJSON(),\n message: message?.toJSON(),\n channel: channel?.toJSON(),\n };\n this._stateAdapter.set(key, context, MODAL_CONTEXT_TTL_MS).catch((err) => {\n this.logger.error(\"Failed to store modal context\", {\n contextId,\n error: err,\n });\n });\n }\n\n /**\n * Retrieve and delete modal context from server-side storage.\n * Called when processing modal submit/close to reconstruct thread/message/channel.\n */\n private async retrieveModalContext(\n adapterName: string,\n contextId?: string\n ): Promise<{\n relatedThread: Thread | undefined;\n relatedMessage: SentMessage | undefined;\n relatedChannel: Channel | undefined;\n }> {\n if (!contextId) {\n return {\n relatedThread: undefined,\n relatedMessage: undefined,\n relatedChannel: undefined,\n };\n }\n\n const key = `modal-context:${adapterName}:${contextId}`;\n const stored = await this._stateAdapter.get<StoredModalContext>(key);\n\n if (!stored) {\n return {\n relatedThread: undefined,\n relatedMessage: undefined,\n relatedChannel: undefined,\n };\n }\n\n const adapter = this.adapters.get(adapterName);\n\n // Reconstruct thread with adapter directly (if present)\n let relatedThread: Thread | undefined;\n if (stored.thread) {\n relatedThread = ThreadImpl.fromJSON(stored.thread, adapter) as Thread;\n }\n\n // Reconstruct message if present\n let relatedMessage: SentMessage | undefined;\n if (stored.message && relatedThread) {\n const message = Message.fromJSON(stored.message);\n relatedMessage = (\n relatedThread as ThreadImpl<TState>\n ).createSentMessageFromMessage(message);\n }\n\n // Reconstruct channel if present\n let relatedChannel: Channel | undefined;\n if (stored.channel) {\n relatedChannel = ChannelImpl.fromJSON(stored.channel, adapter) as Channel;\n }\n\n return { relatedThread, relatedMessage, relatedChannel };\n }\n\n /**\n * Handle an action event internally.\n */\n private async handleActionEvent(\n event: Omit<ActionEvent, \"thread\" | \"openModal\"> & { adapter: Adapter }\n ): Promise<void> {\n this.logger.debug(\"Incoming action\", {\n adapter: event.adapter.name,\n actionId: event.actionId,\n value: event.value,\n user: event.user.userName,\n messageId: event.messageId,\n threadId: event.threadId,\n });\n\n // Skip actions from self (shouldn't happen, but be safe)\n if (event.user.isMe) {\n this.logger.debug(\"Skipping action from self\", {\n actionId: event.actionId,\n });\n return;\n }\n\n const isSubscribed = false;\n const messageForThread = event.messageId\n ? new Message({\n id: event.messageId,\n threadId: event.threadId,\n text: \"\",\n formatted: { type: \"root\", children: [] },\n raw: event.raw,\n author: event.user,\n metadata: { dateSent: new Date(), edited: false },\n attachments: [],\n })\n : ({} as Message);\n\n // Create thread for the action event (skip for view-based actions with no threadId)\n const thread = event.threadId\n ? await this.createThread(\n event.adapter,\n event.threadId,\n messageForThread,\n isSubscribed\n )\n : null;\n\n // Build full event with thread and openModal helper\n const fullEvent: ActionEvent = {\n ...event,\n thread,\n openModal: async (modal) => {\n if (!event.triggerId) {\n this.logger.warn(\"Cannot open modal: no triggerId available\");\n return undefined;\n }\n if (!event.adapter.openModal) {\n this.logger.warn(\n `Cannot open modal: ${event.adapter.name} does not support modals`\n );\n return undefined;\n }\n\n // Convert JSX to ModalElement if needed (same pattern as thread.post)\n let modalElement: ModalElement = modal as ModalElement;\n if (isJSX(modal)) {\n const converted = toModalElement(modal);\n if (!converted) {\n throw new Error(\"Invalid JSX element: must be a Modal element\");\n }\n modalElement = converted;\n }\n\n // Store context server-side and pass contextId to adapter\n let message: Message | undefined;\n if (thread) {\n const isEphemeralMessage = event.messageId?.startsWith(\"ephemeral:\");\n if (isEphemeralMessage) {\n const recentMessage = thread.recentMessages[0];\n if (recentMessage && typeof recentMessage.toJSON === \"function\") {\n message = recentMessage as Message;\n }\n } else if (event.messageId && event.adapter.fetchMessage) {\n const fetched = await event.adapter\n .fetchMessage(event.threadId, event.messageId)\n .catch(() => null);\n if (fetched) {\n message = new Message(fetched);\n } else {\n const recentMessage = thread.recentMessages[0];\n if (recentMessage && typeof recentMessage.toJSON === \"function\") {\n message = recentMessage as Message;\n }\n }\n }\n }\n const contextId = crypto.randomUUID();\n const channel = thread\n ? ((thread as ThreadImpl<TState>).channel as ChannelImpl<TState>)\n : undefined;\n this.storeModalContext(\n event.adapter.name,\n contextId,\n thread ? (thread as ThreadImpl<TState>) : undefined,\n message,\n channel\n );\n return event.adapter.openModal(\n event.triggerId,\n modalElement,\n contextId\n );\n },\n };\n\n // Run matching handlers\n this.logger.debug(\"Checking action handlers\", {\n handlerCount: this.actionHandlers.length,\n actionId: event.actionId,\n });\n\n for (const { actionIds, handler } of this.actionHandlers) {\n // If no action ID filter, run handler for all actions\n if (actionIds.length === 0) {\n this.logger.debug(\"Running catch-all action handler\");\n await handler(fullEvent);\n continue;\n }\n\n // Check if the action matches any of the specified action IDs\n if (actionIds.includes(event.actionId)) {\n this.logger.debug(\"Running matched action handler\", {\n actionId: event.actionId,\n });\n await handler(fullEvent);\n }\n }\n }\n\n /**\n * Handle a reaction event internally.\n */\n private async handleReactionEvent(\n event: Omit<ReactionEvent, \"adapter\" | \"thread\"> & { adapter?: Adapter }\n ): Promise<void> {\n this.logger.debug(\"Incoming reaction\", {\n adapter: event.adapter?.name,\n emoji: event.emoji,\n rawEmoji: event.rawEmoji,\n added: event.added,\n user: event.user.userName,\n messageId: event.messageId,\n threadId: event.threadId,\n });\n\n // Skip reactions from self\n if (event.user.isMe) {\n this.logger.debug(\"Skipping reaction from self\", {\n emoji: event.emoji,\n });\n return;\n }\n\n // Adapter is required for thread creation\n if (!event.adapter) {\n this.logger.error(\"Reaction event missing adapter\");\n return;\n }\n\n // Create thread for the reaction event\n const isSubscribed = await this._stateAdapter.isSubscribed(event.threadId);\n const thread = await this.createThread(\n event.adapter,\n event.threadId,\n event.message ?? ({} as Message),\n isSubscribed\n );\n\n // Build full event with thread and adapter\n const fullEvent: ReactionEvent = {\n ...event,\n adapter: event.adapter,\n thread,\n };\n\n // Run matching handlers\n this.logger.debug(\"Checking reaction handlers\", {\n handlerCount: this.reactionHandlers.length,\n emoji: event.emoji.name,\n rawEmoji: event.rawEmoji,\n });\n\n for (const { emoji: emojiFilter, handler } of this.reactionHandlers) {\n // If no emoji filter, run handler for all reactions\n if (emojiFilter.length === 0) {\n this.logger.debug(\"Running catch-all reaction handler\");\n await handler(fullEvent);\n continue;\n }\n\n // Check if the reaction matches any of the specified emoji\n const matches = emojiFilter.some((filter) => {\n // EmojiValue object identity comparison (recommended)\n if (filter === fullEvent.emoji) {\n return true;\n }\n\n // String comparison: check against emoji name or rawEmoji\n const filterName = typeof filter === \"string\" ? filter : filter.name;\n return (\n filterName === fullEvent.emoji.name ||\n filterName === fullEvent.rawEmoji\n );\n });\n\n this.logger.debug(\"Reaction filter check\", {\n filterEmoji: emojiFilter.map((e) =>\n typeof e === \"string\" ? e : e.name\n ),\n eventEmoji: fullEvent.emoji.name,\n matches,\n });\n\n if (matches) {\n this.logger.debug(\"Running matched reaction handler\");\n await handler(fullEvent);\n }\n }\n }\n\n getState(): StateAdapter {\n return this._stateAdapter;\n }\n\n getUserName(): string {\n return this.userName;\n }\n\n getLogger(prefix?: string): Logger {\n if (prefix) {\n return this.logger.child(prefix);\n }\n return this.logger;\n }\n\n /**\n * Open a direct message conversation with a user.\n *\n * Accepts either a user ID string or an Author object (from message.author or event.user).\n *\n * The adapter is automatically inferred from the userId format:\n * - Slack: `U...` (e.g., \"U00FAKEUSER1\")\n * - Teams: `29:...` (e.g., \"29:198PbJuw...\")\n * - Google Chat: `users/...` (e.g., \"users/100000000000000000001\")\n * - Discord: numeric snowflake (e.g., \"1033044521375764530\")\n *\n * @param user - Platform-specific user ID string, or an Author object\n * @returns A Thread that can be used to post messages\n *\n * @example\n * ```ts\n * // Using user ID directly\n * const dmThread = await chat.openDM(\"U123456\");\n * await dmThread.post(\"Hello via DM!\");\n *\n * // Using Author object from a message\n * chat.onSubscribedMessage(async (thread, message) => {\n * const dmThread = await chat.openDM(message.author);\n * await dmThread.post(\"Hello via DM!\");\n * });\n * ```\n */\n async openDM(user: string | Author): Promise<Thread<TState>> {\n const userId = typeof user === \"string\" ? user : user.userId;\n const adapter = this.inferAdapterFromUserId(userId);\n if (!adapter.openDM) {\n throw new ChatError(\n `Adapter \"${adapter.name}\" does not support openDM`,\n \"NOT_SUPPORTED\"\n );\n }\n\n const threadId = await adapter.openDM(userId);\n return this.createThread(adapter, threadId, {} as Message, false);\n }\n\n /**\n * Get a Channel by its channel ID.\n *\n * The adapter is automatically inferred from the channel ID prefix.\n *\n * @param channelId - Channel ID (e.g., \"slack:C123ABC\", \"gchat:spaces/ABC123\")\n * @returns A Channel that can be used to list threads, post messages, iterate messages, etc.\n *\n * @example\n * ```typescript\n * const channel = chat.channel(\"slack:C123ABC\");\n *\n * // Iterate messages newest first\n * for await (const msg of channel.messages) {\n * console.log(msg.text);\n * }\n *\n * // List threads\n * for await (const t of channel.threads()) {\n * console.log(t.rootMessage.text, t.replyCount);\n * }\n *\n * // Post to channel\n * await channel.post(\"Hello channel!\");\n * ```\n */\n channel(channelId: string): Channel<TState> {\n const adapterName = channelId.split(\":\")[0];\n if (!adapterName) {\n throw new ChatError(\n `Invalid channel ID: ${channelId}`,\n \"INVALID_CHANNEL_ID\"\n );\n }\n\n const adapter = this.adapters.get(adapterName);\n if (!adapter) {\n throw new ChatError(\n `Adapter \"${adapterName}\" not found for channel ID \"${channelId}\"`,\n \"ADAPTER_NOT_FOUND\"\n );\n }\n\n return new ChannelImpl<TState>({\n id: channelId,\n adapter,\n stateAdapter: this._stateAdapter,\n });\n }\n\n /**\n * Infer which adapter to use based on the userId format.\n */\n private inferAdapterFromUserId(userId: string): Adapter {\n // Google Chat: users/123456789\n if (userId.startsWith(\"users/\")) {\n const adapter = this.adapters.get(\"gchat\");\n if (adapter) {\n return adapter;\n }\n }\n\n // Teams: 29:base64string...\n if (userId.startsWith(\"29:\")) {\n const adapter = this.adapters.get(\"teams\");\n if (adapter) {\n return adapter;\n }\n }\n\n // Slack: U followed by alphanumeric (e.g., U00FAKEUSER1)\n if (SLACK_USER_ID_REGEX.test(userId)) {\n const adapter = this.adapters.get(\"slack\");\n if (adapter) {\n return adapter;\n }\n }\n\n // Discord: snowflake ID (17-19 digit number)\n if (DISCORD_SNOWFLAKE_REGEX.test(userId)) {\n const adapter = this.adapters.get(\"discord\");\n if (adapter) {\n return adapter;\n }\n }\n\n throw new ChatError(\n `Cannot infer adapter from userId \"${userId}\". Expected format: Slack (U...), Teams (29:...), Google Chat (users/...), or Discord (numeric snowflake).`,\n \"UNKNOWN_USER_ID_FORMAT\"\n );\n }\n\n /**\n * Handle an incoming message from an adapter.\n * This is called by adapters when they receive a webhook.\n *\n * The Chat class handles common concerns centrally:\n * - Deduplication: Same message may arrive multiple times (e.g., Slack sends\n * both `message` and `app_mention` events, GChat sends direct webhook + Pub/Sub)\n * - Bot filtering: Messages from the bot itself are skipped\n * - Locking: Only one instance processes a thread at a time\n */\n async handleIncomingMessage(\n adapter: Adapter,\n threadId: string,\n message: Message\n ): Promise<void> {\n this.logger.debug(\"Incoming message\", {\n adapter: adapter.name,\n threadId,\n messageId: message.id,\n text: message.text,\n author: message.author.userName,\n authorUserId: message.author.userId,\n isBot: message.author.isBot,\n isMe: message.author.isMe,\n });\n\n // Skip messages from self (bot's own messages)\n if (message.author.isMe) {\n this.logger.debug(\"Skipping message from self (isMe=true)\", {\n adapter: adapter.name,\n threadId,\n author: message.author.userName,\n });\n return;\n }\n\n // Deduplicate messages atomically - same message can arrive via multiple paths\n // (e.g., Slack message + app_mention events, GChat direct webhook + Pub/Sub)\n const dedupeKey = `dedupe:${adapter.name}:${message.id}`;\n const isFirstProcess = await this._stateAdapter.setIfNotExists(\n dedupeKey,\n true,\n this._dedupeTtlMs\n );\n if (!isFirstProcess) {\n this.logger.debug(\"Skipping duplicate message\", {\n adapter: adapter.name,\n messageId: message.id,\n });\n return;\n }\n\n // Try to acquire lock on thread\n let lock = await this._stateAdapter.acquireLock(\n threadId,\n DEFAULT_LOCK_TTL_MS\n );\n if (!lock) {\n const resolution =\n typeof this._onLockConflict === \"function\"\n ? await this._onLockConflict(threadId, message)\n : (this._onLockConflict ?? \"drop\");\n if (resolution === \"force\") {\n this.logger.info(\"Force-releasing lock on thread\", { threadId });\n await this._stateAdapter.forceReleaseLock(threadId);\n // Note: another instance could acquire the lock between release and re-acquire.\n // If that happens, lock will be null and we fall through to the LockError below.\n lock = await this._stateAdapter.acquireLock(\n threadId,\n DEFAULT_LOCK_TTL_MS\n );\n }\n if (!lock) {\n this.logger.warn(\"Could not acquire lock on thread\", { threadId });\n throw new LockError(\n `Could not acquire lock on thread ${threadId}. Another instance may be processing.`\n );\n }\n }\n\n this.logger.debug(\"Lock acquired\", { threadId, token: lock.token });\n\n try {\n // Set isMention on the message for handler access\n // Preserve existing isMention if already set (e.g., from Gateway detection)\n message.isMention =\n message.isMention || this.detectMention(adapter, message);\n\n // Check if this is a subscribed thread first\n const isSubscribed = await this._stateAdapter.isSubscribed(threadId);\n this.logger.debug(\"Subscription check\", {\n threadId,\n isSubscribed,\n subscribedHandlerCount: this.subscribedMessageHandlers.length,\n });\n\n // Create thread object (with subscription context for optimization)\n const thread = await this.createThread(\n adapter,\n threadId,\n message,\n isSubscribed\n );\n\n if (isSubscribed) {\n this.logger.debug(\"Message in subscribed thread - calling handlers\", {\n threadId,\n handlerCount: this.subscribedMessageHandlers.length,\n });\n await this.runHandlers(this.subscribedMessageHandlers, thread, message);\n return;\n }\n\n // Check for @-mention of bot\n if (message.isMention) {\n this.logger.debug(\"Bot mentioned\", {\n threadId,\n text: message.text.slice(0, 100),\n });\n await this.runHandlers(this.mentionHandlers, thread, message);\n return;\n }\n\n // Check message patterns\n this.logger.debug(\"Checking message patterns\", {\n patternCount: this.messagePatterns.length,\n patterns: this.messagePatterns.map((p) => p.pattern.toString()),\n messageText: message.text,\n });\n let matchedPattern = false;\n for (const { pattern, handler } of this.messagePatterns) {\n const matches = pattern.test(message.text);\n this.logger.debug(\"Pattern test\", {\n pattern: pattern.toString(),\n text: message.text,\n matches,\n });\n if (matches) {\n this.logger.debug(\"Message matched pattern - calling handler\", {\n pattern: pattern.toString(),\n });\n matchedPattern = true;\n await handler(thread, message);\n }\n }\n\n // Log if no handlers matched\n if (!matchedPattern) {\n this.logger.debug(\"No handlers matched message\", {\n threadId,\n text: message.text.slice(0, 100),\n });\n }\n } finally {\n await this._stateAdapter.releaseLock(lock);\n this.logger.debug(\"Lock released\", { threadId });\n }\n }\n\n private createThread(\n adapter: Adapter,\n threadId: string,\n initialMessage: Message,\n isSubscribedContext = false\n ): Thread<TState> {\n // Parse thread ID to get channel info\n // Format: \"adapter:channel:thread\"\n const parts = threadId.split(\":\");\n const channelId = parts[1] || \"\";\n\n // Check if this is a DM\n const isDM = adapter.isDM?.(threadId) ?? false;\n\n return new ThreadImpl<TState>({\n id: threadId,\n adapter,\n channelId,\n stateAdapter: this._stateAdapter,\n initialMessage,\n isSubscribedContext,\n isDM,\n currentMessage: initialMessage,\n streamingUpdateIntervalMs: this._streamingUpdateIntervalMs,\n fallbackStreamingPlaceholderText: this._fallbackStreamingPlaceholderText,\n });\n }\n\n /**\n * Detect if the bot was mentioned in the message.\n * All adapters normalize mentions to @name format, so we just check for @username.\n */\n private detectMention(adapter: Adapter, message: Message): boolean {\n const botUserName = adapter.userName || this.userName;\n const botUserId = adapter.botUserId;\n\n // Primary check: @username format (normalized by all adapters)\n const usernamePattern = new RegExp(\n `@${this.escapeRegex(botUserName)}\\\\b`,\n \"i\"\n );\n if (usernamePattern.test(message.text)) {\n return true;\n }\n\n // Fallback: check for user ID mention if available (e.g., @U_BOT_123)\n if (botUserId) {\n const userIdPattern = new RegExp(\n `@${this.escapeRegex(botUserId)}\\\\b`,\n \"i\"\n );\n if (userIdPattern.test(message.text)) {\n return true;\n }\n\n // Discord format: <@USER_ID> or <@!USER_ID>\n const discordPattern = new RegExp(\n `<@!?${this.escapeRegex(botUserId)}>`,\n \"i\"\n );\n if (discordPattern.test(message.text)) {\n return true;\n }\n }\n\n return false;\n }\n\n private escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n }\n\n private async runHandlers(\n handlers: Array<\n (thread: Thread<TState>, message: Message) => void | Promise<void>\n >,\n thread: Thread<TState>,\n message: Message\n ): Promise<void> {\n for (const handler of handlers) {\n await handler(thread, message);\n }\n }\n}\n","import type {\n CustomEmojiMap,\n EmojiFormats,\n EmojiMapConfig,\n EmojiValue,\n WellKnownEmoji,\n} from \"./types\";\n\n// Re-export EmojiValue for convenience\nexport type { EmojiValue } from \"./types\";\n\n// =============================================================================\n// EmojiValue - Immutable singleton emoji objects with object identity\n// =============================================================================\n\n/** Internal emoji registry for singleton instances */\nconst emojiRegistry = new Map<string, EmojiValue>();\n\n/**\n * Get or create an immutable singleton EmojiValue.\n *\n * Always returns the same frozen object for the same name,\n * enabling `===` comparison for emoji identity.\n *\n * @example\n * ```typescript\n * const e1 = getEmoji(\"thumbs_up\");\n * const e2 = getEmoji(\"thumbs_up\");\n * console.log(e1 === e2); // true - same object\n * ```\n */\nexport function getEmoji(name: string): EmojiValue {\n let emojiValue = emojiRegistry.get(name);\n if (!emojiValue) {\n emojiValue = Object.freeze({\n name,\n toString: () => `{{emoji:${name}}}`,\n toJSON: () => `{{emoji:${name}}}`,\n });\n emojiRegistry.set(name, emojiValue);\n }\n return emojiValue;\n}\n\n// =============================================================================\n// Emoji Map - Platform-specific formats\n// =============================================================================\n\n/**\n * Default emoji map for well-known emoji.\n * Maps normalized emoji names to platform-specific formats.\n */\nexport const DEFAULT_EMOJI_MAP: Record<string, EmojiFormats> = {\n // Reactions & Gestures\n thumbs_up: { slack: [\"+1\", \"thumbsup\"], gchat: \"👍\" },\n thumbs_down: { slack: [\"-1\", \"thumbsdown\"], gchat: \"👎\" },\n clap: { slack: \"clap\", gchat: \"👏\" },\n wave: { slack: \"wave\", gchat: \"👋\" },\n pray: { slack: \"pray\", gchat: \"🙏\" },\n muscle: { slack: \"muscle\", gchat: \"💪\" },\n ok_hand: { slack: \"ok_hand\", gchat: \"👌\" },\n point_up: { slack: \"point_up\", gchat: \"👆\" },\n point_down: { slack: \"point_down\", gchat: \"👇\" },\n point_left: { slack: \"point_left\", gchat: \"👈\" },\n point_right: { slack: \"point_right\", gchat: \"👉\" },\n raised_hands: { slack: \"raised_hands\", gchat: \"🙌\" },\n shrug: { slack: \"shrug\", gchat: \"🤷\" },\n facepalm: { slack: \"facepalm\", gchat: \"🤦\" },\n\n // Emotions & Faces\n heart: { slack: \"heart\", gchat: [\"❤️\", \"❤\"] },\n smile: { slack: [\"smile\", \"slightly_smiling_face\"], gchat: \"😊\" },\n laugh: { slack: [\"laughing\", \"satisfied\", \"joy\"], gchat: [\"😂\", \"😆\"] },\n thinking: { slack: \"thinking_face\", gchat: \"🤔\" },\n sad: { slack: [\"cry\", \"sad\", \"white_frowning_face\"], gchat: \"😢\" },\n cry: { slack: \"sob\", gchat: \"😭\" },\n angry: { slack: \"angry\", gchat: \"😠\" },\n love_eyes: { slack: \"heart_eyes\", gchat: \"😍\" },\n cool: { slack: \"sunglasses\", gchat: \"😎\" },\n wink: { slack: \"wink\", gchat: \"😉\" },\n surprised: { slack: \"open_mouth\", gchat: \"😮\" },\n worried: { slack: \"worried\", gchat: \"😟\" },\n confused: { slack: \"confused\", gchat: \"😕\" },\n neutral: { slack: \"neutral_face\", gchat: \"😐\" },\n sleeping: { slack: \"sleeping\", gchat: \"😴\" },\n sick: { slack: \"nauseated_face\", gchat: \"🤢\" },\n mind_blown: { slack: \"exploding_head\", gchat: \"🤯\" },\n relieved: { slack: \"relieved\", gchat: \"😌\" },\n grimace: { slack: \"grimacing\", gchat: \"😬\" },\n rolling_eyes: { slack: \"rolling_eyes\", gchat: \"🙄\" },\n hug: { slack: \"hugging_face\", gchat: \"🤗\" },\n zany: { slack: \"zany_face\", gchat: \"🤪\" },\n\n // Status & Symbols\n check: {\n slack: [\"white_check_mark\", \"heavy_check_mark\"],\n gchat: [\"✅\", \"✔️\"],\n },\n x: { slack: [\"x\", \"heavy_multiplication_x\"], gchat: [\"❌\", \"✖️\"] },\n question: { slack: \"question\", gchat: [\"❓\", \"?\"] },\n exclamation: { slack: \"exclamation\", gchat: \"❗\" },\n warning: { slack: \"warning\", gchat: \"⚠️\" },\n stop: { slack: \"octagonal_sign\", gchat: \"🛑\" },\n info: { slack: \"information_source\", gchat: \"ℹ️\" },\n \"100\": { slack: \"100\", gchat: \"💯\" },\n fire: { slack: \"fire\", gchat: \"🔥\" },\n star: { slack: \"star\", gchat: \"⭐\" },\n sparkles: { slack: \"sparkles\", gchat: \"✨\" },\n lightning: { slack: \"zap\", gchat: \"⚡\" },\n boom: { slack: \"boom\", gchat: \"💥\" },\n eyes: { slack: \"eyes\", gchat: \"👀\" },\n\n // Status Indicators (colored circles)\n green_circle: { slack: \"large_green_circle\", gchat: \"🟢\" },\n yellow_circle: { slack: \"large_yellow_circle\", gchat: \"🟡\" },\n red_circle: { slack: \"red_circle\", gchat: \"🔴\" },\n blue_circle: { slack: \"large_blue_circle\", gchat: \"🔵\" },\n white_circle: { slack: \"white_circle\", gchat: \"⚪\" },\n black_circle: { slack: \"black_circle\", gchat: \"⚫\" },\n\n // Objects & Tools\n rocket: { slack: \"rocket\", gchat: \"🚀\" },\n party: { slack: [\"tada\", \"partying_face\"], gchat: [\"🎉\", \"🥳\"] },\n confetti: { slack: \"confetti_ball\", gchat: \"🎊\" },\n balloon: { slack: \"balloon\", gchat: \"🎈\" },\n gift: { slack: \"gift\", gchat: \"🎁\" },\n trophy: { slack: \"trophy\", gchat: \"🏆\" },\n medal: { slack: \"first_place_medal\", gchat: \"🥇\" },\n lightbulb: { slack: \"bulb\", gchat: \"💡\" },\n gear: { slack: \"gear\", gchat: \"⚙️\" },\n wrench: { slack: \"wrench\", gchat: \"🔧\" },\n hammer: { slack: \"hammer\", gchat: \"🔨\" },\n bug: { slack: \"bug\", gchat: \"🐛\" },\n link: { slack: \"link\", gchat: \"🔗\" },\n lock: { slack: \"lock\", gchat: \"🔒\" },\n unlock: { slack: \"unlock\", gchat: \"🔓\" },\n key: { slack: \"key\", gchat: \"🔑\" },\n pin: { slack: \"pushpin\", gchat: \"📌\" },\n memo: { slack: \"memo\", gchat: \"📝\" },\n clipboard: { slack: \"clipboard\", gchat: \"📋\" },\n calendar: { slack: \"calendar\", gchat: \"📅\" },\n clock: { slack: \"clock1\", gchat: \"🕐\" },\n hourglass: { slack: \"hourglass\", gchat: \"⏳\" },\n bell: { slack: \"bell\", gchat: \"🔔\" },\n megaphone: { slack: \"mega\", gchat: \"📢\" },\n speech_bubble: { slack: \"speech_balloon\", gchat: \"💬\" },\n email: { slack: \"email\", gchat: \"📧\" },\n inbox: { slack: \"inbox_tray\", gchat: \"📥\" },\n outbox: { slack: \"outbox_tray\", gchat: \"📤\" },\n package: { slack: \"package\", gchat: \"📦\" },\n folder: { slack: \"file_folder\", gchat: \"📁\" },\n file: { slack: \"page_facing_up\", gchat: \"📄\" },\n chart_up: { slack: \"chart_with_upwards_trend\", gchat: \"📈\" },\n chart_down: { slack: \"chart_with_downwards_trend\", gchat: \"📉\" },\n coffee: { slack: \"coffee\", gchat: \"☕\" },\n pizza: { slack: \"pizza\", gchat: \"🍕\" },\n beer: { slack: \"beer\", gchat: \"🍺\" },\n\n // Arrows & Directions\n arrow_up: { slack: \"arrow_up\", gchat: \"⬆️\" },\n arrow_down: { slack: \"arrow_down\", gchat: \"⬇️\" },\n arrow_left: { slack: \"arrow_left\", gchat: \"⬅️\" },\n arrow_right: { slack: \"arrow_right\", gchat: \"➡️\" },\n refresh: { slack: \"arrows_counterclockwise\", gchat: \"🔄\" },\n\n // Nature & Weather\n sun: { slack: \"sunny\", gchat: \"☀️\" },\n cloud: { slack: \"cloud\", gchat: \"☁️\" },\n rain: { slack: \"rain_cloud\", gchat: \"🌧️\" },\n snow: { slack: \"snowflake\", gchat: \"❄️\" },\n rainbow: { slack: \"rainbow\", gchat: \"🌈\" },\n};\n\n/**\n * Emoji resolver that handles conversion between platform formats and normalized names.\n */\nexport class EmojiResolver {\n private readonly emojiMap: Record<string, EmojiFormats>;\n private readonly slackToNormalized: Map<string, string>;\n private readonly gchatToNormalized: Map<string, string>;\n\n constructor(customMap?: EmojiMapConfig) {\n this.emojiMap = { ...DEFAULT_EMOJI_MAP, ...customMap };\n this.slackToNormalized = new Map();\n this.gchatToNormalized = new Map();\n this.buildReverseMaps();\n }\n\n private buildReverseMaps(): void {\n for (const [normalized, formats] of Object.entries(this.emojiMap)) {\n // Build Slack reverse map\n const slackFormats = Array.isArray(formats.slack)\n ? formats.slack\n : [formats.slack];\n for (const slack of slackFormats) {\n this.slackToNormalized.set(slack.toLowerCase(), normalized);\n }\n\n // Build GChat reverse map\n const gchatFormats = Array.isArray(formats.gchat)\n ? formats.gchat\n : [formats.gchat];\n for (const gchat of gchatFormats) {\n this.gchatToNormalized.set(gchat, normalized);\n }\n }\n }\n\n /**\n * Convert a Slack emoji name to normalized EmojiValue.\n * Returns an EmojiValue for the raw emoji if no mapping exists.\n */\n fromSlack(slackEmoji: string): EmojiValue {\n // Remove colons if present (e.g., \":+1:\" -> \"+1\")\n const cleaned = slackEmoji.replace(/^:|:$/g, \"\").toLowerCase();\n const normalized = this.slackToNormalized.get(cleaned) ?? slackEmoji;\n return getEmoji(normalized);\n }\n\n /**\n * Convert a Google Chat unicode emoji to normalized EmojiValue.\n * Returns an EmojiValue for the raw emoji if no mapping exists.\n */\n fromGChat(gchatEmoji: string): EmojiValue {\n const normalized = this.gchatToNormalized.get(gchatEmoji) ?? gchatEmoji;\n return getEmoji(normalized);\n }\n\n /**\n * Convert a Teams reaction type to normalized EmojiValue.\n * Teams uses specific names: like, heart, laugh, surprised, sad, angry\n * Returns an EmojiValue for the raw reaction if no mapping exists.\n */\n fromTeams(teamsReaction: string): EmojiValue {\n const teamsMap: Record<string, string> = {\n like: \"thumbs_up\",\n heart: \"heart\",\n laugh: \"laugh\",\n surprised: \"surprised\",\n sad: \"sad\",\n angry: \"angry\",\n };\n const normalized = teamsMap[teamsReaction] ?? teamsReaction;\n return getEmoji(normalized);\n }\n\n /**\n * Convert a normalized emoji (or EmojiValue) to Slack format.\n * Returns the first Slack format if multiple exist.\n */\n toSlack(emoji: EmojiValue | string): string {\n const name = typeof emoji === \"string\" ? emoji : emoji.name;\n const formats = this.emojiMap[name];\n if (!formats) {\n return name;\n }\n return Array.isArray(formats.slack) ? formats.slack[0] : formats.slack;\n }\n\n /**\n * Convert a normalized emoji (or EmojiValue) to Google Chat format.\n * Returns the first GChat format if multiple exist.\n */\n toGChat(emoji: EmojiValue | string): string {\n const name = typeof emoji === \"string\" ? emoji : emoji.name;\n const formats = this.emojiMap[name];\n if (!formats) {\n return name;\n }\n return Array.isArray(formats.gchat) ? formats.gchat[0] : formats.gchat;\n }\n\n /**\n * Convert a normalized emoji (or EmojiValue) to Discord format (unicode).\n * Discord uses unicode emoji, same as Google Chat.\n */\n toDiscord(emoji: EmojiValue | string): string {\n // Discord uses unicode emoji like GChat\n return this.toGChat(emoji);\n }\n\n /**\n * Check if an emoji (in any format) matches a normalized emoji name or EmojiValue.\n */\n matches(rawEmoji: string, normalized: EmojiValue | string): boolean {\n const name = typeof normalized === \"string\" ? normalized : normalized.name;\n const formats = this.emojiMap[name];\n if (!formats) {\n return rawEmoji === name;\n }\n\n const slackFormats = Array.isArray(formats.slack)\n ? formats.slack\n : [formats.slack];\n const gchatFormats = Array.isArray(formats.gchat)\n ? formats.gchat\n : [formats.gchat];\n\n const cleanedRaw = rawEmoji.replace(/^:|:$/g, \"\").toLowerCase();\n\n return (\n slackFormats.some((s) => s.toLowerCase() === cleanedRaw) ||\n gchatFormats.includes(rawEmoji)\n );\n }\n\n /**\n * Add or override emoji mappings.\n */\n extend(customMap: EmojiMapConfig): void {\n Object.assign(this.emojiMap, customMap);\n this.buildReverseMaps();\n }\n}\n\n/**\n * Default emoji resolver instance.\n */\nexport const defaultEmojiResolver = new EmojiResolver();\n\n/** Placeholder pattern for emoji in text: {{emoji:name}} */\nconst EMOJI_PLACEHOLDER_REGEX = /\\{\\{emoji:([a-z0-9_]+)\\}\\}/gi;\n\n/**\n * Convert emoji placeholders in text to platform-specific format.\n *\n * @example\n * ```typescript\n * convertEmojiPlaceholders(\"Thanks! {{emoji:thumbs_up}}\", \"slack\");\n * // Returns: \"Thanks! :+1:\"\n *\n * convertEmojiPlaceholders(\"Thanks! {{emoji:thumbs_up}}\", \"gchat\");\n * // Returns: \"Thanks! 👍\"\n * ```\n */\nexport function convertEmojiPlaceholders(\n text: string,\n platform: \"slack\" | \"gchat\" | \"teams\" | \"discord\" | \"github\" | \"linear\",\n resolver: EmojiResolver = defaultEmojiResolver\n): string {\n return text.replace(EMOJI_PLACEHOLDER_REGEX, (_, emojiName: string) => {\n switch (platform) {\n case \"slack\":\n return `:${resolver.toSlack(emojiName)}:`;\n case \"gchat\":\n return resolver.toGChat(emojiName);\n case \"teams\":\n // Teams uses unicode emoji\n return resolver.toGChat(emojiName);\n case \"discord\":\n // Discord uses unicode emoji\n return resolver.toDiscord(emojiName);\n case \"github\":\n // GitHub uses unicode emoji\n return resolver.toGChat(emojiName);\n case \"linear\":\n // Linear uses unicode emoji\n return resolver.toGChat(emojiName);\n default:\n return resolver.toGChat(emojiName);\n }\n });\n}\n\n// =============================================================================\n// Emoji Helper Types\n// =============================================================================\n\n/** Base emoji object with well-known emoji as EmojiValue singletons */\ntype BaseEmojiHelper = {\n [K in WellKnownEmoji]: EmojiValue;\n} & {\n /** Create an EmojiValue for a custom emoji name */\n custom: (name: string) => EmojiValue;\n};\n\n/** Extended emoji object including custom emoji from module augmentation */\ntype ExtendedEmojiHelper = BaseEmojiHelper & {\n [K in keyof CustomEmojiMap]: EmojiValue;\n};\n\n/**\n * Create a type-safe emoji helper with custom emoji.\n *\n * Returns immutable singleton EmojiValue objects that support:\n * - Object identity comparison (`event.emoji === emoji.thumbs_up`)\n * - Template string interpolation (`${emoji.thumbs_up}` → \"{{emoji:thumbs_up}}\")\n *\n * Custom emoji are automatically registered with the default resolver,\n * so placeholders will convert correctly in messages.\n *\n * @example\n * ```typescript\n * // First, extend the CustomEmojiMap type (usually in a .d.ts file)\n * declare module \"chat\" {\n * interface CustomEmojiMap {\n * unicorn: EmojiFormats;\n * company_logo: EmojiFormats;\n * }\n * }\n *\n * // Then create the emoji helper with your custom emoji\n * const emoji = createEmoji({\n * unicorn: { slack: \"unicorn_face\", gchat: \"🦄\" },\n * company_logo: { slack: \"company\", gchat: \"🏢\" },\n * });\n *\n * // Object identity works for comparisons\n * if (event.emoji === emoji.unicorn) { ... }\n *\n * // Template strings work for messages\n * await thread.post(`${emoji.unicorn} Magic!`);\n * // Slack: \":unicorn_face: Magic!\"\n * // GChat: \"🦄 Magic!\"\n * ```\n */\nexport function createEmoji<\n T extends Record<\n string,\n { slack: string | string[]; gchat: string | string[] }\n >,\n>(customEmoji?: T): BaseEmojiHelper & { [K in keyof T]: EmojiValue } {\n // All well-known emoji names\n const wellKnownEmoji: WellKnownEmoji[] = [\n // Reactions & Gestures\n \"thumbs_up\",\n \"thumbs_down\",\n \"clap\",\n \"wave\",\n \"pray\",\n \"muscle\",\n \"ok_hand\",\n \"point_up\",\n \"point_down\",\n \"point_left\",\n \"point_right\",\n \"raised_hands\",\n \"shrug\",\n \"facepalm\",\n // Emotions & Faces\n \"heart\",\n \"smile\",\n \"laugh\",\n \"thinking\",\n \"sad\",\n \"cry\",\n \"angry\",\n \"love_eyes\",\n \"cool\",\n \"wink\",\n \"surprised\",\n \"worried\",\n \"confused\",\n \"neutral\",\n \"sleeping\",\n \"sick\",\n \"mind_blown\",\n \"relieved\",\n \"grimace\",\n \"rolling_eyes\",\n \"hug\",\n \"zany\",\n // Status & Symbols\n \"check\",\n \"x\",\n \"question\",\n \"exclamation\",\n \"warning\",\n \"stop\",\n \"info\",\n \"100\",\n \"fire\",\n \"star\",\n \"sparkles\",\n \"lightning\",\n \"boom\",\n \"eyes\",\n // Status Indicators\n \"green_circle\",\n \"yellow_circle\",\n \"red_circle\",\n \"blue_circle\",\n \"white_circle\",\n \"black_circle\",\n // Objects & Tools\n \"rocket\",\n \"party\",\n \"confetti\",\n \"balloon\",\n \"gift\",\n \"trophy\",\n \"medal\",\n \"lightbulb\",\n \"gear\",\n \"wrench\",\n \"hammer\",\n \"bug\",\n \"link\",\n \"lock\",\n \"unlock\",\n \"key\",\n \"pin\",\n \"memo\",\n \"clipboard\",\n \"calendar\",\n \"clock\",\n \"hourglass\",\n \"bell\",\n \"megaphone\",\n \"speech_bubble\",\n \"email\",\n \"inbox\",\n \"outbox\",\n \"package\",\n \"folder\",\n \"file\",\n \"chart_up\",\n \"chart_down\",\n \"coffee\",\n \"pizza\",\n \"beer\",\n // Arrows & Directions\n \"arrow_up\",\n \"arrow_down\",\n \"arrow_left\",\n \"arrow_right\",\n \"refresh\",\n // Nature & Weather\n \"sun\",\n \"cloud\",\n \"rain\",\n \"snow\",\n \"rainbow\",\n ];\n\n // Build the emoji helper object with EmojiValue singletons\n const helper: Record<string, EmojiValue | ((name: string) => EmojiValue)> = {\n custom: (name: string): EmojiValue => getEmoji(name),\n };\n\n // Add all well-known emoji\n for (const name of wellKnownEmoji) {\n helper[name] = getEmoji(name);\n }\n\n // Add custom emoji if provided\n if (customEmoji) {\n for (const key of Object.keys(customEmoji)) {\n helper[key] = getEmoji(key);\n }\n // Extend the default resolver so placeholders convert correctly\n defaultEmojiResolver.extend(customEmoji as EmojiMapConfig);\n }\n\n return helper as BaseEmojiHelper & {\n [K in keyof T]: EmojiValue;\n };\n}\n\n/**\n * Type-safe emoji helper for embedding emoji in messages.\n *\n * @example\n * ```typescript\n * import { emoji } from \"chat\";\n *\n * await thread.post(`Great job! ${emoji.thumbs_up} ${emoji.fire}`);\n * // Slack: \"Great job! :+1: :fire:\"\n * // GChat: \"Great job! 👍 🔥\"\n * ```\n *\n * For custom emoji, use `createEmoji()` with module augmentation:\n * @example\n * ```typescript\n * // types.d.ts\n * declare module \"chat\" {\n * interface CustomEmojiMap {\n * unicorn: EmojiFormats;\n * }\n * }\n *\n * // bot.ts\n * const emoji = createEmoji({ unicorn: { slack: \"unicorn\", gchat: \"🦄\" } });\n * await thread.post(`${emoji.unicorn} Magic!`);\n * ```\n */\nexport const emoji: ExtendedEmojiHelper = createEmoji() as ExtendedEmojiHelper;\n","// Main exports\n\nexport {\n ChannelImpl,\n deriveChannelId,\n type SerializedChannel,\n} from \"./channel\";\nexport { Chat } from \"./chat\";\nexport { fromFullStream } from \"./from-full-stream\";\nexport {\n Message,\n type MessageData,\n type SerializedMessage,\n} from \"./message\";\nexport { StreamingMarkdownRenderer } from \"./streaming-markdown\";\nexport { type SerializedThread, ThreadImpl } from \"./thread\";\n\n// Card builders - import then re-export to ensure values are properly exported\nimport {\n Actions as _Actions,\n Button as _Button,\n Card as _Card,\n CardLink as _CardLink,\n CardText as _CardText,\n cardChildToFallbackText as _cardChildToFallbackText,\n Divider as _Divider,\n Field as _Field,\n Fields as _Fields,\n fromReactElement as _fromReactElement,\n Image as _Image,\n isCardElement as _isCardElement,\n LinkButton as _LinkButton,\n Section as _Section,\n Table as _Table,\n} from \"./cards\";\nimport type {\n ActionsComponent,\n ButtonComponent,\n CardComponent,\n CardLinkComponent,\n DividerComponent,\n FieldComponent,\n FieldsComponent,\n ImageComponent,\n LinkButtonComponent,\n ModalComponent,\n RadioSelectComponent,\n SectionComponent,\n SelectComponent,\n SelectOptionComponent,\n TextComponent,\n TextInputComponent,\n} from \"./jsx-runtime\";\nimport {\n isJSX as _isJSX,\n toCardElement as _toCardElement,\n toModalElement as _toModalElement,\n} from \"./jsx-runtime\";\n\n// Cast to JSX-compatible overloaded types.\n// The `as unknown as` is safe — JSX never calls these directly; the jsx factory handles resolution.\nexport const Actions = _Actions as unknown as ActionsComponent;\nexport const Button = _Button as unknown as ButtonComponent;\nexport const Card = _Card as unknown as CardComponent;\nexport const cardChildToFallbackText = _cardChildToFallbackText;\nexport const CardLink = _CardLink as unknown as CardLinkComponent;\nexport const CardText = _CardText as unknown as TextComponent;\nexport const Divider = _Divider as unknown as DividerComponent;\nexport const Field = _Field as unknown as FieldComponent;\nexport const Fields = _Fields as unknown as FieldsComponent;\nexport const fromReactElement = _fromReactElement;\nexport const Image = _Image as unknown as ImageComponent;\nexport const isCardElement = _isCardElement;\nexport const isJSX = _isJSX;\nexport const LinkButton = _LinkButton as unknown as LinkButtonComponent;\nexport const Section = _Section as unknown as SectionComponent;\nexport const Table = _Table;\nexport const toCardElement = _toCardElement;\nexport const toModalElement = _toModalElement;\n\n// Modal builders\nimport {\n fromReactModalElement as _fromReactModalElement,\n isModalElement as _isModalElement,\n Modal as _Modal,\n RadioSelect as _RadioSelect,\n Select as _Select,\n SelectOption as _SelectOption,\n TextInput as _TextInput,\n} from \"./modals\";\nexport const fromReactModalElement = _fromReactModalElement;\nexport const isModalElement = _isModalElement;\nexport const Modal = _Modal as unknown as ModalComponent;\nexport const RadioSelect = _RadioSelect as unknown as RadioSelectComponent;\nexport const Select = _Select as unknown as SelectComponent;\nexport const SelectOption = _SelectOption as unknown as SelectOptionComponent;\nexport const TextInput = _TextInput as unknown as TextInputComponent;\n\n// Card types\nexport type {\n ActionsElement,\n ButtonElement,\n ButtonOptions,\n ButtonStyle,\n CardChild,\n CardElement,\n CardOptions,\n DividerElement,\n FieldElement,\n FieldsElement,\n ImageElement,\n LinkButtonElement,\n LinkButtonOptions,\n LinkElement,\n SectionElement,\n TableAlignment,\n TableElement,\n TableOptions,\n TextElement,\n TextStyle,\n} from \"./cards\";\n// Emoji utilities\nexport {\n convertEmojiPlaceholders,\n createEmoji,\n DEFAULT_EMOJI_MAP,\n defaultEmojiResolver,\n EmojiResolver,\n type EmojiValue,\n emoji,\n getEmoji,\n} from \"./emoji\";\n// JSX types\nexport type {\n ActionsComponent,\n ButtonComponent,\n ButtonProps,\n CardComponent,\n CardJSXElement,\n CardJSXProps,\n CardLinkComponent,\n CardLinkProps,\n CardProps,\n ChatElement,\n ContainerProps,\n DividerComponent,\n DividerProps,\n FieldComponent,\n FieldProps,\n FieldsComponent,\n ImageComponent,\n ImageProps,\n LinkButtonComponent,\n LinkButtonProps,\n ModalComponent,\n ModalProps,\n RadioSelectComponent,\n SectionComponent,\n SelectComponent,\n SelectOptionComponent,\n SelectOptionProps,\n SelectProps,\n TextComponent,\n TextInputComponent,\n TextInputProps,\n TextProps,\n} from \"./jsx-runtime\";\n// Re-export mdast types for adapters\nexport type {\n Blockquote,\n Code,\n Content,\n Delete,\n Emphasis,\n InlineCode,\n Link,\n List,\n ListItem,\n Paragraph,\n Root,\n Strong,\n Table as MdastTable,\n TableCell,\n TableRow,\n Text,\n} from \"./markdown\";\n// Markdown/AST utilities\nexport {\n // Format converter base class\n BaseFormatConverter,\n blockquote,\n codeBlock,\n emphasis,\n // Types\n type FormatConverter,\n // Type guards for mdast nodes\n getNodeChildren,\n getNodeValue,\n inlineCode,\n isBlockquoteNode,\n isCodeNode,\n isDeleteNode,\n isEmphasisNode,\n isInlineCodeNode,\n isLinkNode,\n isListItemNode,\n isListNode,\n isParagraphNode,\n isStrongNode,\n isTableCellNode,\n isTableNode,\n isTableRowNode,\n isTextNode,\n link,\n type MarkdownConverter,\n markdownToPlainText,\n paragraph,\n // Parsing and stringifying\n parseMarkdown,\n root,\n strikethrough,\n stringifyMarkdown,\n strong,\n tableElementToAscii,\n tableToAscii,\n // AST node builders\n text,\n toPlainText,\n walkAst,\n} from \"./markdown\";\n// Modal types\nexport type {\n ModalChild,\n ModalElement,\n ModalOptions,\n RadioSelectElement,\n RadioSelectOptions,\n SelectElement,\n SelectOptionElement,\n SelectOptions,\n TextInputElement,\n TextInputOptions,\n} from \"./modals\";\n// Types\nexport type {\n ActionEvent,\n ActionHandler,\n Adapter,\n AdapterPostableMessage,\n AppHomeOpenedEvent,\n AppHomeOpenedHandler,\n AssistantContextChangedEvent,\n AssistantContextChangedHandler,\n AssistantThreadStartedEvent,\n AssistantThreadStartedHandler,\n Attachment,\n Author,\n Channel,\n ChannelInfo,\n ChatConfig,\n ChatInstance,\n CustomEmojiMap,\n Emoji,\n EmojiFormats,\n EmojiMapConfig,\n EphemeralMessage,\n FetchDirection,\n FetchOptions,\n FetchResult,\n FileUpload,\n FormattedContent,\n ListThreadsOptions,\n ListThreadsResult,\n Lock,\n Logger,\n LogLevel,\n MarkdownTextChunk,\n MemberJoinedChannelEvent,\n MemberJoinedChannelHandler,\n MentionHandler,\n MessageHandler,\n MessageMetadata,\n ModalCloseEvent,\n ModalCloseHandler,\n ModalCloseResponse,\n ModalErrorsResponse,\n ModalPushResponse,\n ModalResponse,\n ModalSubmitEvent,\n ModalSubmitHandler,\n ModalUpdateResponse,\n PlanUpdateChunk,\n Postable,\n PostableAst,\n PostableCard,\n PostableMarkdown,\n PostableMessage,\n PostableRaw,\n PostEphemeralOptions,\n RawMessage,\n ReactionEvent,\n ReactionHandler,\n ScheduledMessage,\n SentMessage,\n SlashCommandEvent,\n SlashCommandHandler,\n StateAdapter,\n StreamChunk,\n StreamEvent,\n StreamOptions,\n SubscribedMessageHandler,\n TaskUpdateChunk,\n Thread,\n ThreadInfo,\n ThreadSummary,\n WebhookOptions,\n WellKnownEmoji,\n} from \"./types\";\n// Errors and Logger\nexport {\n ChatError,\n ConsoleLogger,\n LockError,\n NotImplementedError,\n RateLimitError,\n THREAD_STATE_TTL_MS,\n} from \"./types\";\n"],"mappings":"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","names":["WORKFLOW_DESERIALIZE","WORKFLOW_SERIALIZE","WORKFLOW_SERIALIZE","WORKFLOW_DESERIALIZE","emoji","WORKFLOW_DESERIALIZE","WORKFLOW_SERIALIZE","text","isLazyConfig","isAsyncIterable","WORKFLOW_SERIALIZE","WORKFLOW_DESERIALIZE","extractMessageContent","postable","emoji","emoji","text","Actions","Button","Card","cardChildToFallbackText","CardLink","CardText","Divider","Field","Fields","fromReactElement","Image","isCardElement","isJSX","LinkButton","Section","Table","toCardElement","toModalElement","fromReactModalElement","isModalElement","Modal","RadioSelect","Select","SelectOption","TextInput"]}
|