跳至主要內容

頻道

新增於: @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(例如 slackdiscord)。如要使用預設值,可直接傳入 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_reactionremove_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 的額外選項。適用於 dedupeTtlMsfallbackStreamingPlaceholderTextlockScopemessageHistory 等進階設定。

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/functionswaitUntil。系統會從 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。解析次序:獨立的 waitUntilresolveWaitUntil(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 執行,只會在 resulterror event 觸發;runningapproval 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 設定自訂狀態文字;傳回字串以設定狀態,或傳回 falsenullundefined 以保持不變。可配合從 @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 會接收 ToolDisplayEventrunningresulterrorapproval) 及 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 一次;傳回字串以設定狀態,或傳回 falsenullundefined 以保持目前狀態不變。傳回值會去重,因此平台只會在狀態改變時 收到呼叫。

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
平台名稱(例如 slackdiscord)。

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
平台名稱(例如 slackdiscord)。

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/pngimage/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)