@mastra/core
Version:
90 lines • 4.15 kB
TypeScript
import type { Adapter, Thread } from 'chat';
import type { IMastraLogger } from '../logger/logger.js';
import type { ProcessOutputStreamArgs } from '../processors/index.js';
import type { AgentChunkType, ChunkType } from '../stream/types.js';
import type { AgentChannels } from './agent-channels.js';
import type { PendingApprovalRecord } from './stream-helpers.js';
import type { ToolDisplay, ToolDisplayFn } from './types.js';
/**
* Per-run render dependencies stashed onto `requestContext` by
* `AgentChannels.processChatMessage` (and the slash-command / resume paths
* once those migrate). The output processor reads this on the first chunk
* and routes subsequent chunks through the resolved chat-SDK driver.
*
* Kept separate from `ChannelContext` (which is part of the public LLM
* surface) so we don't leak runtime handles into prompts or persisted
* provider metadata.
*
* @internal
*/
export interface ChatChannelRenderContext {
adapter: Adapter;
chatThread: Thread;
platform: string;
streaming: {
enabled: boolean;
options?: {
updateIntervalMs?: number;
};
};
toolDisplay: ToolDisplay;
toolDisplayFn?: ToolDisplayFn;
channelToolNames: Set<string>;
logger?: IMastraLogger;
onApprovalPosted: (toolCallId: string, record: PendingApprovalRecord) => void;
getPendingApproval: (toolCallId: string) => PendingApprovalRecord | undefined;
takePendingApproval: (toolCallId: string) => PendingApprovalRecord | undefined;
wrapStream: (stream: AsyncIterable<AgentChunkType<any>>) => AsyncIterable<AgentChunkType<any>>;
typingGate: {
active: boolean;
};
formatError?: (error: Error) => unknown;
approvalContext?: {
toolCallId: string;
messageId: string;
};
}
/** Key the processor reads off `requestContext` to locate its render deps. */
export declare const CHAT_CHANNEL_RENDER_CONTEXT_KEY = "__mastra_chat_channel_render";
/**
* Output processor that mirrors the agent's stream to the originating chat
* platform (Slack/Discord/etc.) via the existing streaming/static drivers.
*
* On the first chunk of a run, the processor opens a render session: it spins
* up an async queue, hands the queue's iterable to `runStreamingDriver` (or
* `runStaticDriver`), and stores the session on the per-run `state` arg.
* Subsequent chunks push into the queue and return immediately — the driver
* pumps chunks to the platform in the background, never blocking the agent
* loop.
*
* On `finish` / `error` chunks the queue is closed and the driver promise is
* awaited so the run doesn't end (and a serverless invocation isn't allowed
* to freeze) before the last `chat.update` lands.
*
* Render context is resolved in two ways, in order:
*
* 1. Fast path — `CHAT_CHANNEL_RENDER_CONTEXT_KEY` on `requestContext`, stashed
* by `AgentChannels` on inbound platform events (`processChatMessage`,
* approve/decline). This is the original webhook path and is unchanged.
* 2. Fallback — when no render context is on `requestContext` (schedule fire,
* Studio, custom UI, user code) but the processor is bound to its owning
* `AgentChannels`, it reconstructs the render context from the run's
* `threadId` via `agentChannels.buildRenderContextForThread(threadId)`,
* which reads the thread's persisted channel coordinates. The `threadId` is
* taken from the memory context the framework stashes on `requestContext`
* under the `MastraMemory` key.
*
* Runs that resolve no render context at all — non-channel threads, or an
* unbound processor with no `requestContext` key — pass through untouched.
*
* @internal
*/
export declare class ChatChannelOutputProcessor {
#private;
readonly id = "chat-channel-render";
/** Need data-* chunks because some drivers (`hidden`/`grouped`) inspect them. */
readonly processDataParts = true;
constructor(agentChannels?: AgentChannels);
processOutputStream({ part, state, requestContext, }: ProcessOutputStreamArgs): Promise<ChunkType | null | undefined>;
}
//# sourceMappingURL=output-processor.d.ts.map