import type { AudioFrame } from '@livekit/rtc-node';
import type { ReadableStream } from 'stream/web';
import type { Instructions } from '../llm/chat_context.js';
import { type ChatContext, FunctionCall, FunctionCallOutput } from '../llm/chat_context.js';
import { type ToolChoice, type ToolContext } from '../llm/tool_context.js';
import { type FlushSentinel } from '../types.js';
import { type Aborted, Future, Task } from '../utils.js';
import { type Agent, type ModelSettings } from './agent.js';
import type { ForwardOutput } from './agent_activity.ts';
import type { AgentSession } from './agent_session.js';
import { AudioOutput, type LLMNode, type TTSNode, type TextOutput, type TimedString } from './io.js';
import type { SpeechHandle } from './speech_handle.js';
import { type TextTransform } from './transcription/text_transforms.js';
export declare const DEFAULT_TTS_READ_IDLE_TIMEOUT_MS = 10000;
export declare const DEFAULT_FORWARD_AUDIO_IDLE_TIMEOUT_MS = 10000;
export declare const RUNNING_TOOL_PLACEHOLDER = "The tool call is still in progress.";
export declare const RUNNING_TOOL_PLACEHOLDER_KEY = "__lk_running_placeholder__";
export declare function _injectRunningToolCalls(chatCtx: ChatContext, runningCalls: Iterable<FunctionCall>): void;
export declare function _stripRunningToolCalls(chatCtx: ChatContext): void;
/** @internal */
export declare class _LLMGenerationData {
    readonly textStream: ReadableStream<string | FlushSentinel>;
    readonly toolCallStream: ReadableStream<FunctionCall>;
    generatedText: string;
    generatedToolCalls: FunctionCall[];
    generatedExtra: Record<string, unknown>;
    id: string;
    ttft?: number;
    constructor(textStream: ReadableStream<string | FlushSentinel>, toolCallStream: ReadableStream<FunctionCall>);
}
/**
 * TTS generation data containing audio stream and optional timed transcripts.
 * @internal
 */
export interface _TTSGenerationData {
    /** Audio frame stream from TTS */
    audioStream: ReadableStream<AudioFrame>;
    /**
     * Future that resolves to a stream of timed transcripts, or null if TTS doesn't support it.
     */
    timedTextsFut: Future<ReadableStream<TimedString> | null, never>;
    /** Time to first byte (set when first audio frame is received) */
    ttfb?: number;
}
export declare class _ToolOutput {
    output: _JsOutput[];
    firstToolFut: Future;
    constructor();
}
export declare class _SanitizedOutput {
    toolCall: FunctionCall;
    toolCallOutput?: FunctionCallOutput;
    replyRequired: boolean;
    agentTask?: Agent;
    constructor(toolCall: FunctionCall, toolCallOutput: FunctionCallOutput | undefined, replyRequired: boolean, agentTask: Agent | undefined);
    static create(params: {
        toolCall: FunctionCall;
        toolCallOutput?: FunctionCallOutput;
        replyRequired?: boolean;
        agentTask?: Agent;
    }): _SanitizedOutput;
}
export declare class ToolExecutionOutput {
    readonly toolCall: FunctionCall;
    readonly toolCallOutput: FunctionCallOutput | undefined;
    readonly agentTask: Agent | undefined;
    readonly rawOutput: unknown;
    readonly rawException: Error | undefined;
    readonly replyRequired: boolean;
    constructor(toolCall: FunctionCall, toolCallOutput: FunctionCallOutput | undefined, agentTask: Agent | undefined, rawOutput: unknown, rawException: Error | undefined, replyRequired: boolean);
    static create(params: {
        toolCall: FunctionCall;
        toolCallOutput?: FunctionCallOutput;
        agentTask?: Agent;
        rawOutput: unknown;
        rawException?: Error;
        replyRequired?: boolean;
    }): ToolExecutionOutput;
}
export interface ToolOutput {
    output: ToolExecutionOutput[];
    firstToolStartedFuture: Future<void>;
}
export declare class _JsOutput {
    #private;
    toolCall: FunctionCall;
    output: unknown;
    exception?: Error;
    constructor(toolCall: FunctionCall, output: unknown, exception: Error | undefined);
    static create(params: {
        toolCall: FunctionCall;
        output?: unknown;
        exception?: Error;
    }): _JsOutput;
    sanitize(): _SanitizedOutput;
}
export declare function createToolOutput(params: {
    toolCall: FunctionCall;
    output?: unknown;
    exception?: Error;
}): ToolExecutionOutput;
export declare const INSTRUCTIONS_MESSAGE_ID = "lk.agent_task.instructions";
/**
 * Update the instruction message in the chat context or insert a new one if missing.
 *
 * This function looks for an existing instruction message in the chat context using the identifier
 * 'INSTRUCTIONS_MESSAGE_ID'.
 *
 * @param options - The options for updating the instructions.
 * @param options.chatCtx - The chat context to update.
 * @param options.instructions - The instructions to add.
 * @param options.addIfMissing - Whether to add the instructions if they are missing.
 */
export declare function updateInstructions(options: {
    chatCtx: ChatContext;
    instructions: string | Instructions;
    addIfMissing: boolean;
}): void;
/**
 * Apply the correct {@link Instructions} variant for the turn's input modality.
 *
 * Locates the instructions message (by {@link INSTRUCTIONS_MESSAGE_ID}) and,
 * if its content contains any {@link Instructions} entries, rebuilds the
 * message so each Instructions renders as the chosen variant. No-op when no
 * modality-aware instructions are present.
 */
export declare function applyInstructionsModality(chatCtx: ChatContext, options: {
    modality: 'audio' | 'text';
}): void;
/**
 * The ID of the expressive TTS markup-guide message in the chat context.
 *
 * The value must not change: it is what lets re-injection replace the previous guide.
 */
export declare const EXPRESSIVE_INSTRUCTIONS_MESSAGE_ID = "lk.expressive.instructions";
/**
 * Insert or replace the expressive markup-guide system message.
 *
 * Keyed by {@link EXPRESSIVE_INSTRUCTIONS_MESSAGE_ID} so per-turn re-injection replaces the
 * previous guide instead of accumulating one copy per turn, and a turn that runs with
 * expressive off can remove it again ({@link removeExpressiveInstructions}).
 */
export declare function updateExpressiveInstructions(chatCtx: ChatContext, options: {
    text: string;
}): void;
/**
 * Whether `chatCtx` carries the expressive markup-guide message.
 *
 * Cheap enough to call per turn (an id lookup, no content inspection), so it can gate the
 * far more expensive — and destructive — {@link stripAssistantMarkup} scrub.
 */
export declare function hasExpressiveInstructions(chatCtx: ChatContext): boolean;
/**
 * Remove the expressive markup-guide message added by
 * {@link updateExpressiveInstructions}, if present.
 */
export declare function removeExpressiveInstructions(chatCtx: ChatContext): void;
/**
 * Remove expressive TTS markup from past assistant messages, in place.
 *
 * Called when a turn runs with expressive off (toggled off via an agent-level override, or
 * a handoff to a TTS without a markup dialect): tags left in history would few-shot the
 * LLM into emitting markup that nothing downstream converts or strips, so an unsupported
 * tag would reach the TTS as literal text and be spoken. Mutates the stored history: once
 * a turn runs with expressive off, prior turns' markup is gone even if expressive is
 * re-enabled later (the re-injected instructions carry the style examples instead).
 */
export declare function stripAssistantMarkup(chatCtx: ChatContext): void;
export declare function performLLMInference(node: LLMNode, chatCtx: ChatContext, toolCtx: ToolContext, modelSettings: ModelSettings, controller: AbortController, model?: string, provider?: string): [Task<void>, _LLMGenerationData];
export declare function performTTSInference(node: TTSNode, text: ReadableStream<string | TimedString>, modelSettings: ModelSettings, controller: AbortController, model?: string, provider?: string, readIdleTimeout?: number, textTransforms?: readonly TextTransform[] | null): [Task<void>, _TTSGenerationData];
export interface _TextOut {
    text: string;
    firstTextFut: Future;
}
export declare function performTextForwarding(source: ReadableStream<string | TimedString>, controller: AbortController, textOutput: TextOutput | null): [Task<void>, _TextOut];
export interface _AudioOut {
    firstFrameFut: Future<number>;
    /**
     * Timestamp (ms, `Date.now()`) when the first audio frame was forwarded to the
     * `AudioOutput`. Set by `forwardAudio` as soon as the first TTS frame is
     * received; remains `undefined` until then. Used together with the playback-started
     * timestamp from `firstFrameFut` to derive the assistant's `playbackLatency`
     * metric.
     */
    startedForwardingAt?: number;
    /**
     * Whether this segment has captured at least one of its own frames. Read by the
     * PLAYBACK_STARTED listener registered in `performAudioForwarding` to ignore a
     * stray event emitted by another overlapping segment before this one has played.
     * @internal
     */
    _hasCapturedOwnFrame: boolean;
    /**
     * The output's monotonic segment count (`capturedPlayoutSegments`) when forwarding
     * was set up. The interrupted-commit gate compares the current count against this
     * baseline: a bump proves a frame of THIS segment made it through
     * `AudioOutput.captureFrame` — the same condition under which `waitForPlayout()`
     * waits for this segment's playback event instead of returning a stale one — so
     * the reported playback position can be trusted as evidence of partial playback.
     * (`startedForwardingAt` is set *before* `captureFrame` resolves and is therefore
     * not usable as capture evidence; a frame can bail at a pause/interrupt gate,
     * e.g. `ParticipantAudioOutput`, without ever being counted.)
     */
    capturedSegmentsBefore: number;
}
/**
 * The text that actually reached the user, accounting for interruptions.
 *
 * On a mid-playout interruption (`played === 'partial'`) we prefer the
 * playback-aligned `synchronizedTranscript`, but fall back to the full generated
 * text when no synchronized transcript is available (e.g. avatar outputs) so the
 * heard reply is still committed to chat ctx rather than dropped.
 */
export declare function forwardedTextFor(output: ForwardOutput): string;
export declare function performAudioForwarding(ttsStream: ReadableStream<AudioFrame>, audioOutput: AudioOutput, controller: AbortController, reconcilePlayoutPause: () => void, idleTimeout?: number): [Task<void>, _AudioOut];
export declare function performToolExecutions({ session, speechHandle, toolCtx, toolChoice, toolCallStream, agentName, onToolExecutionStarted, onToolExecutionCompleted, controller, }: {
    session: AgentSession;
    speechHandle: SpeechHandle;
    toolCtx: ToolContext;
    toolChoice?: ToolChoice;
    toolCallStream: ReadableStream<FunctionCall>;
    /** The agent that invoked these tools — a handoff may already have swapped the session's. */
    agentName?: string;
    onToolExecutionStarted?: (toolCall: FunctionCall) => void;
    onToolExecutionCompleted?: (toolExecutionOutput: ToolExecutionOutput) => void;
    controller: AbortController;
}): [Task<void>, ToolOutput];
/**
 * Race tool completion against interruption while preferring an already-settled result.
 *
 * A plain pre-abort short circuit can discard a result that settled earlier in the same
 * JavaScript turn but whose reaction has not run yet. Passing the original promise first to
 * `Promise.race` preserves the settlement order in that case.
 *
 * @internal
 */
export declare function _waitForToolExecutionResult<T>(promise: Promise<T>, signal: AbortSignal): Promise<Aborted<T>>;
export declare function removeInstructions(chatCtx: ChatContext): void;
//# sourceMappingURL=generation.d.ts.map