import { appendPiTranscriptMessage } from "../../session/parity/jsonl-transcript-io.js";
import { formatAgentRunErrorForClient } from "../client-error-format.js";
import { resolveEffectiveReasoningLevel } from "../../session/thinking-resolve.js";
import { initSessionTurn } from "../../session/init-session-turn.js";
import "../../session/index.js";
import { isVoiceLikeAttachment, mergeSttConfigFromAppConfig, mergeVoiceTranscriptsIntoUserText } from "../../channels/attachments/voice-stt-webchat.js";
import { abortEmbeddedRun } from "../embedded/runs.js";
import { mapEmbeddedEventToGatewaySse } from "../embedded/map-stream-events.js";
import { applyReasoningVisibilityToSseEvent } from "../streaming/reasoning-visibility-sse.js";
import { AsyncQueue } from "./async-queue.js";
import { hydratePerTurnState, runDirectAgentTurn, tryRunSlashCommand } from "./direct-turn-helpers.js";
//#region src/agent/service/process-direct-streaming.ts
async function* runProcessDirectStreaming(deps, input) {
	const sessionKey = input.sessionKey ?? "agent:main:main";
	const { channel, chatId } = deps.parseSessionKey(sessionKey);
	const context = deps.initDirectStreamingSession(sessionKey, channel, chatId);
	const queue = new AsyncQueue();
	let reasoningLevel = "stream";
	const pushVisible = (event) => {
		const visible = applyReasoningVisibilityToSseEvent(event, reasoningLevel);
		if (visible !== null) queue.push(visible);
	};
	const formatStreamError = (raw) => {
		let provider;
		let modelRef;
		try {
			provider = deps.modelManager.getResolvedModelForSession(sessionKey).provider;
			modelRef = deps.modelManager.getModelForSession(sessionKey);
		} catch {}
		return formatAgentRunErrorForClient(raw, {
			provider,
			modelRef
		});
	};
	if (channel === "webchat") deps.registerWebchatSsePublisher(sessionKey, pushVisible);
	const signal = input.signal;
	let userAborted = false;
	let abortHandled = false;
	let inboundVoice = false;
	let ranSlashCommand = false;
	let mergedUserText = input.content;
	let webchatSlashReceipt;
	const taskPromise = (async () => {
		try {
			const cfg = deps.getConfig();
			let turnBody = input.content;
			let resetTriggeredAtInit = false;
			if (cfg) {
				const turn = await initSessionTurn({
					cfg,
					sessionKey,
					body: input.content,
					resetSession: deps.resetSession
				});
				resetTriggeredAtInit = turn.resetTriggered;
				if (turn.bareReset && turn.ackMessage) {
					ranSlashCommand = true;
					webchatSlashReceipt = turn.ackMessage;
					pushVisible({
						type: "token",
						content: turn.ackMessage
					});
					return;
				}
				turnBody = turn.bodyStripped;
				if (turn.isNewSession) deps.log.debug({
					sessionKey,
					sessionId: turn.sessionId,
					previousSessionId: turn.previousSessionId,
					resetTriggered: turn.resetTriggered,
					staleRollover: turn.staleRollover
				}, "Session reset boundary at direct turn start");
			}
			await hydratePerTurnState(deps, sessionKey, input.thinking);
			{
				const defReason = deps.getConfig()?.agents?.defaults?.reasoningDefault ?? "stream";
				reasoningLevel = await resolveEffectiveReasoningLevel(deps.sessionConfigStore, sessionKey, defReason);
			}
			const prepared = await deps.prepareInboundAttachments(sessionKey, input.attachments);
			const sttCfg = mergeSttConfigFromAppConfig(deps.getConfig()?.tools?.media?.audio, deps.getConfig()?.tools?.media);
			const voiceMerge = await mergeVoiceTranscriptsIntoUserText(deps.attachmentRootsForSession(sessionKey), prepared, turnBody, sttCfg);
			mergedUserText = voiceMerge.text;
			inboundVoice = voiceMerge.inboundVoice;
			if (inboundVoice) {
				const transcriptParts = [voiceMerge.voiceTranscripts.filter(Boolean).join("\n"), turnBody.trim()].filter(Boolean);
				const voiceAttachments = (prepared ?? []).filter(isVoiceLikeAttachment).map((att) => ({
					workspaceRelativePath: att.workspaceRelativePath,
					mimeType: att.mimeType,
					name: att.name
				}));
				pushVisible({
					type: "user_transcript",
					text: transcriptParts.join("\n\n"),
					attachments: voiceAttachments
				});
			}
			const armAbort = () => {
				if (abortHandled) return;
				abortHandled = true;
				userAborted = true;
				abortEmbeddedRun(sessionKey);
				queue.close();
			};
			if (signal) {
				if (signal.aborted) {
					armAbort();
					return;
				}
				signal.addEventListener("abort", armAbort, { once: true });
			}
			const slash = await tryRunSlashCommand(deps, {
				sessionKey,
				channel,
				chatId,
				senderId: context.senderId,
				isGroup: context.isGroup
			}, mergedUserText, { skipResetCommands: resetTriggeredAtInit });
			if (slash.matched) {
				ranSlashCommand = true;
				const text = slash.aggregatedText.trim();
				if (text) {
					webchatSlashReceipt = text;
					pushVisible({
						type: "token",
						content: text
					});
				} else if (channel === "webchat") {
					webchatSlashReceipt = "Command finished with no assistant text. If you used `/goal`, a follow-up turn may still be scheduled automatically.";
					pushVisible({
						type: "token",
						content: webchatSlashReceipt
					});
				}
				return;
			}
			const textForAgent = mergedUserText.trimStart().startsWith("/skill:") ? deps.agentManager.expandSkillUserText(mergedUserText) : mergedUserText;
			const userMessage = {
				role: "user",
				content: await deps.buildMessageContent(textForAgent, prepared, sessionKey),
				timestamp: Date.now()
			};
			if (channel === "webchat") pushVisible({
				type: "user_message",
				timestamp: userMessage.timestamp,
				content: userMessage.content,
				attachments: prepared?.map((att) => ({
					type: att.type,
					mimeType: att.mimeType,
					name: att.name,
					size: att.size,
					workspaceRelativePath: att.workspaceRelativePath
				}))
			});
			const result = await runDirectAgentTurn({
				sessionStore: deps.sessionStore,
				agentManager: deps.agentManager,
				modelManager: deps.modelManager,
				config: deps.getConfig()
			}, {
				sessionKey,
				userMessage,
				abortSignal: signal,
				onEvent: (embeddedEvent) => {
					const mapped = mapEmbeddedEventToGatewaySse(embeddedEvent);
					if (mapped) {
						if (mapped.type === "error" && typeof mapped.content === "string") mapped.content = formatStreamError(mapped.content);
						pushVisible(mapped);
					}
				}
			});
			if (result.lastAssistantText) deps.onTurnComplete?.(sessionKey, result.lastAssistantText);
			if (!result.ok && result.errorMessage && !abortHandled) pushVisible({
				type: "error",
				content: formatStreamError(result.errorMessage)
			});
		} catch (err) {
			if (!abortHandled) pushVisible({
				type: "error",
				content: formatStreamError(err instanceof Error ? err.message : String(err))
			});
		} finally {
			queue.close();
		}
	})();
	try {
		for await (const event of queue) yield event;
		await taskPromise;
		if (channel === "webchat" && ranSlashCommand) try {
			const { absPath } = await deps.sessionStore.resolveTranscriptPath(sessionKey);
			const workspaceDir = deps.agentManager.getResolvedWorkspaceForSession(sessionKey);
			await appendPiTranscriptMessage({
				absPath,
				cwd: workspaceDir,
				message: {
					role: "user",
					content: [{
						type: "text",
						text: mergedUserText
					}],
					timestamp: Date.now()
				},
				sessionKey
			});
			if (webchatSlashReceipt?.trim()) await appendPiTranscriptMessage({
				absPath,
				cwd: workspaceDir,
				message: {
					role: "assistant",
					content: [{
						type: "text",
						text: webchatSlashReceipt.trim()
					}],
					timestamp: Date.now()
				},
				sessionKey
			});
			deps.reloadWebchatTranscript?.(sessionKey);
		} catch (err) {
			deps.log.warn({
				err,
				sessionKey
			}, "Failed to persist webchat slash command receipt");
		}
		if (!userAborted) {
			const ttsAudioEvent = await deps.maybeEmitWebchatTts(sessionKey, inboundVoice);
			if (ttsAudioEvent) yield ttsAudioEvent;
		}
		deps.recordPersistentGoalStreamOutcome?.(sessionKey, { skipPersistentGoalPostTurn: ranSlashCommand });
	} finally {
		if (channel === "webchat") deps.unregisterWebchatSsePublisher(sessionKey);
		deps.endDirectRequestContext();
	}
}
//#endregion
export { runProcessDirectStreaming };

//# sourceMappingURL=process-direct-streaming.js.map