Channels
Slack, webhook, and chat channel integrations.
Classes
ChatToolset
Defined in: src/channels/chatToolset.ts:367
Toolset exposing Chat SDK's outbound primitives to a Maniac agent.
Construct once and pass via app.agent({ toolsets: [new ChatToolset({ chat })] }).
All write tools accept a platform argument so a single agent can
fan messages out to every adapter on the same Chat instance.
Extends
Constructors
Constructor
new ChatToolset(
options):ChatToolset
Defined in: src/channels/chatToolset.ts:370
Parameters
options
Returns
Overrides
Properties
name
readonlyname:string
Defined in: src/tools/base.ts:135
Inherited from
description
readonlydescription:string
Defined in: src/tools/base.ts:136
Inherited from
Methods
listTools()
listTools():
Promise<Tool[]>
Defined in: src/channels/chatToolset.ts:382
Returns
Promise<Tool[]>
Overrides
searchTools()
searchTools(
query,k?):Promise<Tool[]>
Defined in: src/tools/base.ts:149
Parameters
query
string
k?
number = 10
Returns
Promise<Tool[]>
Inherited from
invoke()
invoke(
toolName,args):Promise<{ok:boolean;value?:unknown;error?:string|null;metadata:Record<string,unknown>;cause?: {kind:"validation"|"runtime";name?:string;message?:string;stack?:string|null;code?:string|null;metadata?:JsonDict; } |null; }>
Defined in: src/tools/base.ts:162
Parameters
toolName
string
args
Returns
Promise<{ ok: boolean; value?: unknown; error?: string | null; metadata: Record<string, unknown>; cause?: { kind: "validation" | "runtime"; name?: string; message?: string; stack?: string | null; code?: string | null; metadata?: JsonDict; } | null; }>
Inherited from
asDelegated()
asDelegated(
options?):Tool
Defined in: src/tools/base.ts:171
Parameters
options?
DelegatedToolOptions = {}
Returns
Inherited from
InMemoryIdempotencyStore
Defined in: src/channels/idempotency.ts:54
Bounded in-memory implementation. Tracks expiry per entry and evicts the oldest insertion when the cap is hit. Suitable for single-process deployments and for tests; multi-process callers must plug in a shared backend.
Implements
Constructors
Constructor
new InMemoryIdempotencyStore(
options?):InMemoryIdempotencyStore
Defined in: src/channels/idempotency.ts:58
Parameters
options?
maxEntries?
number
Returns
Methods
claim()
claim(
key,ttlSeconds):Promise<boolean>
Defined in: src/channels/idempotency.ts:62
Atomically record key (with a per-entry TTL in seconds) if it is
not already present, and report whether this call was the one to
claim it.
- Returns
truewhen the key was newly recorded (first delivery — the caller should process it). - Returns
falsewhen the key was already present (a duplicate — the caller should skip / short-circuit).
The single op closes the check-then-act (TOCTOU) race a separate
has + set pair exposes: when two duplicate deliveries land
concurrently, both can miss has and both run. Implementations
MUST be atomic per key — for the same key, exactly one in-flight
claim may return true. Maps directly onto Redis
SET key <val> NX EX <ttlSeconds> (reply OK => true, nil =>
false) for multi-process / multi-pod production stores.
Entries older than their TTL must be evicted before a later claim
treats them as present.
Parameters
key
string
ttlSeconds
number
Returns
Promise<boolean>
Implementation of
clear()
clear():
void
Defined in: src/channels/idempotency.ts:86
Test-only: drop everything.
Returns
void
size()
size():
number
Defined in: src/channels/idempotency.ts:91
Test-only: report current size.
Returns
number
ChannelsServer
Defined in: src/channels/serveChannels.ts:196
Container returned by serveChannels.
handle(event) is the inbound entry point. handleApprovalCallback
resumes a paused run from a card click.
Constructors
Constructor
new ChannelsServer(
app,agentId,options):ChannelsServer
Defined in: src/channels/serveChannels.ts:212
Parameters
app
agentId
string
options
Returns
Properties
agentId
readonlyagentId:string
Defined in: src/channels/serveChannels.ts:200
Public agent id this server is bound to. Exposed so the webhook helper can reject mismatched URL paths with a 404 before any application code runs.
Methods
resolveThread()
resolveThread(
event):ThreadIdResolution
Defined in: src/channels/serveChannels.ts:220
Resolve a ChannelEvent into Maniac thread / resource ids.
Parameters
event
Returns
handle()
handle(
event):Promise<AgentResult<unknown>>
Defined in: src/channels/serveChannels.ts:232
Drive a single inbound event through the agent and post the response back. Returns the terminal AgentResult for inspection. Rethrows on errors after invoking ChannelsOptions.onError; framework handlers should catch to translate into HTTP responses.
Parameters
event
Returns
Promise<AgentResult<unknown>>
postText()
staticpostText(thread,text):Promise<void>
Defined in: src/channels/serveChannels.ts:388
Convenience helper: render a single text reply onto a thread without going through the full agent loop. Useful for adapters that need to ack a webhook synchronously before kicking off the agent on a side queue.
Parameters
thread
text
string
Returns
Promise<void>
handleApprovalCallback()
handleApprovalCallback(
payload):Promise<{result:AgentResult<unknown> |null;callback:ApprovalCallback;deduped:boolean; } |null>
Defined in: src/channels/serveChannels.ts:419
Resume a paused run from a Chat SDK card-click callback.
Returns null when the payload is not one of our cards (e.g.
another bot's interaction); the caller should ignore.
Idempotency: callbacks are deduped on
(agentId, checkpointId, pendingId) — deliberately excluding the
decision so a second action of any kind (including a stale
deny-after-approve) is a no-op — through the configured
ChannelsOptions.idempotencyStore (defaults to an
in-memory store). A duplicate click returns
{ deduped: true, callback, result: null } instead of resuming.
The underlying Maniac.resumeCheckpoint ALSO defends with
a compare-and-swap claim on the checkpoint store, so a race that
slips past the application-layer dedup throws
CheckpointNotPendingError which we swallow and report as
a dedup.
Streaming: on a successful claim we drive Maniac.resumeCheckpointStream and pipe the resumed response back to the originating thread via ChatLike.threads.getById. When the thread can't be resolved we still resume (so the durable run completes) but skip the post — never throw out of this handler because of a Chat SDK adapter quirk.
Parameters
payload
actionId?
string | null
value?
string | null
metadata?
Record<string, unknown> | null
Returns
Promise<{ result: AgentResult<unknown> | null; callback: ApprovalCallback; deduped: boolean; } | null>
Interfaces
ApprovalCardContext
Defined in: src/channels/approvals.ts:35
Properties
agentId
agentId:
string
Defined in: src/channels/approvals.ts:36
threadId
threadId:
string
Defined in: src/channels/approvals.ts:37
checkpoint
checkpoint:
object
Defined in: src/channels/approvals.ts:38
schema_version
schema_version:
1|2
spec_id
spec_id:
string
iteration
iteration:
number
usage
usage:
object
usage.prompt
prompt:
number
usage.completion
completion:
number
usage.cost_usd?
optionalcost_usd?:number|null
usage.cache_creation_input_tokens?
optionalcache_creation_input_tokens?:number|null
usage.cache_read_input_tokens?
optionalcache_read_input_tokens?:number|null
root_messages
root_messages:
object[]
subagent_sessions
subagent_sessions:
object[]
pending
pending:
object[]
checkpoint_id?
optionalcheckpoint_id?:string|null
pending
pending:
object
Defined in: src/channels/approvals.ts:39
id
id:
string
principal
principal:
string
toolset
toolset:
string
tool
tool:
string
args
args:
JsonDict
rule_id?
optionalrule_id?:string|null
reason?
optionalreason?:string|null
requested_at
requested_at:
string
sub_actions
sub_actions:
object[]
Per-sub-call breakdown for batched delegated_tool invocations
(Tier 1 #2). Empty list keeps the legacy single-call shape; when
non-empty, the caller must populate
ApprovalResponse.sub_decisions to cover every sub-id (or
resume fails with a missing-decision error).
platformThreadId?
optionalplatformThreadId?:string|null
Defined in: src/channels/approvals.ts:46
Native platform thread reference (un-prefixed). Persisted in the
card metadata so the resume callback can resolve the originating
thread via the real adapter id rather than the composite
{platform}:{ref} threadId.
ApprovalCallback
Defined in: src/channels/approvals.ts:115
Information extracted from a Chat SDK button-click callback.
Properties
agentId
agentId:
string
Defined in: src/channels/approvals.ts:116
threadId
threadId:
string
Defined in: src/channels/approvals.ts:117
checkpointId
checkpointId:
string
Defined in: src/channels/approvals.ts:118
decision
decision:
"approve"|"deny"
Defined in: src/channels/approvals.ts:119
pendingId
pendingId:
string
Defined in: src/channels/approvals.ts:120
platformThreadId?
optionalplatformThreadId?:string|null
Defined in: src/channels/approvals.ts:126
Native platform thread reference recovered from the card metadata, when the card was rendered with one. Absent for legacy cards that only carried the composite threadId.
IdempotencyStore
Defined in: src/channels/idempotency.ts:23
Idempotency store for the channels integration.
Two call sites use this:
- buildWebhookHandler dedupes platform webhook retries
(Slack re-delivers an event when our 200 doesn't make it back
inside 3s; without dedupe each retry kicks off a fresh
chatStreamrun and the user sees the agent post twice). - ChannelsServer.handleApprovalCallback dedupes
double-clicked approval cards keyed on
(checkpointId, pendingId, decision).
The default InMemoryIdempotencyStore is suitable for
single-process deployments. Production deployments with multiple
processes / pods MUST plug in a Redis-backed (or similar) store —
an in-memory cap won't help if the same delivery hits a different
pod. Both call sites accept an optional idempotencyStore so the
user can swap implementations without touching the rest of the
surface.
Methods
claim()
claim(
key,ttlSeconds):Promise<boolean>
Defined in: src/channels/idempotency.ts:45
Atomically record key (with a per-entry TTL in seconds) if it is
not already present, and report whether this call was the one to
claim it.
- Returns
truewhen the key was newly recorded (first delivery — the caller should process it). - Returns
falsewhen the key was already present (a duplicate — the caller should skip / short-circuit).
The single op closes the check-then-act (TOCTOU) race a separate
has + set pair exposes: when two duplicate deliveries land
concurrently, both can miss has and both run. Implementations
MUST be atomic per key — for the same key, exactly one in-flight
claim may return true. Maps directly onto Redis
SET key <val> NX EX <ttlSeconds> (reply OK => true, nil =>
false) for multi-process / multi-pod production stores.
Entries older than their TTL must be evicted before a later claim
treats them as present.
Parameters
key
string
ttlSeconds
number
Returns
Promise<boolean>
ChatSdkMessageLike
Defined in: src/channels/schemas.ts:24
Minimal view of a Chat SDK message. Covers the fields we read in toManiacMessages and the multimodal/multi-user helpers; any adapter-specific extras pass through unchanged.
Properties
id?
optionalid?:string|null
Defined in: src/channels/schemas.ts:26
Stable per-thread id assigned by the platform / adapter.
role?
optionalrole?:string
Defined in: src/channels/schemas.ts:28
Logical role of the speaker.
text?
optionaltext?:string|null
Defined in: src/channels/schemas.ts:30
Best-effort plain-text body. Part-form messages may leave this empty.
parts?
optionalparts?: readonlyChatSdkPartLike[] |null
Defined in: src/channels/schemas.ts:32
Optional structured content (Chat SDK uses an array of "parts").
user?
optionaluser?:ChatSdkUserLike|null
Defined in: src/channels/schemas.ts:34
Sender attribution. Populated for inbound messages.
threadId?
optionalthreadId?:string|null
Defined in: src/channels/schemas.ts:36
Thread / channel attribution.
platform?
optionalplatform?:string|null
Defined in: src/channels/schemas.ts:38
Adapter that produced this message ("slack", "discord", ...).
createdAt?
optionalcreatedAt?:string|null
Defined in: src/channels/schemas.ts:40
Wall-clock time the message was created at, ISO-8601.
metadata?
optionalmetadata?:Record<string,unknown> |null
Defined in: src/channels/schemas.ts:42
Adapter-specific extras propagated through unchanged.
ChatSdkPartLike
Defined in: src/channels/schemas.ts:46
Multimodal content part attached to a Chat SDK message.
Properties
type
type:
string
Defined in: src/channels/schemas.ts:54
Discriminator. Chat SDK officially uses "text", "image",
"file", "video", "audio", plus adapter-specific extensions.
We map the first three onto our ContentPart union directly; the
rest degrade to a TextPart summary unless inlineMedia
permits them.
text?
optionaltext?:string|null
Defined in: src/channels/schemas.ts:56
Text body for type === "text" parts.
url?
optionalurl?:string|null
Defined in: src/channels/schemas.ts:58
URL or data: URI of the underlying asset.
mimeType?
optionalmimeType?:string|null
Defined in: src/channels/schemas.ts:60
Adapter-supplied MIME type. We HEAD-check link parts when missing.
filename?
optionalfilename?:string|null
Defined in: src/channels/schemas.ts:62
Original filename when the platform exposes it.
metadata?
optionalmetadata?:Record<string,unknown> |null
Defined in: src/channels/schemas.ts:64
Pass-through for adapter extensions.
ChatSdkUserLike
Defined in: src/channels/schemas.ts:68
Sender of an inbound message.
Properties
id
id:
string
Defined in: src/channels/schemas.ts:70
Platform-stable user id (@U123ABC on Slack, snowflake on Discord).
displayName?
optionaldisplayName?:string|null
Defined in: src/channels/schemas.ts:72
Best-effort display name.
isBot?
optionalisBot?:boolean|null
Defined in: src/channels/schemas.ts:74
True for the agent itself when it is the speaker.
metadata?
optionalmetadata?:Record<string,unknown> |null
Defined in: src/channels/schemas.ts:76
Adapter-specific extras.
ChatSdkThreadLike
Defined in: src/channels/schemas.ts:80
Thread-level context the adapter exposes when a message arrives.
Properties
id
id:
string
Defined in: src/channels/schemas.ts:82
Platform-side thread reference (Slack thread_ts, Discord channel id, ...).
platform
platform:
string
Defined in: src/channels/schemas.ts:84
Adapter that produced this thread.
isDirectMessage?
optionalisDirectMessage?:boolean|null
Defined in: src/channels/schemas.ts:86
True for direct/private messages. Used to gate the multi-user prefixer.
adapter
adapter:
ChatSdkAdapterLike
Defined in: src/channels/schemas.ts:105
Adapter handle for fetching prior messages.
Methods
subscribe()?
optionalsubscribe():void|Promise<void>
Defined in: src/channels/schemas.ts:93
Subscribe to a thread so future messages from this thread route
through the agent's webhook. Mastra calls this on first mention.
Implementations are free to return undefined when subscription
is implicit.
Returns
void | Promise<void>
post()
post(
stream):Promise<void>
Defined in: src/channels/schemas.ts:98
Post a streamed assistant response back to the platform. Accepts an AI-SDK-compatible UI stream (see toChatStream).
Parameters
stream
AsyncIterable<ChatSdkUiStreamPart>
Returns
Promise<void>
postCard()?
optionalpostCard(card):Promise<string|void|null>
Defined in: src/channels/schemas.ts:103
Post a single rich card (used by the approval-card bridge). Returns the platform-side card id when the adapter exposes one.
Parameters
card
Returns
Promise<string | void | null>
ChatSdkAdapterLike
Defined in: src/channels/schemas.ts:109
Adapter-level surface area we touch.
Properties
name
name:
string
Defined in: src/channels/schemas.ts:111
Adapter name ("slack", "discord", ...).
Methods
fetchMessages()
fetchMessages(
threadId,options):Promise<{messages:ChatSdkMessageLike[]; }>
Defined in: src/channels/schemas.ts:116
Backfill recent messages on first mention. Returns a list ordered oldest-first.
Parameters
threadId
string
options
limit
number
Returns
Promise<{ messages: ChatSdkMessageLike[]; }>
ChatLike
Defined in: src/channels/schemas.ts:120
Top-level Chat SDK instance. Constructor / state are out of scope.
Properties
adapters
adapters:
Record<string,ChatSdkAdapterLike>
Defined in: src/channels/schemas.ts:122
Adapter registry keyed by platform name.
threads?
optionalthreads?:object
Defined in: src/channels/schemas.ts:127
Thread-level helpers. Implementations vary; we only call
getById from the approval-resume callback path.
getById()
getById(
threadId):Promise<ChatSdkThreadLike|null>
Parameters
threadId
string
Returns
Promise<ChatSdkThreadLike | null>
ChatSdkCardPayload
Defined in: src/channels/schemas.ts:172
Minimal interactive-card payload accepted by thread.postCard(). We
only model the approve/deny flow; richer cards remain out of scope
for v0.1.
Properties
title
title:
string
Defined in: src/channels/schemas.ts:174
Headline rendered above the card body.
body
body:
string
Defined in: src/channels/schemas.ts:176
Markdown-flavoured body.
actions
actions:
object[]
Defined in: src/channels/schemas.ts:178
Buttons surfaced to the user.
label
label:
string
Button label.
actionId
actionId:
string
Stable id used to route the callback.
value?
optionalvalue?:string|null
Optional value carried with the action.
style?
optionalstyle?:"default"|"primary"|"danger"
Visual hint, mapped per-adapter.
metadata?
optionalmetadata?:Record<string,unknown>
Defined in: src/channels/schemas.ts:192
Opaque payload echoed back on the callback. We persist the checkpoint id and per-pending-approval id here.
ChannelEvent
Defined in: src/channels/schemas.ts:200
Inbound event emitted by the Chat SDK webhook adapter for a single message. We normalize each adapter's event shape into this envelope before invoking serveChannels's router.
Properties
platform
platform:
string
Defined in: src/channels/schemas.ts:202
The platform that produced this event.
thread
thread:
ChatSdkThreadLike
Defined in: src/channels/schemas.ts:204
Thread-level context (subscribed, post, fetchMessages, ...).
message
message:
ChatSdkMessageLike
Defined in: src/channels/schemas.ts:206
The new inbound message that triggered this event.
user
user:
ChatSdkUserLike
Defined in: src/channels/schemas.ts:208
Sender attribution. Convenience pointer; mirrors message.user.
isDirectMessage
isDirectMessage:
boolean
Defined in: src/channels/schemas.ts:210
True when the platform delivered this as a DM / private chat.
raw?
optionalraw?:unknown
Defined in: src/channels/schemas.ts:212
Adapter-specific extras (signed body, headers, ...).
ThreadIdResolution
Defined in: src/channels/schemas.ts:257
Thread-id resolution decision returned by a ThreadIdResolver.
threadId and resourceId are passed to Maniac.chatStream so the
existing ConversationStore / observational / working-memory tiers
key per-(platform, thread) and per-(platform, user) without any
extra wiring on the caller's side.
Properties
threadId
threadId:
string
Defined in: src/channels/schemas.ts:258
resourceId?
optionalresourceId?:string|null
Defined in: src/channels/schemas.ts:259
userPeerId?
optionaluserPeerId?:string|null
Defined in: src/channels/schemas.ts:260
assistantPeerId?
optionalassistantPeerId?:string|null
Defined in: src/channels/schemas.ts:261
platformThreadId?
optionalplatformThreadId?:string|null
Defined in: src/channels/schemas.ts:271
Native, un-prefixed platform thread reference (e.g. the Slack
thread_ts / Discord channel id) before the {platform}: prefix
the default resolver folds into threadId. Persisted in the
approval card metadata so the resume callback can hand the real
adapter id to chat.threads.getById instead of the composite
{platform}:{ref} key. Leave null/unset to fall back to the
composite.
MultiUserOptions
Defined in: src/channels/schemas.ts:288
Multi-user prefixing config. Off in DMs by default.
Properties
mode?
optionalmode?:"auto"|"off"|"always"
Defined in: src/channels/schemas.ts:294
"auto" (the default) prefixes group threads only.
"always" prefixes every message.
"off" disables prefixing.
Methods
format()?
optionalformat(user):string
Defined in: src/channels/schemas.ts:300
Override the rendered prefix. Receives the sender and returns the
string to prepend to the user message body. Defaults to
[{name} (@{id})]: .
Parameters
user
Returns
string
ThreadContextOptions
Defined in: src/channels/schemas.ts:304
Thread-context (Mastra "first mention backfill") config.
Properties
maxMessages?
optionalmaxMessages?:number
Defined in: src/channels/schemas.ts:310
Backfill up to this many prior platform messages on the first
mention of a non-DM thread. Set to 0 to disable. Defaults to
10, matching Mastra.
ChannelsOptions
Defined in: src/channels/schemas.ts:314
Configuration accepted by serveChannels.
Properties
chat
chat:
ChatLike
Defined in: src/channels/schemas.ts:316
Chat SDK instance the agent is wired to. Required.
threadContext?
optionalthreadContext?:ThreadContextOptions
Defined in: src/channels/schemas.ts:318
First-mention backfill behaviour.
inlineMedia?
optionalinlineMedia?:string[]
Defined in: src/channels/schemas.ts:324
MIME type patterns that ride inline (forwarded to the model as
structured content parts). Defaults to ["image/*"] to match
Mastra's default.
inlineLinks?
optionalinlineLinks?:InlineLinkMatch[]
Defined in: src/channels/schemas.ts:330
URL-style heuristics for treating raw links as media. Each match
either inherits the existing platform-supplied MIME type or
forces a specific one (e.g. YouTube → video/*).
threadIdResolver?
optionalthreadIdResolver?:ThreadIdResolver
Defined in: src/channels/schemas.ts:332
Override the default {platform}:{ref} thread-id resolver.
multiUser?
optionalmultiUser?:MultiUserOptions
Defined in: src/channels/schemas.ts:334
Multi-user attribution prefixing.
cards?
optionalcards?:boolean
Defined in: src/channels/schemas.ts:346
When true (default), the agent renders interactive approval cards
on status="paused". Set false to fall back to "post the
proposed args as text and let autoResumeSuspendedTools handle
it" — same opt-out as Mastra's cards: false.
idempotencyStore?
optionalidempotencyStore?:IdempotencyStore
Defined in: src/channels/schemas.ts:355
Optional idempotency store used by
ChannelsServer.handleApprovalCallback to short-circuit
duplicate (agentId, checkpointId, pendingId) button clicks.
Defaults to a fresh in-memory store with TTL eviction. Pass a
shared instance (Redis-backed in production) when multiple
processes / pods serve the same webhook.
Methods
onError()?
optionalonError(error,event):void
Defined in: src/channels/schemas.ts:339
Callback invoked when an inbound webhook crashes. Defaults to a
console.error log; production callers wire OTel here.
Parameters
error
unknown
event
ChannelEvent | null
Returns
void
ChatToolsetOptions
Defined in: src/channels/schemas.ts:359
Configuration accepted by ChatToolset.
Properties
chat
chat:
ChatLike
Defined in: src/channels/schemas.ts:361
Chat SDK instance the toolset wraps. Required.
name?
optionalname?:string
Defined in: src/channels/schemas.ts:367
Toolset name surfaced to the model (default "chat"). Per-tool
names follow the chat_post_message / chat_react_message
convention so OpenAI's tool-name validator accepts them.
description?
optionaldescription?:string
Defined in: src/channels/schemas.ts:369
Toolset description forwarded to the LM.
preset?
optionalpreset?:"all"|"messenger"|"moderator"
Defined in: src/channels/schemas.ts:373
Built-in tool preset. Mirrors Chat SDK's createChatTools({ preset }).
requireApproval?
optionalrequireApproval?:boolean
Defined in: src/channels/schemas.ts:382
Whether write tools require human approval. When true, each write
tool is emitted carrying the requires_approval tag, which the
runner's tag-gate enforces directly: the call pauses for approval
even when no permission policy is configured. An explicit policy
(e.g. RuleBasedPolicy) still takes precedence and can allow or
deny a matched rule outright.
tools?
optionaltools?:string[]
Defined in: src/channels/schemas.ts:387
Optional explicit allow-list of tool names. Cheap wrapper over
createChatTools's tools option; takes precedence over preset.
ToManiacMessagesOptions
Defined in: src/channels/toManiacMessages.ts:17
Properties
inlineMedia?
optionalinlineMedia?:string[]
Defined in: src/channels/toManiacMessages.ts:22
Whitelist of MIME types accepted as inline media. Mirrors
ChannelsOptions.inlineMedia. Defaults to ["image/*"].
treatOtherAssistantsAsUser?
optionaltreatOtherAssistantsAsUser?:boolean
Defined in: src/channels/toManiacMessages.ts:30
Render assistant messages produced by other bots as user
turns so the model treats them as input. The agent's own
messages (when persisted upstream) collapse into the assistant
role unchanged. Defaults to true to match Chat SDK's
toAiMessages behaviour.
selfPeerId?
optionalselfPeerId?:string|null
Defined in: src/channels/toManiacMessages.ts:37
The agent's own peer id (typically "agent:{agentId}"). Messages
authored by this id resolve to assistant; everyone else
(including bots) resolves to user when
treatOtherAssistantsAsUser is true.
WebhookHandlerOptions
Defined in: src/channels/webhookHandler.ts:70
Properties
eventTransform?
optionaleventTransform?: (body) =>ChannelEvent|null
Defined in: src/channels/webhookHandler.ts:80
Map a parsed JSON request body into a ChannelEvent. When omitted, the body is expected to already match the ChannelEvent shape (suitable for tests and Chat SDK's built-in normaliser). The default validator runs ChannelEventEnvelopeSchema against the result and 400s on a parse failure; opt out by setting skipDefaultValidation.
Parameters
body
unknown
Returns
ChannelEvent | null
interactionTransform?
optionalinteractionTransform?: (body) => {actionId?:string|null;value?:string|null;metadata?:Record<string,unknown> |null; } |null
Defined in: src/channels/webhookHandler.ts:86
Map a parsed JSON request body onto an approval callback. When
omitted, we expect the body to already be a { actionId, value, metadata } shape.
Parameters
body
unknown
Returns
{ actionId?: string | null; value?: string | null; metadata?: Record<string, unknown> | null; } | null
verifyRequest?
optionalverifyRequest?:VerifyRequest
Defined in: src/channels/webhookHandler.ts:100
Verify the raw (un-parsed) request bytes before any JSON parsing
or routing. Returning anything other than true /
{ ok: true } rejects with 401. Use
slackSigningSecret for the documented Slack scheme or
supply your own bridge. Strongly recommended for production
traffic — leaving this unset disables signature verification and
logs a one-shot warning.
idempotencyStore?
optionalidempotencyStore?:IdempotencyStore|null
Defined in: src/channels/webhookHandler.ts:110
Optional idempotency store. When set, the handler computes a
stable key per delivery and returns 200
{ ok: true, deduped: true } on cache hit. Defaults to a fresh
in-memory store (one per handler; set to null to opt out
completely). The same instance can be passed to
ChannelsOptions.idempotencyStore to share the cache
between webhook deliveries and approval callbacks.
idempotencyKey?
optionalidempotencyKey?: (args) =>string|null
Defined in: src/channels/webhookHandler.ts:117
Build the per-delivery idempotency key. Receives the parsed body
(after eventTransform for event routes; raw for interaction
routes) and the request headers. Return null to skip dedup for
this particular request. Defaults to defaultIdempotencyKey.
Parameters
args
body
unknown
headers
Headers
rawBody
Uint8Array
route
Returns
string | null
skipDefaultValidation?
optionalskipDefaultValidation?:boolean
Defined in: src/channels/webhookHandler.ts:129
Skip the default ChannelEventEnvelopeSchema validation
pass on event routes. Use only when your eventTransform
produces a structurally-different event envelope that you
validate yourself.
maxBodyBytes?
optionalmaxBodyBytes?:number
Defined in: src/channels/webhookHandler.ts:137
Maximum number of raw request-body bytes to accept. A delivery
whose Content-Length header — or whose actual streamed size —
exceeds this is rejected with 413 before any signature
verification, JSON parsing, or dispatch. Defaults to ~1 MB
(DEFAULT_MAX_BODY_BYTES).
onError?
optionalonError?: (error,context) =>void
Defined in: src/channels/webhookHandler.ts:144
Listener invoked when the handler returns 5xx (i.e. the inner
call threw). Receives the real error and a small context bag
(route, optional verify metadata). Defaults to a console.error
log so the surface is never silent.
Parameters
error
unknown
context
route
ParsedRoute | null
verifyMeta?
unknown
Returns
void
getRawBody?
optionalgetRawBody?: (req) =>Buffer<ArrayBufferLike> |Uint8Array<ArrayBufferLike> |null|undefined
Defined in: src/channels/webhookHandler.ts:154
Framework-specific raw-body accessor. Set by the Fastify /
Express bindings when the framework has pre-parsed req.body
and we still need the original bytes for signature verification.
Most callers won't pass this directly; use the wrapper helpers.
Parameters
req
unknown
Returns
Buffer<ArrayBufferLike> | Uint8Array<ArrayBufferLike> | null | undefined
ParsedRoute
Defined in: src/channels/webhookHandler.ts:157
Properties
agentId
agentId:
string
Defined in: src/channels/webhookHandler.ts:158
platform
platform:
string
Defined in: src/channels/webhookHandler.ts:159
kind
kind:
"event"|"interaction"
Defined in: src/channels/webhookHandler.ts:160
Type Aliases
ChatSdkUiStreamPart
ChatSdkUiStreamPart = {
type:"text-delta";id?:string;textDelta:string; } | {type:"reasoning-delta";id?:string;textDelta:string; } | {type:"tool-call";toolCallId:string;toolName:string;args:Record<string,unknown>; } | {type:"tool-result";toolCallId:string;toolName:string;result:unknown;isError?:boolean; } | {type:"finish";finishReason:"stop"|"length"|"tool-calls"|"error"|"other";usage?: {promptTokens?:number;completionTokens?:number; }; } | {type:"error";error: {message:string;cause?:unknown; }; } | {type:"card";card:ChatSdkCardPayload; }
Defined in: src/channels/schemas.ts:140
AI-SDK-compatible UI stream part.
Chat SDK's thread.post(stream) consumes the same shape AI SDK's
streamText().fullStream produces. We model only the discriminants
we emit from toChatStream; consumers of this type are
Chat-SDK-compatible producers.
ThreadIdResolver
ThreadIdResolver = (
event) =>ThreadIdResolution
Defined in: src/channels/schemas.ts:275
Resolves a Chat SDK event onto Maniac thread / resource ids.
Parameters
event
Returns
InlineLinkMatch
InlineLinkMatch =
string| {match:string|RegExp;mimeType:string; }
Defined in: src/channels/schemas.ts:283
URL-style match for inlineLinks. A bare string is a substring
match against the link href; the object form lets the caller force
a specific MIME type when the platform doesn't provide one (e.g.
YouTube URLs treated as video/* by Gemini).
VerifyRequest
VerifyRequest = (
rawBody,headers) =>Promise<boolean| {ok:true;meta?:unknown; }> |boolean| {ok:true;meta?:unknown; }
Defined in: src/channels/signature.ts:24
Verify a webhook request from the raw (un-parsed) bytes. May return
a simple boolean or { ok: true; meta? } to surface adapter-specific
metadata (e.g. parsed timestamp) on the logger.
Parameters
rawBody
Uint8Array
headers
Headers
Returns
Promise<boolean | { ok: true; meta?: unknown; }> | boolean | { ok: true; meta?: unknown; }
Variables
ChannelEventEnvelopeSchema
constChannelEventEnvelopeSchema:ZodObject<{platform:ZodString;thread:ZodObject<{id:ZodString;platform:ZodString; },$loose>;message:ZodObject<{ },$loose>;user:ZodObject<{id:ZodString; },$loose>;isDirectMessage:ZodBoolean;raw:ZodOptional<ZodUnknown>; },$loose>
Defined in: src/channels/schemas.ts:229
Lightweight runtime schema for the minimum ChannelEvent
shape, used by the webhook handler's default validator. We
intentionally model only what the inbound router needs to dispatch
(platform, thread.{id,platform}, user.id, isDirectMessage)
plus enough of the message body for the downstream chatStream
call. Function members (thread.post, thread.adapter.fetchMessages,
...) are not validated here — the Chat SDK adapter shape carries
them and tests use the MockChat fixture. Callers with a
stricter envelope can either swap in their own
WebhookHandlerOptions.eventTransform or layer a Zod schema
on top of the returned event before calling
ChannelsServer.handle.
defaultThreadIdResolver
constdefaultThreadIdResolver:ThreadIdResolver
Defined in: src/channels/threadIds.ts:26
Functions
renderApprovalCard()
renderApprovalCard(
ctx):ChatSdkCardPayload
Defined in: src/channels/approvals.ts:55
Build the canonical card payload for a pending approval. Adapters that don't natively render approve/deny buttons can fall back to a plain-text variant; here we always emit the structured shape so Chat SDK's adapter can degrade gracefully on its own.
Parameters
ctx
Returns
renderApprovalText()
renderApprovalText(
ctx):string
Defined in: src/channels/approvals.ts:101
Plain-text fallback used when cards: false.
Parameters
ctx
Returns
string
parseApprovalCallback()
parseApprovalCallback(
payload):ApprovalCallback|null
Defined in: src/channels/approvals.ts:136
Parse a Chat SDK callback payload back into a structured decision
record so serveChannels can dispatch it to
Maniac.resumeCheckpoint. Returns null when the payload doesn't
look like one of our cards (e.g. the user clicked a card belonging
to another bot).
Parameters
payload
actionId?
string | null
value?
string | null
metadata?
Record<string, unknown> | null
Returns
ApprovalCallback | null
buildApprovalResponse()
buildApprovalResponse(
callback):object
Defined in: src/channels/approvals.ts:180
Build the ApprovalResponse list to feed resumeCheckpoint.
Parameters
callback
Returns
id
id:
string
decision
decision:
"approve"|"deny"
reason?
optionalreason?:string|null
sub_decisions
sub_decisions:
Record<string,"approve"|"deny">
Per-sub-id approve/deny mapping (Tier 1 #2). Required (and
exhaustive) when the matching PendingApproval.sub_actions is
non-empty. When decision === "deny" the runtime treats the
whole batch as denied even if sub_decisions is set.
buildFastifyHandler()
buildFastifyHandler(
server,options?): (request,reply) =>Promise<unknown>
Defined in: src/channels/frameworkBindings.ts:137
Fastify-style handler: (request, reply) => Promise<void>.
Parameters
server
options?
Returns
(request, reply) => Promise<unknown>
buildExpressHandler()
buildExpressHandler(
server,options?): (req,res) =>Promise<unknown>
Defined in: src/channels/frameworkBindings.ts:157
Express-style middleware: (req, res, next) => void.
Parameters
server
options?
Returns
(req, res) => Promise<unknown>
buildHonoHandler()
buildHonoHandler(
server,options?): (c) =>Promise<Response>
Defined in: src/channels/frameworkBindings.ts:178
Hono-style handler: (c) => Response.
Parameters
server
options?
Returns
(c) => Promise<Response>
matchesMimePattern()
matchesMimePattern(
mime,patterns):boolean
Defined in: src/channels/media.ts:25
Canonical MIME-pattern matcher. Supports */*, image/*, exact match.
Parameters
mime
string | null | undefined
patterns
string[]
Returns
boolean
resolveMediaParts()
resolveMediaParts(
part,inlineMedia,inlineLinks?): ({type:"text";text:string;cache_control?: {type:"ephemeral"; } |null; } | {type:"image";source: {kind:"base64";media_type:string;data:string; } | {kind:"url";url:string; };cache_control?: {type:"ephemeral"; } |null; } | {type:"file";source: {kind:"base64";media_type:string;data:string; } | {kind:"url";url:string; };filename?:string|null;cache_control?: {type:"ephemeral"; } |null; })[]
Defined in: src/channels/media.ts:96
Resolve a Chat SDK part into zero or more ContentParts. The
caller passes the configured inlineMedia MIME patterns; anything
that doesn't match degrades to a textual summary.
Pure-data — no network. Link parts rely on the adapter-supplied MIME type or an explicit InlineLinkMatch override; we never fetch the URL to sniff a content type.
Parameters
part
inlineMedia
string[]
inlineLinks?
InlineLinkMatch[] = []
Returns
({ type: "text"; text: string; cache_control?: { type: "ephemeral"; } | null; } | { type: "image"; source: { kind: "base64"; media_type: string; data: string; } | { kind: "url"; url: string; }; cache_control?: { type: "ephemeral"; } | null; } | { type: "file"; source: { kind: "base64"; media_type: string; data: string; } | { kind: "url"; url: string; }; filename?: string | null; cache_control?: { type: "ephemeral"; } | null; })[]
shouldPrefix()
shouldPrefix(
event,options):boolean
Defined in: src/channels/multiUser.ts:37
Decide whether to apply prefixing to this event under the given options.
Parameters
event
options
MultiUserOptions | undefined
Returns
boolean
renderPrefix()
renderPrefix(
event,options):string
Defined in: src/channels/multiUser.ts:48
Render the prefix for a given event. Returns the empty string when prefixing is disabled.
Parameters
event
options
MultiUserOptions | undefined
Returns
string
buildUserMessage()
buildUserMessage(
event,options,body):string
Defined in: src/channels/multiUser.ts:59
Build the final user-message string for an inbound channel event. Combines the (optional) multi-user prefix with the message body exactly the way Mastra's adapter does.
Parameters
event
options
MultiUserOptions | undefined
body
string
Returns
string
serveChannels()
serveChannels(
app,agentId,options):ChannelsServer
Defined in: src/channels/serveChannels.ts:592
Public entry point. Returns a ChannelsServer bound to the given app + agent id with the supplied options applied.
Parameters
app
agentId
string
options
Returns
slackSigningSecret()
slackSigningSecret(
secret,opts?):VerifyRequest
Defined in: src/channels/signature.ts:42
Slack's documented signing-secret scheme:
sigBase = v0:${X-Slack-Request-Timestamp}:${rawBody}
expected = v0=${hex(hmac_sha256(secret, sigBase))}
compare(expected, X-Slack-Signature) with timingSafeEqual
Requests with timestamp drift greater than toleranceSeconds
(default 300, same as Slack's documented replay window) are
rejected to mitigate replay attacks.
Reference: https://api.slack.com/authentication/verifying-requests-from-slack
Parameters
secret
string
opts?
toleranceSeconds?
number
now?
() => number
Returns
timingSafeStringEquals()
timingSafeStringEquals(
a,b):boolean
Defined in: src/channels/signature.ts:94
Length-aware constant-time string compare. timingSafeEqual requires equal-length buffers; we short-circuit the length check (which is itself a side channel for length mismatches, but every caller will have a known fixed length for the expected signature) before delegating.
Parameters
a
string
b
string
Returns
boolean
validateThreadIdResolution()
validateThreadIdResolution(
resolution,event):ThreadIdResolution
Defined in: src/channels/threadIds.ts:47
Validate the user-supplied resolver's output and fail fast on
obvious mistakes (empty threadId is a programmer error and
silently bucketing every event into one thread is far worse than
crashing). Returns the input unchanged when valid.
Parameters
resolution
event
Returns
toChatStream()
toChatStream<
T>(source):AsyncGenerator<ChatSdkUiStreamPart,void,undefined>
Defined in: src/channels/toChatStream.ts:138
Iterate StreamEnvelopes and yield Chat-SDK-compatible UI
parts. The terminal envelope produces a finish part (and a
text-delta for any final-answer text that wasn't already
streamed).
Type Parameters
T
T = unknown
Parameters
source
AsyncIterable<StreamEnvelope<T>>
Returns
AsyncGenerator<ChatSdkUiStreamPart, void, undefined>
toManiacMessages()
toManiacMessages(
messages,options?):object[]
Defined in: src/channels/toManiacMessages.ts:130
Public API. Returns a list of validated Maniac messages ready to
splice into runAgent / chatStream prefixMessages.
Parameters
messages
options?
Returns
parseRoute()
parseRoute(
url):ParsedRoute|null
Defined in: src/channels/webhookHandler.ts:169
Parse the URL into our route discriminator. The default URL grammar
matches Mastra's documented shape; callers with bespoke routing can
skip this helper and call server.handle / server.handleApprovalCallback
directly.
Parameters
url
string | URL
Returns
ParsedRoute | null
defaultIdempotencyKey()
defaultIdempotencyKey(
args):string|null
Defined in: src/channels/webhookHandler.ts:220
Default per-delivery idempotency key. Tries Slack's stable
event_id (most common shape), falls back to the
X-Slack-Retry-Reason + signature combo, and finally hashes the
raw body as a last resort. Returns null when nothing usable is
available (the handler skips dedup in that case).
Parameters
args
body
unknown
headers
Headers
rawBody
Uint8Array
route
Returns
string | null
buildWebhookHandler()
buildWebhookHandler(
server,options?): (req) =>Promise<Response>
Defined in: src/channels/webhookHandler.ts:401
Build a framework-agnostic (Request) => Promise<Response> handler.
Returns 200 with { ok: true } on success, 4xx on bad inputs
(invalid JSON / route / signature / event shape), 413 on an
over-sized body, and a generic 5xx ({ error: "internal_error" })
on internal errors — the real error is routed to
WebhookHandlerOptions.onError so OTel exporters and
ChannelsOptions.onError can capture it.
Parameters
server
options?
Returns
(req) => Promise<Response>