import type { ImageContent } from "@mariozechner/pi-ai";
import type { TypingController } from "./reply/typing.js";
export type BlockReplyContext = {
    abortSignal?: AbortSignal;
    timeoutMs?: number;
};
/** Context passed to onModelSelected callback with actual model used. */
export type ModelSelectedContext = {
    provider: string;
    model: string;
    thinkLevel: string | undefined;
};
export type GetReplyOptions = {
    /** Override run id for koi events (defaults to random UUID). */
    runId?: string;
    /** Abort signal for the underlying koi run. */
    abortSignal?: AbortSignal;
    /** Optional inbound images (used for webchat attachments). */
    images?: ImageContent[];
    /** Notifies when an koi run actually starts (useful for webchat command handling). */
    onKoiRunStart?: (runId: string) => void;
    onReplyStart?: () => Promise<void> | void;
    /** Called when the typing controller cleans up (e.g., run ended with NO_REPLY). */
    onTypingCleanup?: () => void;
    onTypingController?: (typing: TypingController) => void;
    isHeartbeat?: boolean;
    /** Set internally when retrying a heartbeat due to zero tool calls. Prevents infinite retry loops. */
    isHeartbeatRetry?: boolean;
    onPartialReply?: (payload: ReplyPayload) => Promise<void> | void;
    onReasoningStream?: (payload: ReplyPayload) => Promise<void> | void;
    onBlockReply?: (payload: ReplyPayload, context?: BlockReplyContext) => Promise<void> | void;
    onToolResult?: (payload: ReplyPayload) => Promise<void> | void;
    /** Called when the actual model is selected (including after fallback).
     * Use this to get model/provider/thinkLevel for responsePrefix template interpolation. */
    onModelSelected?: (ctx: ModelSelectedContext) => void;
    disableBlockStreaming?: boolean;
    /** Timeout for block reply delivery (ms). */
    blockReplyTimeoutMs?: number;
    /** If provided, only load these skills for this session (empty = no skills). */
    skillFilter?: string[];
    /** Mutable ref to track if a reply was sent (for Slack "first" threading mode). */
    hasRepliedRef?: {
        value: boolean;
    };
};
export type ReplyPayload = {
    text?: string;
    mediaUrl?: string;
    mediaUrls?: string[];
    replyToId?: string;
    replyToTag?: boolean;
    /** True when [[reply_to_current]] was present but not yet mapped to a message id. */
    replyToCurrent?: boolean;
    /** Send audio as voice message (bubble) instead of audio file. Defaults to false. */
    audioAsVoice?: boolean;
    isError?: boolean;
    /** Channel-specific payload data (per-channel envelope). */
    channelData?: Record<string, unknown>;
    /** Number of tool calls made during a heartbeat run (auditor tracking). */
    heartbeatToolCallCount?: number;
};
