import type { Chat, Adapter } from 'chat'; import type { Agent } from '../agent/agent.js'; import type { IMastraLogger } from '../logger/logger.js'; import type { Mastra } from '../mastra/index.js'; import type { InputProcessor, InputProcessorOrWorkflow } from '../processors/index.js'; import type { ApiRoute } from '../server/types.js'; import type { ChannelAdapterConfig, ChannelConfig } from './types.js'; /** * Manages a single Chat SDK instance for an agent, wiring all adapters * to the Mastra pipeline (thread mapping → agent.stream → thread.post). * * One AgentChannels = one bot identity across multiple platforms. * * @internal Created automatically by the Agent when `channels` config is provided. */ export declare class AgentChannels { readonly adapters: Record; private chat; /** Stored initialization promise so webhook handlers can await readiness on serverless cold starts. */ private initPromise; private agent; private logger?; private customState; private stateAdapter; private userName; /** Normalized per-adapter configs (gateway flags, hooks, etc.). */ private adapterConfigs; /** Handler overrides from config. */ private handlerOverrides; /** Additional Chat SDK options. */ private chatOptions; /** Thread context config for fetching prior messages. */ private threadContext; /** Determines whether a mime type should be sent inline to the model. */ private shouldInline; /** Inline-link rules for promoting URLs in message text to file parts. */ private inlineLinkRules; /** Whether channel tools (reactions, etc.) are enabled. */ private toolsEnabled; /** Optional hook to resolve the memory resourceId (owner) for newly-created channel threads. */ private resolveResourceId; /** * The original `ChannelConfig` passed to the constructor. * * Useful for rebuilding `AgentChannels` while preserving existing adapters/handlers, * e.g. when a `ChannelProvider` wants to inject its own adapter without clobbering * adapters configured by the agent author: * * @example * ```ts * const existing = agent.getChannels(); * existing?.close(); * const next = new AgentChannels({ * ...existing?.channelConfig, * adapters: { ...existing?.channelConfig.adapters, slack: slackAdapter }, * }); * agent.setChannels(next); * ``` */ readonly channelConfig: ChannelConfig; /** Channel tool names whose effects are already visible on the platform (skip rendering cards). */ private channelToolNames; /** Platforms whose routes are managed externally (e.g., by SlackProvider). */ private externallyManagedPlatforms; /** * Per-Mastra-thread subscriptions. We lazily open one `agent.subscribeToThread()` per channel * thread on the first message we route through it, so any signals we send (and any signals * other callers send to the same thread) are rendered exactly once to the platform. The * subscription stays open until `close()` is called or the consumer errors out — we don't * eagerly subscribe at startup because the per-thread chunk consumer needs the `chatThread` * handle, which only exists after a platform event arrives. */ private threadSubscriptions; /** * Tool-approval cards that have been clicked and are about to be resumed via `approveToolCall` / * `declineToolCall`. The resumed run's `tool-result` chunks arrive through the thread * subscription consumer rather than the click handler, so we stash the approval card's * platform `messageId` (plus the tool's display metadata) here for the consumer to pick up * when it renders the result. Entries are removed as soon as the consumer consumes them. */ private pendingApprovalCards; /** * Platforms we've already warned about for misconfigured `toolDisplay` (e.g. * `'timeline'` without `streaming: true`). Keeps log output to one warn per * platform per AgentChannels instance. */ private warnedToolDisplayFallback; constructor(config: ChannelConfig); /** * Bind this AgentChannels to its owning agent. Called by Agent constructor. * @internal */ __setAgent(agent: Agent): void; /** * Set the logger. Called by Mastra.addAgent. * @internal */ __setLogger(logger: IMastraLogger): void; /** * Register an adapter dynamically. * When `managesRoutes` is true, AgentChannels will NOT create webhook routes for this platform * (the ChannelProvider handles routing and calls handleWebhookEvent directly). * @internal */ __registerAdapter(platform: string, adapter: Adapter, config?: ChannelAdapterConfig, options?: { managesRoutes?: boolean; }): void; /** * Check if an adapter is registered for the given platform. */ hasAdapter(platform: string): boolean; /** * Get the underlying Chat SDK instance. * Available after Mastra initialization. Use this to register additional * event handlers or access adapter-specific methods. * * @example * ```ts * agent.channels.sdk.onReaction((thread, reaction) => { * console.log('Reaction received:', reaction); * }); * ``` */ get sdk(): Chat | null; /** * Initialize the Chat SDK, register handlers, and start gateway listeners. * Called by Mastra.addAgent after the server is ready. */ initialize(mastra: Mastra): Promise; /** * Returns API routes for receiving webhook events from each adapter. * One POST route per adapter at `/api/agents/{agentId}/channels/{platform}/webhook`. * Skips platforms that are externally managed (e.g., by SlackProvider). */ getWebhookRoutes(): ApiRoute[]; /** * Handle a webhook event from an external source (e.g., SlackProvider). * Use this when a ChannelProvider manages its own routes but wants AgentChannels * to process the actual message handling (threading, agent responses, etc.). * * @param platform - The platform name (e.g., 'slack') * @param request - The raw HTTP request * @param options - Optional execution context for serverless environments * @returns The response from the Chat SDK webhook handler */ handleWebhookEvent(platform: string, request: Request, options?: { waitUntil?: (p: Promise) => void; }): Promise; /** * Returns channel input processors (e.g. system prompt injection). * * - Skipped entirely when `channels.threadContext.addSystemMessage` is `false`. * - Skipped if the user already added a processor with the same id. */ getInputProcessors(configuredProcessors?: InputProcessorOrWorkflow[]): InputProcessor[]; /** * Returns generic channel tools (send_message, add_reaction, etc.) * that resolve the target adapter from the current request context. */ getTools(): Record; /** * Tear down all live thread subscriptions opened by this AgentChannels. Safe to call * multiple times. Useful for tests and for graceful shutdown of long-lived processes — * each cached subscription holds a handler in the agent's thread-stream runtime that * would otherwise stay registered for the lifetime of the process. */ close(): void; /** * Resolve the adapter for the current conversation from request context. */ private getAdapterFromContext; /** * Derive the three per-event shapes we hand off to downstream systems from one set of * inputs. Keeping this in one place ensures the LLM (`attributes`), input processors * (`requestContext`), and memory (`metadata`) all see consistent author / thread facts. * * - `channelContext` — goes on `requestContext` under the 'channel' key, consumed by * `ChatChannelProcessor` and other input processors. * - `attributes` — serialized as XML on the user message element the LLM sees (e.g. on * ``). Strings only. * - `providerOptions` — written to the stored message's `content.providerMetadata` * under `mastra.channels.` so UI/query callers can read author/channel * facts off the message (e.g. show a Slack icon + author name) without unpacking * the signal envelope. The LLM ignores `providerOptions.mastra.*` since only * provider-keyed entries (openai, anthropic, …) are forwarded to the model. */ /** * Resolve the external thread id to use when looking up a Mastra thread for * a tool-approval flow. Dispatches to per-platform compat shims that work * around quirks in how adapters surface threading on inbound action events. * Add new platform branches here as their compat shims land in `./compat/*`. */ private resolveExternalThreadId; private buildEventContext; /** * Core handler wired to Chat SDK's onDirectMessage, onNewMention, * and onSubscribedMessage. Streams the Mastra agent response and * updates the channel message in real-time via edits. */ private handleChatMessage; private processChatMessage; /** * Fetch recent messages from the platform thread to provide context. * Returns messages in chronological order (oldest first), excluding the * current triggering message. */ private fetchThreadHistory; /** * Lazily open (and cache) an `agent.subscribeToThread()` for a Mastra thread, attaching a * background chunk consumer that renders run output to the originating chat platform. We * cache by `mastraThreadId` so multiple incoming messages on the same thread share one * subscription and run output is never rendered twice. * * If the underlying consumer throws (e.g. the platform `chatThread` becomes unusable), we * tear down the cache entry so the next message can reopen a fresh subscription. */ private ensureThreadSubscription; private consumeAgentStream; /** * Normalize the per-adapter `streaming` option (`boolean | { updateIntervalMs? }`) * into a flat `{ enabled, options }` shape so call-sites don't have to * re-derive both from the raw union. */ private resolveStreaming; /** * Pass-through async generator that yields chunks unchanged but emits * typing-status updates (`startTyping`) along the way. Lives outside the * drivers so both drivers benefit from the same dedup + gate logic. * * The streaming driver flips `typingGate.active = true` while a * `StreamingPlan` post is in flight — Slack's `assistant.threads.setStatus` * (what `startTyping` maps to) only auto-clears on `chat.postMessage`, not * on `chat.stopStream`, so a status set during streaming would stick after * the run ends. The static driver leaves the gate `false` so typing works * normally in cards/hidden modes. */ private withTypingStatus; /** * Resolves an existing Mastra thread for the given external IDs, or creates one. */ private getOrCreateThread; /** * Generate generic channel tools that resolve the adapter from request context. * Tool names are platform-agnostic (e.g. `send_message`, not `discord_send_message`). */ private makeChannelTools; /** * Persistent reconnection loop for Gateway-based adapters (e.g. Discord). */ private startGatewayLoop; /** * Resolve the tool-display mode for a run. * * - `'timeline'` / `'grouped'` push `task_update` chunks into a streaming * Plan widget, so they require `streaming: true`. Without streaming we * fall back to `'cards'`. * - `'cards'` posts discrete Block-Kit cards via `chatThread.post`/`edit`, * which the streaming driver doesn't render (everything inside a * `StreamingPlan` post is one message). With streaming enabled we fall * back to `'timeline'`. * * Both fallbacks log a one-time warning per platform so the misconfiguration * is visible without spamming on every run. */ private resolveToolDisplay; private log; } //# sourceMappingURL=agent-channels.d.ts.map