Maniac Docs
API Reference

Channels

Slack, webhook, and chat channel integrations.

@maniac-ai/agents


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

ChatToolsetOptions

Returns

ChatToolset

Overrides

BaseToolset.constructor

Properties

name

readonly name: string

Defined in: src/tools/base.ts:135

Inherited from

BaseToolset.name

description

readonly description: string

Defined in: src/tools/base.ts:136

Inherited from

BaseToolset.description

Methods

listTools()

listTools(): Promise<Tool[]>

Defined in: src/channels/chatToolset.ts:382

Returns

Promise<Tool[]>

Overrides

BaseToolset.listTools

searchTools()

searchTools(query, k?): Promise<Tool[]>

Defined in: src/tools/base.ts:149

Parameters
query

string

k?

number = 10

Returns

Promise<Tool[]>

Inherited from

BaseToolset.searchTools

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

JsonDict

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

BaseToolset.invoke

asDelegated()

asDelegated(options?): Tool

Defined in: src/tools/base.ts:171

Parameters
options?

DelegatedToolOptions = {}

Returns

Tool

Inherited from

BaseToolset.asDelegated


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

InMemoryIdempotencyStore

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 true when the key was newly recorded (first delivery — the caller should process it).
  • Returns false when 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

IdempotencyStore.claim

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

Maniac

agentId

string

options

ChannelsOptions

Returns

ChannelsServer

Properties

agentId

readonly agentId: 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

ChannelEvent

Returns

ThreadIdResolution

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

ChannelEvent

Returns

Promise<AgentResult<unknown>>

postText()

static postText(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

ChatSdkThreadLike

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?

optional cost_usd?: number | null

usage.cache_creation_input_tokens?

optional cache_creation_input_tokens?: number | null

usage.cache_read_input_tokens?

optional cache_read_input_tokens?: number | null

root_messages

root_messages: object[]

subagent_sessions

subagent_sessions: object[]

pending

pending: object[]

checkpoint_id?

optional checkpoint_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?

optional rule_id?: string | null

reason?

optional reason?: 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?

optional platformThreadId?: 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?

optional platformThreadId?: 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:

  1. 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 chatStream run and the user sees the agent post twice).
  2. 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 true when the key was newly recorded (first delivery — the caller should process it).
  • Returns false when 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?

optional id?: string | null

Defined in: src/channels/schemas.ts:26

Stable per-thread id assigned by the platform / adapter.

role?

optional role?: string

Defined in: src/channels/schemas.ts:28

Logical role of the speaker.

text?

optional text?: string | null

Defined in: src/channels/schemas.ts:30

Best-effort plain-text body. Part-form messages may leave this empty.

parts?

optional parts?: readonly ChatSdkPartLike[] | null

Defined in: src/channels/schemas.ts:32

Optional structured content (Chat SDK uses an array of "parts").

user?

optional user?: ChatSdkUserLike | null

Defined in: src/channels/schemas.ts:34

Sender attribution. Populated for inbound messages.

threadId?

optional threadId?: string | null

Defined in: src/channels/schemas.ts:36

Thread / channel attribution.

platform?

optional platform?: string | null

Defined in: src/channels/schemas.ts:38

Adapter that produced this message ("slack", "discord", ...).

createdAt?

optional createdAt?: string | null

Defined in: src/channels/schemas.ts:40

Wall-clock time the message was created at, ISO-8601.

metadata?

optional metadata?: 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?

optional text?: string | null

Defined in: src/channels/schemas.ts:56

Text body for type === "text" parts.

url?

optional url?: string | null

Defined in: src/channels/schemas.ts:58

URL or data: URI of the underlying asset.

mimeType?

optional mimeType?: string | null

Defined in: src/channels/schemas.ts:60

Adapter-supplied MIME type. We HEAD-check link parts when missing.

filename?

optional filename?: string | null

Defined in: src/channels/schemas.ts:62

Original filename when the platform exposes it.

metadata?

optional metadata?: 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?

optional displayName?: string | null

Defined in: src/channels/schemas.ts:72

Best-effort display name.

isBot?

optional isBot?: boolean | null

Defined in: src/channels/schemas.ts:74

True for the agent itself when it is the speaker.

metadata?

optional metadata?: 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?

optional isDirectMessage?: 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()?

optional subscribe(): 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()?

optional postCard(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

ChatSdkCardPayload

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?

optional threads?: 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?

optional value?: string | null

Optional value carried with the action.

style?

optional style?: "default" | "primary" | "danger"

Visual hint, mapped per-adapter.

metadata?

optional metadata?: 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?

optional raw?: 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?

optional resourceId?: string | null

Defined in: src/channels/schemas.ts:259

userPeerId?

optional userPeerId?: string | null

Defined in: src/channels/schemas.ts:260

assistantPeerId?

optional assistantPeerId?: string | null

Defined in: src/channels/schemas.ts:261

platformThreadId?

optional platformThreadId?: 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?

optional mode?: "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()?

optional format(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

ChatSdkUserLike

Returns

string


ThreadContextOptions

Defined in: src/channels/schemas.ts:304

Thread-context (Mastra "first mention backfill") config.

Properties

maxMessages?

optional maxMessages?: 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?

optional threadContext?: ThreadContextOptions

Defined in: src/channels/schemas.ts:318

First-mention backfill behaviour.

inlineMedia?

optional inlineMedia?: 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.

optional inlineLinks?: 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?

optional threadIdResolver?: ThreadIdResolver

Defined in: src/channels/schemas.ts:332

Override the default {platform}:{ref} thread-id resolver.

multiUser?

optional multiUser?: MultiUserOptions

Defined in: src/channels/schemas.ts:334

Multi-user attribution prefixing.

cards?

optional cards?: 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?

optional idempotencyStore?: 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()?

optional onError(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?

optional name?: 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?

optional description?: string

Defined in: src/channels/schemas.ts:369

Toolset description forwarded to the LM.

preset?

optional preset?: "all" | "messenger" | "moderator"

Defined in: src/channels/schemas.ts:373

Built-in tool preset. Mirrors Chat SDK's createChatTools({ preset }).

requireApproval?

optional requireApproval?: 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?

optional tools?: 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?

optional inlineMedia?: string[]

Defined in: src/channels/toManiacMessages.ts:22

Whitelist of MIME types accepted as inline media. Mirrors ChannelsOptions.inlineMedia. Defaults to ["image/*"].

treatOtherAssistantsAsUser?

optional treatOtherAssistantsAsUser?: 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?

optional selfPeerId?: 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?

optional eventTransform?: (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?

optional interactionTransform?: (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?

optional verifyRequest?: 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?

optional idempotencyStore?: 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?

optional idempotencyKey?: (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

ParsedRoute

Returns

string | null

skipDefaultValidation?

optional skipDefaultValidation?: 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?

optional maxBodyBytes?: 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?

optional onError?: (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?

optional getRawBody?: (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

ChannelEvent

Returns

ThreadIdResolution


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

const ChannelEventEnvelopeSchema: 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

const defaultThreadIdResolver: 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

ApprovalCardContext

Returns

ChatSdkCardPayload


renderApprovalText()

renderApprovalText(ctx): string

Defined in: src/channels/approvals.ts:101

Plain-text fallback used when cards: false.

Parameters

ctx

ApprovalCardContext

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

ApprovalCallback

Returns

id

id: string

decision

decision: "approve" | "deny"

reason?

optional reason?: 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

ChannelsServer

options?

WebhookHandlerOptions = {}

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

ChannelsServer

options?

WebhookHandlerOptions = {}

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

ChannelsServer

options?

WebhookHandlerOptions = {}

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

ChatSdkPartLike

inlineMedia

string[]

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

ChannelEvent

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

ChannelEvent

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

ChannelEvent

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

Maniac

agentId

string

options

ChannelsOptions

Returns

ChannelsServer


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

VerifyRequest


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

ThreadIdResolution

event

ChannelEvent

Returns

ThreadIdResolution


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

ChatSdkMessageLike[]

options?

ToManiacMessagesOptions = {}

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

ParsedRoute

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

ChannelsServer

options?

WebhookHandlerOptions = {}

Returns

(req) => Promise<Response>

On this page

ClassesChatToolsetExtendsConstructorsConstructorParametersoptionsReturnsOverridesPropertiesnameInherited fromdescriptionInherited fromMethodslistTools()ReturnsOverridessearchTools()Parametersqueryk?ReturnsInherited frominvoke()ParameterstoolNameargsReturnsInherited fromasDelegated()Parametersoptions?ReturnsInherited fromInMemoryIdempotencyStoreImplementsConstructorsConstructorParametersoptions?maxEntries?ReturnsMethodsclaim()ParameterskeyttlSecondsReturnsImplementation ofclear()Returnssize()ReturnsChannelsServerConstructorsConstructorParametersappagentIdoptionsReturnsPropertiesagentIdMethodsresolveThread()ParameterseventReturnshandle()ParameterseventReturnspostText()ParametersthreadtextReturnshandleApprovalCallback()ParameterspayloadactionId?value?metadata?ReturnsInterfacesApprovalCardContextPropertiesagentIdthreadIdcheckpointschema_versionspec_iditerationusageusage.promptusage.completionusage.cost_usd?usage.cache_creation_input_tokens?usage.cache_read_input_tokens?root_messagessubagent_sessionspendingcheckpoint_id?pendingidprincipaltoolsettoolargsrule_id?reason?requested_atsub_actionsplatformThreadId?ApprovalCallbackPropertiesagentIdthreadIdcheckpointIddecisionpendingIdplatformThreadId?IdempotencyStoreMethodsclaim()ParameterskeyttlSecondsReturnsChatSdkMessageLikePropertiesid?role?text?parts?user?threadId?platform?createdAt?metadata?ChatSdkPartLikePropertiestypetext?url?mimeType?filename?metadata?ChatSdkUserLikePropertiesiddisplayName?isBot?metadata?ChatSdkThreadLikePropertiesidplatformisDirectMessage?adapterMethodssubscribe()?Returnspost()ParametersstreamReturnspostCard()?ParameterscardReturnsChatSdkAdapterLikePropertiesnameMethodsfetchMessages()ParametersthreadIdoptionslimitReturnsChatLikePropertiesadaptersthreads?getById()ParametersthreadIdReturnsChatSdkCardPayloadPropertiestitlebodyactionslabelactionIdvalue?style?metadata?ChannelEventPropertiesplatformthreadmessageuserisDirectMessageraw?ThreadIdResolutionPropertiesthreadIdresourceId?userPeerId?assistantPeerId?platformThreadId?MultiUserOptionsPropertiesmode?Methodsformat()?ParametersuserReturnsThreadContextOptionsPropertiesmaxMessages?ChannelsOptionsPropertieschatthreadContext?inlineMedia?inlineLinks?threadIdResolver?multiUser?cards?idempotencyStore?MethodsonError()?ParameterserroreventReturnsChatToolsetOptionsPropertieschatname?description?preset?requireApproval?tools?ToManiacMessagesOptionsPropertiesinlineMedia?treatOtherAssistantsAsUser?selfPeerId?WebhookHandlerOptionsPropertieseventTransform?ParametersbodyReturnsinteractionTransform?ParametersbodyReturnsverifyRequest?idempotencyStore?idempotencyKey?ParametersargsbodyheadersrawBodyrouteReturnsskipDefaultValidation?maxBodyBytes?onError?ParameterserrorcontextrouteverifyMeta?ReturnsgetRawBody?ParametersreqReturnsParsedRoutePropertiesagentIdplatformkindType AliasesChatSdkUiStreamPartThreadIdResolverParameterseventReturnsInlineLinkMatchVerifyRequestParametersrawBodyheadersReturnsVariablesChannelEventEnvelopeSchemadefaultThreadIdResolverFunctionsrenderApprovalCard()ParametersctxReturnsrenderApprovalText()ParametersctxReturnsparseApprovalCallback()ParameterspayloadactionId?value?metadata?ReturnsbuildApprovalResponse()ParameterscallbackReturnsiddecisionreason?sub_decisionsbuildFastifyHandler()Parametersserveroptions?ReturnsbuildExpressHandler()Parametersserveroptions?ReturnsbuildHonoHandler()Parametersserveroptions?ReturnsmatchesMimePattern()ParametersmimepatternsReturnsresolveMediaParts()ParameterspartinlineMediainlineLinks?ReturnsshouldPrefix()ParameterseventoptionsReturnsrenderPrefix()ParameterseventoptionsReturnsbuildUserMessage()ParameterseventoptionsbodyReturnsserveChannels()ParametersappagentIdoptionsReturnsslackSigningSecret()Parameterssecretopts?toleranceSeconds?now?ReturnstimingSafeStringEquals()ParametersabReturnsvalidateThreadIdResolution()ParametersresolutioneventReturnstoChatStream()Type ParametersTParameterssourceReturnstoManiacMessages()Parametersmessagesoptions?ReturnsparseRoute()ParametersurlReturnsdefaultIdempotencyKey()ParametersargsbodyheadersrawBodyrouteReturnsbuildWebhookHandler()Parametersserveroptions?Returns