import { inboundCorrelationMetadataFromAsyncLogContext } from "../../utils/logger/context.js";
import { createLogger } from "../../utils/logger/index.js";
import { init_logger } from "../../utils/logger.js";
import { formatAgentRunErrorForClient } from "../../agent/client-error-format.js";
import { prependEnvelopeTimestamp } from "../../channels/envelope-timestamp.js";
import { shouldSkipWebchatInboundByAbortCutoff } from "../../session/abort-cutoff.js";
import "../chat-limits.js";
import { resolveWebchatSessionKey } from "../resolve-webchat-session-key.js";
import crypto from "crypto";
//#region src/gateway/service/run-gateway-agent.ts
init_logger();
const log = createLogger("GatewayService");
/**
* @param runOptions.signal — When set (e.g. client disconnect), aborts in-flight generation and persists partial output.
*/
async function* runGatewayAgent(deps, message, channel, chatId, attachments, thinking, runOptions) {
	const cappedAttachments = attachments && attachments.length > 10 ? attachments.slice(0, 10) : attachments;
	if (attachments && cappedAttachments && attachments.length > cappedAttachments.length) log.debug({
		dropped: attachments.length - cappedAttachments.length,
		max: 10
	}, "Attachments capped for webchat");
	const runId = crypto.randomUUID();
	const { config, agentService, bus, runRelay, runAbortControllers, activeWebchatRunBySession, sessionIndex: sessionIndexFromDeps, emit } = deps;
	const sessionIndex = sessionIndexFromDeps;
	let webchatSessionKey;
	let webchatStaleSkip = false;
	if (channel === "webchat") {
		const resolved = resolveWebchatSessionKey({
			cfg: config,
			chatId,
			newSession: false
		});
		if (resolved.ok === false) throw new Error(resolved.error);
		webchatSessionKey = resolved.sessionKey;
		const meta = await sessionIndex.getSessionMetadata(webchatSessionKey);
		webchatStaleSkip = shouldSkipWebchatInboundByAbortCutoff(meta, runOptions?.clientCreatedAtMs);
		if (!webchatStaleSkip && meta?.abortCutoffTimestamp !== void 0) await sessionIndex.updateSessionMetadata(webchatSessionKey, { abortCutoffTimestamp: void 0 }).catch(() => {});
		runRelay.ensureRun(runId, webchatSessionKey);
		runAbortControllers.set(runId, new AbortController());
	}
	const statusEvent = {
		type: "status",
		status: "accepted",
		runId
	};
	if (channel === "webchat") runRelay.publish(runId, statusEvent);
	yield statusEvent;
	try {
		if (channel === "webchat" && webchatSessionKey) {
			if (webchatStaleSkip) {
				runRelay.complete(runId);
				runAbortControllers.delete(runId);
				return {
					status: "skipped",
					summary: "Stale inbound after abort (clientCreatedAtMs before cutoff)"
				};
			}
			const sessionKey = webchatSessionKey;
			const timezone = agentService.resolveUserTimezoneForSession(sessionKey);
			const stampedMessage = message.trimStart().startsWith("/") ? message : prependEnvelopeTimestamp(message, timezone);
			const prepared = await agentService.prepareInboundAttachments(sessionKey, cappedAttachments);
			const runAbort = runAbortControllers.get(runId);
			if (!runAbort) throw new Error("run abort controller missing for webchat");
			const mergedSignal = runOptions?.signal ? AbortSignal.any([runOptions.signal, runAbort.signal]) : runAbort.signal;
			agentService.beginInboundTurn(sessionKey);
			activeWebchatRunBySession.set(sessionKey, runId);
			let streamError;
			try {
				emit("agent.stream", {
					sessionKey,
					event: statusEvent
				});
				const eventStream = agentService.turnDispatcher.processDirectStreaming(stampedMessage, sessionKey, prepared, thinking, { signal: mergedSignal });
				for await (const event of eventStream) {
					runRelay.publish(runId, event);
					emit("agent.stream", {
						sessionKey,
						event
					});
					yield event;
				}
				runRelay.complete(runId);
				try {
					const metaAfter = await sessionIndex.getSessionMetadata(sessionKey);
					if (metaAfter?.name) emit("session.updated", {
						key: sessionKey,
						name: metaAfter.name
					});
				} catch {}
				return {
					status: mergedSignal.aborted ? "aborted" : "ok",
					summary: mergedSignal.aborted ? "Interrupted" : "Message processed successfully"
				};
			} catch (error) {
				log.error({ error }, "Agent processing failed");
				streamError = error instanceof Error ? error.message : "Unknown error";
				const errorEvent = {
					type: "error",
					content: formatAgentRunErrorForClient(streamError)
				};
				runRelay.publish(runId, errorEvent);
				emit("agent.stream", {
					sessionKey,
					event: errorEvent
				});
				runRelay.complete(runId);
				yield errorEvent;
				return {
					status: "error",
					summary: streamError
				};
			} finally {
				activeWebchatRunBySession.delete(sessionKey);
				runAbortControllers.delete(runId);
				const assistantPlainText = agentService.getLastAssistantPlainText(sessionKey);
				const streamOutcome = agentService.persistentGoals.takeStreamOutcome(sessionKey);
				try {
					await agentService.outboundCoordinator.emitSessionTurnComplete({
						sessionKey,
						channel: "webchat",
						chatId: sessionKey,
						inboundUserText: message,
						assistantPlainText,
						aborted: mergedSignal.aborted,
						...streamError !== void 0 ? { streamError } : {},
						skipPersistentGoalPostTurn: streamOutcome?.skipPersistentGoalPostTurn ?? false,
						outboundMetadata: {}
					});
				} catch (goalErr) {
					log.warn({
						err: goalErr,
						sessionKey
					}, `Session turn complete failed: ${goalErr instanceof Error ? goalErr.message : String(goalErr)}`);
				}
				agentService.endInboundTurn(sessionKey);
			}
		}
		const correlationMeta = inboundCorrelationMetadataFromAsyncLogContext();
		await bus.publishInbound({
			channel,
			sender_id: "gateway",
			chat_id: chatId,
			content: message,
			...correlationMeta ? { metadata: correlationMeta } : {}
		});
		yield {
			type: "token",
			content: "Processing...\n"
		};
		await new Promise((resolve) => setTimeout(resolve, 1e3));
		yield {
			type: "token",
			content: "Done\n"
		};
		return {
			status: "ok",
			summary: "Message processed"
		};
	} catch (error) {
		log.error({ error }, "Agent run failed");
		throw error;
	}
}
//#endregion
export { runGatewayAgent };

//# sourceMappingURL=run-gateway-agent.js.map