import { aI as readStackEnv } from './src-CidJbygh.js';
import { g as getState } from './state-fxN45gBp.js';
import { d as getRequestId, r as requireAdmin, e as errorResponse } from './helpers-kzwksNq1.js';
import { o as opencodeFetch } from './http-D8YVIqJa.js';
import { g as getCurrentConfig } from './config-CO8OSXtU.js';
import { existsSync, readFileSync } from 'node:fs';
import { join } from 'node:path';
import './chunk-CLZ62Ad-.js';
import 'node:module';
import './provider-constants-DPrj3yBX.js';
import './dist-DOFcG0Zp.js';
import 'buffer';
import 'fs';
import 'path';
import 'node:child_process';
import 'node:url';
import 'node:crypto';
import 'events';
import 'node:events';
import 'node:stream';
import 'node:string_decoder';
import 'assert';
import 'zlib';
import 'node:assert';
import 'node:fs/promises';
import 'node:os';
import './endpoints-UdQGgKsR.js';
import './coercion-TNFJisCC.js';

//#region src/lib/server/speech-prep.ts
function personaPath() {
	return join(getState().configDir, "assistant", "persona.md");
}
function readPersona() {
	const path = personaPath();
	if (!existsSync(path)) return "";
	try {
		return readFileSync(path, "utf-8").trim();
	} catch {
		return "";
	}
}
function extractText(response) {
	return (response.parts ?? []).filter((part) => part.type === "text" && typeof part.text === "string").map((part) => part.text?.trim() ?? "").join(" ").trim();
}
function buildPrompt(input) {
	const personaBlock = input.persona ? input.persona : "No persona markdown is configured. Use a neutral, helpful, conversational tone.";
	if (input.mode === "chat_ack") return [
		"You are preparing a short spoken acknowledgement for text-to-speech.",
		"Return only the exact words to speak.",
		"Keep it to one brief sentence.",
		"Sound conversational and aligned with the persona context.",
		"Do not use markdown, lists, or quotes.",
		"Do not answer the full request. Just acknowledge that you are working on it.",
		"",
		"Persona markdown:",
		personaBlock,
		"",
		"User request:",
		input.userText
	].join("\n");
	return [
		"You are preparing a spoken summary for text-to-speech.",
		"Return only the exact words to speak.",
		"Rewrite the final assistant reply into a concise, natural, conversational response.",
		"Preserve the meaning and important caveats.",
		"Keep it brief enough for audio playback.",
		"Do not use markdown, lists, or quotes.",
		"",
		"Persona markdown:",
		personaBlock,
		"",
		"User request:",
		input.userText,
		"",
		"Final assistant reply:",
		input.assistantText ?? ""
	].join("\n");
}
function resolveSpeechModel(config) {
	const small = typeof config.small_model === "string" ? config.small_model.trim() : "";
	if (small) return small;
	return (typeof config.model === "string" ? config.model.trim() : "") || null;
}
async function loadSpeechConfig() {
	try {
		return await opencodeFetch("/config");
	} catch {
		return getCurrentConfig();
	}
}
async function prepareSpeechText(input) {
	const userText = input.userText.trim();
	if (!userText) return null;
	if (input.mode === "chat_reply" && !input.assistantText?.trim()) return null;
	const persona = readPersona();
	const model = resolveSpeechModel(await loadSpeechConfig());
	const prompt = buildPrompt({
		mode: input.mode,
		userText,
		assistantText: input.assistantText?.trim(),
		persona
	});
	let sessionId = "";
	try {
		const created = await opencodeFetch("/session", {
			method: "POST",
			body: JSON.stringify({})
		});
		sessionId = typeof created.id === "string" ? created.id : "";
		if (!sessionId) return null;
		return extractText(await opencodeFetch(`/session/${encodeURIComponent(sessionId)}/message`, {
			method: "POST",
			body: JSON.stringify({
				parts: [{
					type: "text",
					text: prompt
				}],
				...model ? { model } : {}
			})
		})) || null;
	} finally {
		if (sessionId) await opencodeFetch(`/session/${encodeURIComponent(sessionId)}`, { method: "DELETE" }).catch(() => {});
	}
}
//#endregion
//#region src/routes/api/speak/+server.ts
var DEFAULT_MODEL = "kokoro";
var DEFAULT_VOICE = "bf_isabella";
var DEFAULT_FORMAT = "wav";
var UPSTREAM_TIMEOUT_MS = 6e4;
function redactKey(s) {
	return s.replace(/(sk-[A-Za-z0-9_-]{4,})/g, "sk-***").replace(/(Bearer\s+)[A-Za-z0-9._-]+/gi, "$1***");
}
var POST = async (event) => {
	const requestId = getRequestId(event);
	const authError = requireAdmin(event, requestId);
	if (authError) return authError;
	const stackEnv = readStackEnv(getState().stackDir);
	const ttsBaseURL = (stackEnv.OP_TTS_BASE_URL || process.env.OP_TTS_BASE_URL || "").trim();
	const ttsModel = (stackEnv.OP_TTS_MODEL || process.env.OP_TTS_MODEL || "").trim() || DEFAULT_MODEL;
	const ttsVoice = (stackEnv.OP_TTS_VOICE || process.env.OP_TTS_VOICE || "").trim() || DEFAULT_VOICE;
	const ttsApiKey = (process.env.OP_TTS_API_KEY ?? "").trim();
	if (!ttsBaseURL) return errorResponse(503, "tts_not_configured", "Configure a TTS engine in Admin → Voice settings.", {}, requestId);
	let body;
	try {
		body = await event.request.json();
	} catch {
		return errorResponse(400, "bad_request", "Invalid JSON body", {}, requestId);
	}
	const b = body ?? {};
	const text = typeof b.text === "string" ? b.text.trim() : "";
	if (!text) return errorResponse(400, "bad_request", "\"text\" is required", {}, requestId);
	const mode = b.mode === "chat_ack" || b.mode === "chat_reply" ? b.mode : null;
	const userText = typeof b.userText === "string" ? b.userText.trim() : "";
	const assistantText = typeof b.assistantText === "string" ? b.assistantText.trim() : "";
	const voice = typeof b.voice === "string" && b.voice.trim() ? b.voice.trim() : ttsVoice;
	const format = typeof b.format === "string" && b.format.trim() ? b.format.trim() : DEFAULT_FORMAT;
	let speechText = text;
	if (mode) try {
		const prepared = await prepareSpeechText({
			mode,
			userText,
			assistantText
		});
		if (prepared) speechText = prepared;
	} catch (err) {
		console.warn("[api/speak] speech prep failed; falling back to raw text", err);
	}
	const upstreamUrl = ttsBaseURL.replace(/\/+$/, "") + "/v1/audio/speech";
	const headers = { "content-type": "application/json" };
	if (ttsApiKey) headers["authorization"] = `Bearer ${ttsApiKey}`;
	let upstream;
	try {
		upstream = await fetch(upstreamUrl, {
			method: "POST",
			headers,
			body: JSON.stringify({
				model: ttsModel,
				voice,
				input: speechText,
				response_format: format
			}),
			signal: AbortSignal.timeout(UPSTREAM_TIMEOUT_MS)
		});
	} catch (err) {
		return errorResponse(502, "upstream_error", `Could not reach TTS endpoint: ${redactKey(err instanceof Error ? err.message : String(err))}`, { upstream: upstreamUrl }, requestId);
	}
	if (!upstream.ok) {
		const body = await upstream.text().catch(() => "");
		return errorResponse(502, "upstream_error", `TTS endpoint returned ${upstream.status}`, {
			upstreamStatus: upstream.status,
			body: redactKey(body).slice(0, 500)
		}, requestId);
	}
	const responseHeaders = new Headers();
	responseHeaders.set("content-type", upstream.headers.get("content-type") ?? "audio/wav");
	const contentLength = upstream.headers.get("content-length");
	if (contentLength) responseHeaders.set("content-length", contentLength);
	responseHeaders.set("x-request-id", requestId);
	return new Response(upstream.body, {
		status: 200,
		headers: responseHeaders
	});
};

export { POST };
//# sourceMappingURL=_server.ts-D7IC_Y32.js.map
