@mastra/core
Version:
170 lines • 6.8 kB
TypeScript
import type { Adapter, StreamChunk, Thread } from 'chat';
import type { IMastraLogger } from '../logger/logger.js';
import type { AgentChunkType } from '../stream/types.js';
import type { PostableMessage, ToolDisplayEvent } from './types.js';
/**
* Approval card metadata stashed when a driver posts an approval card. The
* outer `AgentChannels` instance uses it to resume the correct run by
* `toolCallId` without crawling persisted message metadata (the metadata
* path keys by `toolName` and collides on parallel same-tool approvals).
*/
export interface PendingApprovalRecord {
messageId?: string;
displayName: string;
argsSummary: string;
startedAt: number;
runId?: string;
toolName?: string;
args?: Record<string, unknown>;
}
/**
* Per-tool enrichment data both drivers compute identically:
* display name (without `mastra_*_` prefix), human-readable args summary,
* wall-clock start time, and (for terminal chunks) duration / result text.
*/
export interface ToolEnrichment {
toolCallId: string;
toolName: string;
displayName: string;
argsSummary: string;
args: unknown;
startedAt: number;
/** Set on enrichResult / enrichError when the tool was previously tracked. */
durationMs?: number;
/** Set on enrichResult. */
resultText?: string;
/** Set on enrichError. */
errorText?: string;
/** Set on enrichResult / enrichError. */
isError?: boolean;
}
/**
* Cross-chunk correlation for tool calls. A single instance lives for the
* lifetime of a `consumeAgentStream` run (or driver run). `trackStart` is
* called for `tool-call`, then `enrichResult` / `enrichError` /
* `enrichApproval` look up the matching start to enrich the terminal chunk
* with the original `displayName` / `argsSummary` / `startedAt` (since later
* chunks may not carry the original args verbatim).
*
* Keyed by `toolCallId` (not `toolName`) so parallel same-tool calls don't
* clobber each other — a regression that previously broke parallel
* `requireApproval` flows.
*/
export declare class ToolTracker {
private tools;
/** Returns the number of tool calls currently in flight. */
get inFlightCount(): number;
/** Returns true if `toolCallId` has a tracked start. */
has(toolCallId: string): boolean;
trackStart(call: {
toolCallId: string;
toolName: string;
args: unknown;
}): ToolEnrichment;
enrichResult(call: {
toolCallId: string;
toolName: string;
args: unknown;
result: unknown;
isError?: boolean;
}): ToolEnrichment;
enrichError(call: {
toolCallId: string;
toolName: string;
args: unknown;
error: unknown;
}): ToolEnrichment;
enrichApproval(call: {
toolCallId: string;
toolName: string;
args: unknown;
}): ToolEnrichment;
forget(toolCallId: string): void;
reset(): void;
}
/**
* Pull a human-readable message out of a `tool-error` chunk's `error` payload.
* The error can be a string, an `Error`-shaped object, a `MastraError`-shaped
* object with `message`/`details.errorMessage`, or anything else — fall back
* to the raw value so the failure stays debuggable.
*/
export declare function extractErrorMessage(error: unknown): unknown;
/**
* Post an `error` chunk's payload as a user-visible message. Both drivers
* call this after closing/flushing their active session so the error lands
* after the streamed text. Honors a caller-supplied `formatError` to allow
* adapters to customize the rendering; falls back to a plain `❌ Error: ...`
* prefix otherwise.
*/
export declare function postStreamError(args: {
chunk: Extract<AgentChunkType<any>, {
type: 'error';
}>;
chatThread: Pick<Thread, 'post'>;
platform: string;
logger?: IMastraLogger;
formatError?: (error: Error) => unknown;
}): Promise<void>;
/**
* Post a `tripwire` chunk's payload as a user-visible safety-block message.
* Skipped when the tripwire is marked `retry: true` — the agent will retry
* internally and produce a new response on the same stream.
*/
export declare function postTripwire(args: {
chunk: Extract<AgentChunkType<any>, {
type: 'tripwire';
}>;
chatThread: Pick<Thread, 'post'>;
logger?: IMastraLogger;
}): Promise<void>;
/**
* Post a `file` chunk's payload as a thread attachment. The chunk's `data`
* may be base64-encoded (string) or raw bytes (`Uint8Array`).
*/
export declare function postFileAttachment(args: {
chunk: Extract<AgentChunkType<any>, {
type: 'file';
}>;
chatThread: Pick<Thread, 'post'>;
logger?: IMastraLogger;
}): Promise<void>;
/**
* Edit an existing message by id when one was previously posted, otherwise
* post a fresh one. Both drivers use this for the per-tool card lifecycle
* (post "Running…" → edit with result/error/approval). If the edit fails
* (e.g. the original message was deleted), falls back to posting a new one.
*
* Returns the resulting message id — the static driver tracks this so a
* later `tool-result` can edit the same card; the streaming driver doesn't
* persist follow-up edits past the result, so it ignores the return value.
*/
export declare function editOrPostMessage(args: {
adapter: Pick<Adapter<any, any>, 'editMessage'>;
chatThread: Pick<Thread, 'id' | 'post'>;
messageId: string | undefined;
message: PostableMessage;
logger?: IMastraLogger;
}): Promise<string | undefined>;
/**
* Render a built-in `'cards'` or `'text'` tool event as a `PostableMessage`.
* Both drivers go through this so the lifecycle (post → edit on result) is
* identical — only the platform-specific post/edit calls differ.
*
* `'cards'` → rich Block Kit; `'text'` → plain text. Approval messages are
* always rendered as cards regardless of mode so the Approve/Deny buttons
* render — plain-text approval falls back to a "reply approve/deny" hint.
*/
export declare function renderBuiltInToolEvent(event: ToolDisplayEvent, mode: 'cards' | 'text'): PostableMessage;
/**
* Render a chat-SDK `StreamChunk` as a plain-text fallback message. Used by
* the static driver when a `ToolDisplayFn` returns `{ kind: 'stream' }` —
* the static driver has no `StreamingPlan` to push the chunk into, so we
* flatten it to text so the user still sees the rendered output.
*
* Returns `null` for chunks that have nothing useful to render in static
* mode (e.g. a `text-delta` mid-stream signal, or a `task_update` with no
* title/details). Callers should treat `null` as "skip this event" rather
* than posting an empty message.
*/
export declare function chunkToFallbackMessage(chunk: StreamChunk): string | null;
//# sourceMappingURL=stream-helpers.d.ts.map