import { loadSessionStore } from "../../config/sessions.js"; import { isAudioFileName } from "../../media/mime.js"; import { normalizeVerboseLevel, type VerboseLevel } from "../thinking.js"; import type { ReplyPayload } from "../types.js"; import { scheduleFollowupDrain } from "./queue.js"; import type { TypingSignaler } from "./typing-mode.js"; const hasAudioMedia = (urls?: string[]): boolean => Boolean(urls?.some((url) => isAudioFileName(url))); export const isAudioPayload = (payload: ReplyPayload): boolean => hasAudioMedia(payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : undefined)); export const createShouldEmitToolResult = (params: { sessionKey?: string; storePath?: string; resolvedVerboseLevel: VerboseLevel; }): (() => boolean) => { return () => { if (!params.sessionKey || !params.storePath) { return params.resolvedVerboseLevel !== "off"; } try { const store = loadSessionStore(params.storePath); const entry = store[params.sessionKey]; const current = normalizeVerboseLevel(entry?.verboseLevel); if (current) return current !== "off"; } catch { // ignore store read failures } return params.resolvedVerboseLevel !== "off"; }; }; export const createShouldEmitToolOutput = (params: { sessionKey?: string; storePath?: string; resolvedVerboseLevel: VerboseLevel; }): (() => boolean) => { return () => { if (!params.sessionKey || !params.storePath) { return params.resolvedVerboseLevel === "full"; } try { const store = loadSessionStore(params.storePath); const entry = store[params.sessionKey]; const current = normalizeVerboseLevel(entry?.verboseLevel); if (current) return current === "full"; } catch { // ignore store read failures } return params.resolvedVerboseLevel === "full"; }; }; export const finalizeWithFollowup = ( value: T, queueKey: string, runFollowupTurn: Parameters[1], ): T => { scheduleFollowupDrain(queueKey, runFollowupTurn); return value; }; export const signalTypingIfNeeded = async ( payloads: ReplyPayload[], typingSignals: TypingSignaler, ): Promise => { const shouldSignalTyping = payloads.some((payload) => { const trimmed = payload.text?.trim(); if (trimmed) return true; if (payload.mediaUrl) return true; if (payload.mediaUrls && payload.mediaUrls.length > 0) return true; return false; }); if (shouldSignalTyping) { await typingSignals.signalRunStart(); } };