import { runWithLogContext, updateAsyncLogContext } from "../../utils/logger/context.js";
import { init_logger } from "../../utils/logger.js";
import { shouldSkipResetOverlapCommand } from "../../session/reset-triggers.js";
import { initSessionTurn } from "../../session/init-session-turn.js";
import { MessageBusShutdownError } from "../../infra/bus/queue.js";
import "../../infra/bus/index.js";
import { inboundMessageLogRequestId } from "../service-inbound-utils.js";
import { runEmbeddedTurnForSession } from "../embedded/run-for-session.js";
//#region src/agent/inbound/inbound-loop.ts
init_logger();
/** Initial back-off when bus consume fails. Doubles each consecutive failure. */
const INBOUND_CONSUME_BASE_BACKOFF_MS = 1e3;
/** Hard cap on back-off so a long bus outage settles at one retry per 30s. */
const INBOUND_CONSUME_MAX_BACKOFF_MS = 3e4;
var InboundLoop = class {
	cfg;
	log;
	running = false;
	channelManagerRef = null;
	constructor(cfg) {
		this.cfg = cfg;
		this.log = cfg.log;
	}
	setChannelManager(channelManager) {
		this.channelManagerRef = channelManager;
	}
	isRunning() {
		return this.running;
	}
	/** Begin consuming inbound messages from the bus until {@link stop} or shutdown. */
	async start() {
		this.running = true;
		await this.cfg.hookHandler.trigger("session_start", { sessionId: this.cfg.agentId });
		let consecutiveConsumeFailures = 0;
		while (this.running) {
			let msg;
			try {
				msg = await this.cfg.bus.consumeInbound();
				consecutiveConsumeFailures = 0;
			} catch (error) {
				if (error instanceof MessageBusShutdownError) break;
				consecutiveConsumeFailures += 1;
				const delayMs = Math.min(INBOUND_CONSUME_MAX_BACKOFF_MS, INBOUND_CONSUME_BASE_BACKOFF_MS * 2 ** (consecutiveConsumeFailures - 1));
				const em = error instanceof Error ? error.message : String(error);
				this.log.error({
					err: error,
					errorMessage: em,
					phase: "inbound_consume",
					consecutiveFailures: consecutiveConsumeFailures,
					backoffMs: delayMs
				}, `Bus consume failed (backing off ${delayMs}ms): ${em}`);
				await new Promise((resolve) => setTimeout(resolve, delayMs));
				continue;
			}
			try {
				await this.handleInboundMessage(msg);
			} catch (error) {
				const em = error instanceof Error ? error.message : String(error);
				this.log.error({
					err: error,
					errorMessage: em,
					phase: "inbound_handle",
					channel: msg.channel,
					chatId: msg.chat_id
				}, `Inbound message handler failed: ${em}`);
			}
		}
		await this.cfg.hookHandler.trigger("session_end", {
			sessionId: this.cfg.agentId,
			messageCount: 0
		});
	}
	/** Cooperatively stop the loop; the current message (if any) is allowed to finish. */
	stop() {
		this.running = false;
	}
	async handleInboundMessage(msg) {
		await runWithLogContext({ requestId: inboundMessageLogRequestId(msg) }, async () => {
			const { context, isCommand, command, commandArgs } = await this.cfg.messageRouter.routeMessage(msg);
			const sessionContext = {
				sessionKey: context.sessionKey,
				channel: context.channel,
				chatId: context.chatId,
				senderId: context.senderId || "",
				isGroup: context.isGroup || false,
				metadata: { transcribedVoice: msg.metadata?.transcribedVoice === true }
			};
			updateAsyncLogContext({ sessionId: sessionContext.sessionKey });
			await this.cfg.sessionContextManager.runWith(sessionContext, async () => {
				this.cfg.feedbackCoordinator.setContext(sessionContext);
				this.cfg.agentManager.getOrCreateAgent(sessionContext.sessionKey);
				this.cfg.setupSessionEventHandling(sessionContext.sessionKey);
				await this.cfg.sessionLifecycleManager.startSession(sessionContext);
				let typingController = null;
				let inboundTurnArmed = false;
				let busProcessFailed;
				let inboundMsg = msg;
				try {
					if (msg.channel === "system") {
						await this.handleSystemMessage(msg, sessionContext);
						return;
					}
					if (this.channelManagerRef && msg.channel !== "cli") await this.channelManagerRef.dispatchInboundMessageAction(msg);
					const cfg = this.cfg.getConfig();
					let effectiveContent = msg.content;
					let resetTriggeredAtInit = false;
					if (cfg && typeof msg.content === "string") {
						const turn = await initSessionTurn({
							cfg,
							sessionKey: sessionContext.sessionKey,
							body: msg.content,
							resetSession: (sk) => this.cfg.resetSession(sk)
						});
						resetTriggeredAtInit = turn.resetTriggered;
						if (turn.bareReset && turn.ackMessage) {
							await this.cfg.bus.publishOutbound({
								channel: sessionContext.channel,
								chat_id: sessionContext.chatId,
								content: turn.ackMessage,
								type: "message"
							});
							return;
						}
						effectiveContent = turn.bodyStripped;
					}
					if (isCommand && command) {
						if (!shouldSkipResetOverlapCommand(command, resetTriggeredAtInit)) {
							if (await this.cfg.commandHandler.executeCommand(command, commandArgs || "", {
								sessionKey: sessionContext.sessionKey,
								channel: sessionContext.channel,
								chatId: sessionContext.chatId,
								senderId: sessionContext.senderId,
								isGroup: sessionContext.isGroup,
								inboundMetadata: msg.metadata
							})) return;
						}
					}
					inboundMsg = effectiveContent !== msg.content ? {
						...msg,
						content: effectiveContent
					} : msg;
					typingController = this.cfg.outboundCoordinator.createTypingControllerForInbound(msg);
					typingController?.start();
					if (this.channelManagerRef && msg.channel !== "cli") {
						const meta = msg.metadata;
						const streamHandle = this.channelManagerRef.startStream(msg.channel, msg.chat_id, meta?.accountId, {
							threadId: meta?.threadId,
							replyToMessageId: meta?.messageId
						});
						if (streamHandle) this.cfg.setStreamHandle(streamHandle);
					}
					this.cfg.sessionState.beginInboundTurn(sessionContext.sessionKey);
					inboundTurnArmed = true;
					try {
						await this.cfg.agentOrchestrator.process(inboundMsg, sessionContext);
					} catch (procErr) {
						busProcessFailed = procErr instanceof Error ? procErr.message : String(procErr);
						throw procErr;
					}
				} finally {
					await this.cfg.sessionLifecycleManager.endSession(sessionContext);
					await this.cfg.streamManager.end();
					try {
						await this.cfg.outboundCoordinator.sendFinalResponse(msg, sessionContext);
					} finally {
						await typingController?.stop();
					}
					if (inboundTurnArmed) {
						const meta = msg.metadata;
						const assistantPlainText = this.cfg.getLastAssistantPlainText(sessionContext.sessionKey) ?? "";
						try {
							await this.cfg.outboundCoordinator.emitSessionTurnComplete({
								sessionKey: sessionContext.sessionKey,
								channel: sessionContext.channel,
								chatId: sessionContext.chatId,
								inboundUserText: inboundMsg.content,
								assistantPlainText,
								aborted: false,
								...busProcessFailed !== void 0 ? { streamError: busProcessFailed } : {},
								skipPersistentGoalPostTurn: false,
								outboundMetadata: {
									accountId: meta?.accountId,
									threadId: meta?.threadId
								}
							});
						} catch (turnErr) {
							const em = turnErr instanceof Error ? turnErr.message : String(turnErr);
							this.log.warn({
								err: turnErr,
								sessionKey: sessionContext.sessionKey
							}, `Session turn complete failed: ${em}`);
						}
						this.cfg.sessionState.endInboundTurn(sessionContext.sessionKey);
					}
					this.cfg.feedbackCoordinator.endTask();
					this.cfg.feedbackCoordinator.clearContext();
				}
			});
		});
	}
	async handleSystemMessage(msg, context) {
		this.log.debug({ sessionKey: context.sessionKey }, "Processing system message");
		await this.cfg.sessionHydrator.workspace(context.sessionKey);
		await this.cfg.sessionHydrator.model(context.sessionKey);
		const messages = await this.cfg.sessionStore.load(context.sessionKey);
		await this.cfg.checkAndCompact(context.sessionKey, messages);
		const systemMessage = {
			role: "user",
			content: [{
				type: "text",
				text: `[System: ${msg.sender_id}] ${msg.content}`
			}],
			timestamp: Date.now()
		};
		try {
			const finalContent = (await runEmbeddedTurnForSession({
				sessionKey: context.sessionKey,
				userMessage: systemMessage,
				sessionStore: this.cfg.sessionStore,
				agentManager: this.cfg.agentManager,
				modelManager: this.cfg.modelManager,
				getConfig: this.cfg.getConfig
			})).lastAssistantText ?? this.cfg.getLastAssistantPlainText(context.sessionKey);
			if (finalContent) {
				this.cfg.sessionState.setLastAssistantText(context.sessionKey, finalContent);
				const hookResult = await this.cfg.hookHandler.runMessageSending(context.chatId, finalContent, context.channel);
				if (hookResult.send) await this.cfg.bus.publishOutbound({
					channel: context.channel,
					chat_id: context.chatId,
					content: hookResult.content || finalContent,
					type: "message"
				});
			}
			this.cfg.enqueueMaybeAutoTitleAfterPersist(context.sessionKey);
		} catch (error) {
			const em = error instanceof Error ? error.message : String(error);
			this.log.error({
				err: error,
				errorMessage: em,
				sessionKey: context.sessionKey,
				channel: context.channel,
				chatId: context.chatId,
				senderId: msg.sender_id
			}, `System message handling failed: ${em}`);
			await this.cfg.bus.publishOutbound({
				channel: context.channel,
				chat_id: context.chatId,
				content: "❌ An error occurred while processing the system message.",
				type: "message"
			});
		}
	}
};
//#endregion
export { InboundLoop };

//# sourceMappingURL=inbound-loop.js.map