跳至主要內容

頻道

新增於: @mastra/core@1.22.0

Channels 將 Agent 連接至訊息平台。請透過 Agent constructor 的 channels property 設定。傳入的物件是 ChannelConfig。有關概念及平台設定指引,請參閱 Channels 概覽。

使用範例
使用範例 的直接連結

src/mastra/agents/support-agent.ts
import { Agent } from '@mastra/core/agent'
import { createSlackAdapter } from '@chat-adapter/slack'
import { createDiscordAdapter } from '@chat-adapter/discord'

export const supportAgent = new Agent({
id: 'support-agent',
name: 'Support Agent',
instructions: 'You are a helpful support assistant.',
model: 'openai/gpt-5.6-sol',
channels: {
adapters: {
slack: createSlackAdapter(),
discord: createDiscordAdapter(),
},
},
})

參數
參數 的直接連結

channels property 接受包含以下欄位的 ChannelConfig 物件:

adapters:

Record<string, Adapter | ChannelAdapterConfig>
以名稱作為 key 的平台 adapter(例如 slack、discord)。如要使用預設值,可直接傳入 Adapter;如要自訂各 adapter 的選項,則傳入 ChannelAdapterConfig 物件。

handlers?:

ChannelHandlers
覆寫私訊、提及及已訂閱 thread 的預設訊息 handler。

inlineMedia?:

string[] | ((mimeType: string) => boolean)
= ['image/png', 'image/jpeg', 'image/webp', 'application/pdf']
控制哪些附件類型會以檔案部分傳送至模型。不相符的類型會以文字摘要描述。接受 mime type glob 陣列或 predicate function。預設值符合主要視覺模型支援的格式。

tools?:

boolean
= true
getTools() 是否傳回 channel 專用 Tools(add_reaction、remove_reaction)。對於不支援 function calling 的模型,請設為 false。Channel Tools 絕不會自動加入 Agent;請透過 tools: { ...channels.getTools() } 明確傳入。

state?:

StateAdapter
= MastraStateAdapter(來自 Mastra storage)
用於訂閱及去重的 state adapter。預設使用由 Mastra instance storage 支援的 MastraStateAdapter。Channels 必須先設定 storage。

userName?:

string
= Agent 的 `name`
平台訊息中顯示的 bot 名稱。預設為 Agent 的 name;如未設定名稱,則為 'Mastra'。

threadContext?:

{ maxMessages?: number; addSystemMessage?: boolean }
= { maxMessages: 10, addSystemMessage: true }
Agent 如何取得目前 thread 的 context。maxMessages 控制首次被提及時擷取多少則近期平台訊息(設為 0 可停用;只適用於非私訊 thread)。addSystemMessage: false 會略過內建 system message,該訊息原本會告知 Agent 請求來自哪個 channel/平台。

chatOptions?:

Omit<ChatConfig, 'adapters' | 'state' | 'userName'>
直接傳入 Chat SDK 的額外選項。適用於 dedupeTtlMs、fallbackStreamingPlaceholderText、lockScope 及 messageHistory 等進階設定。

resolveResourceId?:

(ctx: ResolveResourceIdContext) => string | Promise<string>
獨立於訊息傳送者,決定哪個 resourceId 擁有 channel thread 的 resource-level memory。只會在建立新 thread 時執行;重用的 thread 會保留已儲存的擁有者,且不會呼叫 hook。傳回 ctx.defaultResourceId(${platform}:${message.author.userId})即可保留內建行為。

resolveThreadId?:

(ctx: ResolveThreadIdContext) => string | Promise<string>
決定 channel thread 的內部 Mastra thread id。此項會在 resolveResourceId 之後執行,context 會包含已解析的擁有者,而且只會在建立新 thread 時執行;重用的 thread 會保留已儲存的 id,且不會呼叫 hook。傳回的 id 在整個 memory store 中必須是唯一;如有衝突,系統會改用產生的 id。傳回 ctx.defaultThreadId(隨機 UUID)即可保留內建行為。

waitUntil?:

(promise: Promise<unknown>) => void
平台的 waitUntil function。Vercel 必須使用此 function,讓背景 Agent run 在 webhook 傳回 200 後仍可繼續。在 Vercel 上,請傳入來自 @vercel/functions 的 waitUntil。系統會從 request context 自動偵測 Cloudflare Workers 及 Netlify Functions。AWS Lambda 會自然等待 event loop 清空,因此不需要 waitUntil。

resolveWaitUntil?:

(c: Context) => ((promise: Promise<unknown>) => void) | undefined
適用於 waitUntil 位於 Hono request context、但內建 helper 未有涵蓋的 runtime resolver。解析次序:獨立的 waitUntil → resolveWaitUntil(c) → 預設值(Cloudflare Workers、Netlify)。

各 adapter 選項
各 adapter 選項 的直接連結

將 adapter 包裝在 ChannelAdapterConfig 物件內,以設定各 adapter 的選項:

src/mastra/agents/example.ts
import { Agent } from '@mastra/core/agent'
import { createDiscordAdapter } from '@chat-adapter/discord'
import { createSlackAdapter } from '@chat-adapter/slack'

const agent = new Agent({
id: 'example',
name: 'Example',
instructions: '...',
model: 'openai/gpt-5.6-sol',
channels: {
adapters: {
discord: {
adapter: createDiscordAdapter(),
toolDisplay: 'text',
cors: {
origin: ['https://customer-saas.example'],
credentials: true,
},
gateway: false,
},
slack: createSlackAdapter(), // Plain adapter uses defaults
},
},
})

adapter:

Adapter
此平台的 Chat SDK adapter instance。

gateway?:

boolean
= true
啟動持續運作的 Gateway WebSocket listener,以接收私訊、@提及及 reaction。只需 webhook 互動的 serverless 部署可設為 false。

cards?:

boolean
**已棄用** — 請改用 toolDisplay。未設定 toolDisplay 時,cards: true 對應至 toolDisplay: "cards",而 cards: false 對應至 toolDisplay: "text"。IDE 會以刪除線標示此欄位;runtime 行為維持不變。

cors?:

CorsOptions
此 adapter webhook route 的 CORS 設定。需要跨來源 credential 的瀏覽器 channel adapter 可使用此選項。

formatError?:

(error: Error) => PostableMessage
= "❌ 錯誤:<error.message>"
覆寫錯誤在聊天中的呈現方式。請傳回易於理解的訊息,而非公開原始錯誤。

formatToolCall?:

(args: { toolName: string; args: unknown; result: unknown; isError: boolean }) => PostableMessage | null
**已棄用** — 請改用 function 形式的 toolDisplay。設定後會以 ToolDisplayFn 執行,只會在 result/error event 觸發;running 及 approval event 不會呈現。在 type level 上與 toolDisplay 互斥。

streaming?:

boolean | { updateIntervalMs?: number }
= false(Slack 為 true)
Agent 產生文字 delta 時即串流至 channel,而非先緩衝再於每個步驟發佈一次。底層 adapter 必須支援發佈及編輯串流。Slack 預設為 true;其他 adapter 預設為 false。

textFormat?:

'markdown' | 'plain'
= 'markdown'
Agent 最終回覆文字使用的格式。'markdown'(預設值)會以 markdown 發佈回覆:原生支援 markdown 呈現的 adapter(Slack)會直接呈現,其他 adapter 則會轉換成平台格式。'plain' 會以純文字原樣發佈回覆,為被提示輸出 Slack mrkdwn 等平台格式的 Agent 還原採用 markdown 前的行為。只適用於最終回覆文字;Tool card、錯誤訊息及 tripwire 通知不受影響。無論此設定為何,原生串流一律使用 markdown。

toolDisplay?:

'cards' | 'text' | 'timeline' | 'grouped' | 'hidden' | ToolDisplayFn
= 'cards'(Slack 為 'grouped')
控制 Tool call 在 channel 中的呈現方式。"cards" 會以豐富的 Block Kit,為每個 Tool 發佈執行中/結果 card。"text" 會以純文字發佈相同的生命週期(不使用 Block Kit)。"timeline" 及 "grouped" 會將 Tool 狀態以 inline task_update chunk 串流(需要 streaming: true;目前只支援 Slack,其他 adapter 可能會呈現 placeholder)。"hidden" 會靜默執行 Tools。傳入 function 可自行呈現 Tool event;傳回 { kind: "post", message } 以獨立發佈/編輯、傳回 { kind: "stream", chunk } 以推送至串流 widget,或傳回 undefined 以略過該 event。當 chunk 只應套用於使用中的串流 session 時,請在 stream result 加入 openIfEmpty: false。無論採用哪種模式,核准/拒絕提示一律以獨立 card 呈現。

typingStatus?:

boolean | ((chunk: AgentChunkType, ctx: TypingStatusContext) => string | false | null | undefined | void)
= true
控制平台的輸入狀態指示器。true 使用內建預設值(文字為 is typing…、Tool call 為 is calling {tool}…、Tool call 核准為 is waiting for approval…)。false 會完全隱藏輸入狀態;當即時串流 widget(例如 Slack 中的 toolDisplay: "grouped")已顯示進度時尤其適用。傳入 function 可為每個 chunk 設定自訂狀態文字;傳回字串以設定狀態,或傳回 false/null/undefined 以保持不變。可配合從 @mastra/core/channels export 的 defaultTypingStatus,讓未處理的 chunk 使用預設值。

Tool 顯示模式
Tool 顯示模式 的直接連結

toolDisplay 控制 Tool call 在聊天中的呈現方式。預設的 'cards' 會為每個 Tool 發佈「執行中……」card,再以結果編輯該 card,與舊版本的 行為一致。'text' 採用相同生命週期,但不使用豐富的 Block Kit, 適合無法妥善呈現 card 的平台。

'timeline' 及 'grouped' 會把 Tool 狀態連同 Agent 文字,以 inline task_update chunk 串流。這些模式需要 streaming: true,並依賴聊天 adapter 呈現 chunk。Slack 原生支援兩者;其他 adapter 在加入支援前 可能只會呈現 placeholder。如停用 streaming,channel 會記錄一次警告, 並回復使用 'cards'。

'hidden' 會靜默執行 Tools。只有輸入狀態會顯示工作正在進行。

向 toolDisplay 傳入 function,即可完全自訂呈現方式。該 function 會接收 ToolDisplayEvent(running/result/error/approval) 及 ToolDisplayContext({ mode, platform });傳回 { kind: 'post', message } 以獨立發佈/編輯、傳回 { kind: 'stream', chunk } 以推送至 使用中的串流 widget,或傳回 undefined 以略過該 event。

預設情況下,如沒有使用中的串流 session,stream result 會開啟一個 session。 當 chunk 只適用於現有 session 時,請設定 openIfEmpty: false。如沒有使用中的 session,Mastra 會略過該 chunk。靜態 channels 會忽略此選項,並維持現有的 純文字 fallback 行為。

toolDisplay: event => {
if (event.kind !== 'running') return undefined

return {
kind: 'stream',
chunk: {
type: 'task_update',
id: event.toolCallId,
title: event.displayName,
status: 'in_progress',
},
openIfEmpty: false,
}
}

無論採用哪種模式,核准/拒絕提示(requireApproval)一律以獨立 card 呈現,因為 inline task 項目無法包含互動式按鈕。

src/mastra/agents/streaming.ts
import { Agent } from '@mastra/core/agent'
import { createSlackAdapter } from '@chat-adapter/slack'

const agent = new Agent({
id: 'streaming-agent',
name: 'Streaming Agent',
instructions: '...',
model: 'openai/gpt-5.6-sol',
channels: {
adapters: {
slack: {
adapter: createSlackAdapter(),
streaming: true, // already the Slack default
toolDisplay: 'timeline',
},
},
},
})

自訂輸入狀態
自訂輸入狀態 的直接連結

向 typingStatus 傳入 function,以自訂狀態文字。每個 stream chunk 都會 呼叫該 function 一次;傳回字串以設定狀態,或傳回 false/null/ undefined 以保持目前狀態不變。傳回值會去重,因此平台只會在狀態改變時 收到呼叫。

defaultTypingStatus 從 @mastra/core/channels export,讓未處理的 chunk 可以回復使用內建預設值。

src/mastra/agents/custom-typing.ts
import { Agent } from '@mastra/core/agent'
import { defaultTypingStatus } from '@mastra/core/channels'
import { createDiscordAdapter } from '@chat-adapter/discord'

const agent = new Agent({
id: 'custom-typing-agent',
name: 'Custom Typing Agent',
instructions: '...',
model: 'openai/gpt-5.6-sol',
channels: {
adapters: {
discord: {
adapter: createDiscordAdapter(),
typingStatus: (chunk, ctx) => {
if (chunk.type === 'tool-call' && chunk.payload.toolName === 'searchDocs') {
return 'is searching docs…'
}
return defaultTypingStatus(chunk, ctx)
},
},
},
},
})

處理器
處理器 的直接連結

覆寫內建 event handler。每個 handler 可以是:

  • 省略:使用預設 Mastra handler(透過 Agent 路由訊息並發佈回覆)
  • false:完全停用 handler
  • Function (thread, message, defaultHandler) => Promise<void>:包裝或取代預設 handler
src/mastra/agents/custom-handlers.ts
import { Agent } from '@mastra/core/agent'
import { createSlackAdapter } from '@chat-adapter/slack'

const agent = new Agent({
id: 'custom-handler-agent',
name: 'Custom Handler Agent',
instructions: '...',
model: 'openai/gpt-5.6-sol',
channels: {
adapters: {
slack: createSlackAdapter(),
},
handlers: {
onMention: async (thread, message, defaultHandler) => {
console.log('Received mention:', message.text)
await defaultHandler(thread, message)
},
onDirectMessage: false,
},
},
})

onDirectMessage?:

ChannelHandler | false
當 bot 收到私訊時呼叫。

onMention?:

ChannelHandler | false
當 bot 在 channel 或 thread 中被 @提及時呼叫。

onSubscribedMessage?:

ChannelHandler | false
收到 Agent 已訂閱 thread 中的訊息時呼叫。

ChannelHandler function signature:

type ChannelHandler = (
thread: Thread,
message: Message,
defaultHandler: (thread: Thread, message: Message) => Promise<void>,
ctx: ChannelHandlerContext,
) => Promise<void>

type ChannelHandlerContext = {
mastra?: Mastra
requestContext: RequestContext
}

ctx.mastra 是已解析的 mastra instance,因此無需傳入外部 accessor,handler 亦可存取 storage 或其他已註冊 primitive:

onDirectMessage: async (thread, message, defaultHandler, ctx) => {
const store = await ctx.mastra?.getStorage()?.getStore('memory')
await defaultHandler(thread, message)
}

ctx.requestContext 是此訊息即將啟動的 run 所使用的 RequestContext,每則訊息都會重新建立。請在呼叫 defaultHandler 前寫入;該值會連同 Mastra 隨後加入的 channel 項目傳至 run:

onDirectMessage: async (thread, message, defaultHandler, ctx) => {
ctx.requestContext.set('locale', 'en-GB')
await defaultHandler(thread, message)
}

透過此方式,run 從 request context 讀取的任何資料都可按訊息決定,例如平台傳送者對應至哪位使用者。

Resource ID 解析
Resource ID 解析 的直接連結

Channel thread 的 memory resourceId 預設為 ${platform}:${message.author.userId}。傳送者擁有該 memory,而範圍按平台劃分。對於使用共享身分的應用程式(例如單一登入(SSO)),這會拆分 memory:同一使用者在飛書私訊中會取得 feishu:user_123,但在網頁上則是 user_123。

傳入 resolveResourceId,即可獨立於傳送者決定 memory 擁有權。此 function 只會在建立新 thread 時執行。重用的 thread 會保留已儲存的 resourceId,且不會呼叫 hook,因此現有對話不依賴 resolver 是否可用。傳回 ctx.defaultResourceId 即可回復使用內建行為。

src/mastra/agents/sso-agent.ts
import { Agent } from '@mastra/core/agent'
import { createSlackAdapter } from '@chat-adapter/slack'

const agent = new Agent({
id: 'sso-agent',
name: 'SSO Agent',
instructions: '...',
model: 'openai/gpt-5.6-sol',
channels: {
adapters: {
slack: createSlackAdapter(),
},
resolveResourceId: async ({ thread, message }) => {
// DM: share resource-level memory with the web app by using the bare SSO id
if (thread.isDM) {
return await resolveSsoUserId(message)
}
// Group chat: the conversation owns the memory; the sender stays the actor
return thread.channelId
},
},
})

傳入該 function 的 ResolveResourceIdContext:

platform:

string
平台名稱(例如 slack、discord)。

thread:

Thread
訊息送達的 channel thread。使用 thread.isDM 區分私訊與群組/channel thread。

message:

Message
傳入的訊息。message.author.userId 是操作者/傳送者,不一定是 memory 擁有者。

defaultResourceId:

string
內建預設值(${platform}:${message.author.userId})。傳回此值可保留目前行為。

Thread ID 解析
Thread ID 解析 的直接連結

新的 channel thread 預設會取得隨機 UUID,作為內部 Mastra thread id。傳入 resolveThreadId 可自行選擇 id;例如,讓 thread 採用其所屬 session 的相同 id,以配合應用程式為自行建立的 thread 命名的方式。

Hook 會在 resolveResourceId 後執行,因此 context 中可取得已解析的擁有者。與 resolveResourceId 一樣,此 hook 只會在建立新 thread 時執行;重用的 thread 會保留已儲存的 id,且不會呼叫 hook。傳回的 id 在整個 memory store 中必須是唯一。如該 id 已屬於現有 thread,Mastra 會記錄警告並改用產生的 id,確保不會覆寫現有 thread。傳回 ctx.defaultThreadId 即可保留內建行為。

src/mastra/agents/session-agent.ts
import { Agent } from '@mastra/core/agent'
import { createSlackAdapter } from '@chat-adapter/slack'

const agent = new Agent({
id: 'session-agent',
name: 'Session Agent',
instructions: '...',
model: 'openai/gpt-5.6-sol',
channels: {
adapters: {
slack: createSlackAdapter(),
},
// Owner: a session id resolved from the sender's linked account
resolveResourceId: async ctx => resolveSessionId(ctx),
// Thread id: align with the session id so app URLs that address
// threads by session id resolve channel-created threads too
resolveThreadId: ({ resourceId, defaultThreadId }) => {
return isSessionId(resourceId) ? resourceId : defaultThreadId
},
},
})

傳入該 function 的 ResolveThreadIdContext:

platform:

string
平台名稱(例如 slack、discord)。

thread:

Thread
訊息送達的 channel thread。使用 thread.isDM 區分私訊與群組/channel thread。

message:

Message
傳入的訊息。

resourceId:

string
新 thread 將會所屬的已解析 memory resourceId(在 resolveResourceId 後)。

defaultThreadId:

string
內建預設值(隨機 UUID)。傳回此值可保留目前行為。

Inline 媒體
Inline 媒體 的直接連結

控制哪些附件類型(圖片、影片、PDF 等)會以檔案部分傳送至模型。不相符的類型會以文字摘要描述,讓 Agent 得知該檔案,同時避免拒絕不支援類型的模型發生錯誤。

預設值(['image/png', 'image/jpeg', 'image/webp', 'application/pdf'])符合主要視覺模型支援的格式。覆寫 inlineMedia 可擴充清單(例如 ['image/*', 'audio/*']),或以 predicate function 完全取代清單。

支援的 glob pattern:

Pattern相符類型
image/*所有圖片類型(image/png、image/jpeg 等)
video/*所有影片類型
* 或 */*所有類型
application/pdf完全相符的類型

對於使用私人 CDN 的平台(例如 Slack),Chat SDK 會使用已驗證的 credential 擷取附件。對於使用公開 CDN 的平台(例如 Discord),URL 會直接傳送至模型。

將訊息文字中的 URL 提升為檔案部分,讓模型可以處理連結內容,而非只看到原始 URL 文字。每個項目可以是字串(domain pattern),或包含強制 mime type 的物件。

字串項目會比對 domain,並執行 HEAD request 以偵測 Content-Type。系統會按 inlineMedia 檢查解析出的類型,只有相符類型才會成為檔案部分。

物件項目會比對 domain 並強制指定 mime type,略過 HEAD request 及 inlineMedia 檢查。這適合 YouTube 等網站:HEAD request 會傳回 text/html,但模型會將 URL 視為影片內容。

type InlineLinkEntry =
| string // Domain pattern (HEAD determines mime type)
| { match: string; mimeType: string } // Domain + forced mime type (skips HEAD)