import type { WritableStream } from 'node:stream/web'; import type { Workflow } from '@mastra/core/workflows'; import type { VoiceReplyGenerator, VoiceToolCall, VoiceTurnCompleteHook, VoiceTurnContext } from './bridge.js'; export interface WorkflowReplyGeneratorOptions { /** The Mastra workflow that generates each turn's reply. Runs once per turn (no suspend/resume). */ workflow: Workflow; /** * Maps a turn into the workflow's `inputData`. Required — input schemas are caller-defined. * A common shape passes the full transcript so the workflow is stateless between turns, e.g. * `ctx => ({ history: chatContextToMessages(ctx.chatCtx) })`. */ workflowInput: (ctx: VoiceTurnContext) => unknown | Promise; /** * Only stream text from this step (by id). Defaults to every step that writes text to its * `writer`. Set when multiple steps write and only one produces the spoken reply. */ replyStep?: string; /** * Fallback when the workflow streams no text via `writer`: derive the spoken reply from the * final run result. Without this, a non-streaming workflow stays silent. Streaming via the * step `writer` is preferred — it gives the caller low time-to-first-token. */ resultText?: (result: unknown) => string | undefined | void; /** * Speak a short phrase while a tool call runs. Fires only for tool calls the reply step surfaces * to its `writer` — use {@link pipeAgentReplyToWriter} (or pipe the agent's `fullStream`) so * `tool-call` chunks reach the stream. See {@link MastraVoiceAgentOptions.toolFeedback}. */ toolFeedback?: (toolCall: VoiceToolCall) => string | undefined | void; /** * Fired off the audio path after the reply streams, fire-and-forget. Carries the produced reply * text and any tool calls the workflow surfaced. See {@link MastraVoiceAgentOptions.onTurnComplete}. */ onTurnComplete?: VoiceTurnCompleteHook; } /** * Unwraps the text carried by a `workflow-step-output` chunk's `payload.output`. A step that * pipes `agent.stream().textStream` into its `writer` produces raw strings; a step built from an * agent (`createStep(agent)`) produces full `text-delta` chunks. Returns the text for both, or * `undefined` for any other shape. */ export declare function unwrapStepText(output: unknown): string | undefined; /** * Unwraps a tool call carried by a `workflow-step-output` chunk's `payload.output`. A step that * pipes the agent's `fullStream` (rather than just `textStream`) into its `writer` surfaces * `tool-call` chunks; this returns the {@link VoiceToolCall} for those, or `undefined` otherwise. */ export declare function unwrapStepToolCall(output: unknown): VoiceToolCall | undefined; /** The minimal shape of a Mastra agent stream consumed by {@link pipeAgentReplyToWriter}. */ export interface AgentReplyStreamLike { fullStream: AsyncIterable; } /** * Streams a Mastra agent's reply into a workflow step's `writer` for the LiveKit workflow * entrypoint — the recommended way to drive a turn's reply from a step. * * It forwards the agent's text deltas (so text-to-speech starts before the full reply is ready) * AND its `tool-call` chunks (so {@link WorkflowReplyGeneratorOptions.toolFeedback} fires and * {@link WorkflowReplyGeneratorOptions.onTurnComplete}'s `result.toolCalls` is populated). This is * the difference from piping only `agent.stream().textStream`, which silently drops tool calls. * Speech boundaries and tool results are forwarded for flushing and timing. Reasoning is not forwarded, keeping the spoken * stream clean. Returns the accumulated reply text for the step to return. * * Pass the step's `abortSignal` to `agent.stream(...)` so barge-in stops generation promptly. * * ```ts * const generateResponse = createStep({ * // ... * execute: async ({ inputData, mastra, writer, abortSignal }) => { * const stream = await mastra.getAgent('callCenter').stream(inputData.turn, { abortSignal }); * const reply = await pipeAgentReplyToWriter(stream, writer); * return { reply }; * }, * }); * ``` */ export declare function pipeAgentReplyToWriter(agentStream: AgentReplyStreamLike, writer: WritableStream): Promise; /** * A {@link VoiceReplyGenerator} backed by a Mastra workflow. Per turn it starts a fresh run to * completion (LiveKit owns the turn boundary, so there is no suspend/resume and no conversation * state carried between turns) and streams the text its steps write to their `writer`. * * A workflow's own stream emits structured step events, not token deltas — text only surfaces * when a step pipes it into the injected `writer`, arriving as `workflow-step-output` chunks. The * simplest correct reply step calls {@link pipeAgentReplyToWriter}, which forwards both text and * tool calls (and passes the step's `abortSignal` through `agent.stream` so barge-in stops * generation promptly). Piping only `agent.stream(...).textStream.pipeTo(writer)` works for text * but silently drops tool calls, so {@link WorkflowReplyGeneratorOptions.toolFeedback} and * {@link WorkflowReplyGeneratorOptions.onTurnComplete}'s `result.toolCalls` stay empty. */ export declare function createWorkflowReplyGenerator(options: WorkflowReplyGeneratorOptions): VoiceReplyGenerator; //# sourceMappingURL=workflow-generator.d.ts.map