UNPKG

@mastra/core

Version:
292 lines 13.8 kB
import type { Chat, Adapter, Thread } 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, OutputProcessor, OutputProcessorOrWorkflow } from '../processors/index.js'; import type { ApiRoute } from '../server/types.js'; import type { ChatChannelRenderContext } from './output-processor.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<string, Adapter>; 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(); * 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; /** * Tool-approval cards that have been posted and are awaiting user action. When the user * clicks approve/decline, the `onAction` handler looks up the card's `messageId` and * tool metadata here so it can edit the card in place and resume the run with the right * context. Entries are removed after the resume completes. */ 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<any, any, any, any>): 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<void>; /** * 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<unknown>) => void; }): Promise<Response>; /** * 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 channel output processors that render the agent's stream to the * originating chat platform. The processor resolves its render context from * the inbound `requestContext` marker set by `processChatMessage` when * present, and otherwise reconstructs it from the run's thread via the bound * `AgentChannels` (so schedule / Studio / custom-UI runs on a channel-backed * thread still post back). Non-channel runs pass through untouched. * * Skipped if the user already added a processor with the same id. */ /** * @deprecated No longer needed — `AgentChannels` no longer holds stateful resources that require cleanup. * Kept as a no-op for backwards compatibility with existing `ChannelProvider` implementations. */ close(): void; getOutputProcessors(configuredProcessors?: OutputProcessorOrWorkflow[]): OutputProcessor[]; /** * Returns generic channel tools (send_message, add_reaction, etc.) * that resolve the target adapter from the current request context. */ getTools(): Record<string, unknown>; /** * 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 * `<user messageId=... authorId=... />`). Strings only. * - `providerOptions` — written to the stored message's `content.providerMetadata` * under `mastra.channels.<platform>` 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; /** * Build the per-event render dependencies stashed on `requestContext` for * `ChatChannelOutputProcessor`. Captures the adapter, driver mode, * tool-display config, approval-card stash callbacks, and the typing-status * wrapper as a callable so the processor can apply it after the queue is * created. The returned object is plain data — no streams, no promises — * so it's safe to stash on `requestContext` for the processor to read later. * * @internal Used by `processChatMessage` and the approve/decline paths. */ _buildRenderContext(chatThread: Thread, platform: string, approvalContext?: { toolCallId: string; messageId: string; }): ChatChannelRenderContext; /** * Reconstruct a {@link ChatChannelRenderContext} for a Mastra thread that is * backed by a channel, without an inbound platform event. * * The inbound webhook paths (`processChatMessage`, approve/decline) stash a * render context on `requestContext` because they already hold the live * `Thread` handle from `event.thread`. Runs that did NOT originate from a * platform message (schedule fires, Studio, custom UI, user code) have no such * handle, so `ChatChannelOutputProcessor` calls this to rebuild it from the * thread's persisted channel coordinates. * * Returns `null` when the thread is not channel-backed (no `channel_platform` * metadata) or its platform adapter isn't configured on this instance — the * processor then passes the run through untouched. * * Delegates to the same {@link _buildRenderContext} used by the inbound paths, * so both paths produce an identical render context (single source of truth). * The only per-fire inputs are `platform` (a persisted string) and the live * `Thread` handle, which the Chat SDK materializes from the stored external id * via `chat.thread(externalThreadId)`. */ buildRenderContextForThread(threadId: string): Promise<ChatChannelRenderContext | null>; /** * 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