import { createLogger } from "../../utils/logger/index.js";
import { init_logger } from "../../utils/logger.js";
import { createTypingController } from "../lifecycle/typing.js";
import { shouldSilence } from "../../heartbeat/tokens.js";
//#region src/agent/messaging/outbound-coordinator.ts
init_logger();
const log = createLogger("OutboundCoordinator");
var OutboundCoordinator = class {
	bus;
	hookHandler;
	streamManager;
	getConfig;
	getLastAssistantPlainText;
	runPersistentGoalPostTurn;
	constructor(config) {
		this.bus = config.bus;
		this.hookHandler = config.hookHandler;
		this.streamManager = config.streamManager;
		this.getConfig = config.getConfig;
		this.getLastAssistantPlainText = config.getLastAssistantPlainText;
		this.runPersistentGoalPostTurn = config.runPersistentGoalPostTurn;
	}
	/**
	* Build the typing indicator controller for an inbound message. Returns
	* `null` for the CLI channel (no typing UI). Caller is responsible for
	* `start()` and `stop()` (`stop()` should run AFTER the final outbound so
	* Telegram/Weixin see `typing_off` only once the message is delivered).
	*/
	createTypingControllerForInbound(msg) {
		if (msg.channel === "cli") return null;
		const meta = msg.metadata;
		return createTypingController({
			intervalSeconds: 5,
			onStart: async () => {
				await this.bus.publishOutbound({
					channel: msg.channel,
					chat_id: msg.chat_id,
					content: "",
					type: "typing_on",
					metadata: {
						accountId: meta?.accountId,
						threadId: meta?.threadId,
						sessionWebhook: meta?.sessionWebhook,
						conversationId: meta?.conversationId
					}
				});
			},
			onStop: async () => {
				await this.bus.publishOutbound({
					channel: msg.channel,
					chat_id: msg.chat_id,
					content: "",
					type: "typing_off",
					metadata: {
						accountId: meta?.accountId,
						threadId: meta?.threadId,
						sessionWebhook: meta?.sessionWebhook,
						conversationId: meta?.conversationId
					}
				});
			}
		});
	}
	/**
	* Publish the assistant's visible text as the final bus message. Honours the
	* stream-manager "channel already streamed the final text" hint, the heartbeat
	* silence guard, and the extension `message_sending` hook.
	*/
	async sendFinalResponse(msg, sessionContext) {
		if (this.streamManager.consumeSkipFinalOutbound()) return;
		const finalContent = this.getLastAssistantPlainText(sessionContext.sessionKey);
		if (!finalContent?.trim()) return;
		if (shouldSilence(finalContent, this.getConfig()?.gateway?.heartbeat?.ackMaxChars ?? 300) || finalContent.trim() === "NO_REPLY") {
			log.debug({ sessionKey: sessionContext.sessionKey }, "Silent reply — skipping outbound");
			return;
		}
		const hookResult = await this.hookHandler.runMessageSending(sessionContext.chatId, finalContent, sessionContext.channel);
		if (!hookResult.send) return;
		await this.bus.publishOutbound({
			channel: sessionContext.channel,
			chat_id: sessionContext.chatId,
			content: hookResult.content || finalContent,
			type: "message",
			metadata: {
				accountId: msg.metadata?.accountId,
				threadId: msg.metadata?.threadId,
				transcribedVoice: sessionContext.metadata?.transcribedVoice,
				sessionWebhook: msg.metadata?.sessionWebhook,
				conversationId: msg.metadata?.conversationId
			}
		});
	}
	/**
	* Post-turn hook + `/goal` checklist evaluation. Wired into both the bus-driven
	* inbound finally block and the webchat direct-stream finally. The goal verdict
	* itself lives in `PersistentGoalService`; we just trigger the extension hook
	* here and delegate the `/goal` work.
	*/
	async emitSessionTurnComplete(payload) {
		await this.hookHandler.triggerWithSessionKey(payload.sessionKey, "webchat_turn_complete", {
			sessionKey: payload.sessionKey,
			channel: payload.channel,
			chatId: payload.chatId,
			inboundUserText: payload.inboundUserText,
			assistantPlainText: payload.assistantPlainText,
			aborted: payload.aborted,
			...payload.streamError !== void 0 ? { streamError: payload.streamError } : {}
		});
		await this.runPersistentGoalPostTurn(payload);
	}
	/** Extension hook pass-through (Gateway ChannelManager). */
	invokeOutboundMessageSending(to, content, channel) {
		return this.hookHandler.runMessageSending(to, content, channel);
	}
	/** Extension hook pass-through (Gateway ChannelManager). */
	invokeOutboundMessageSent(to, content, success, error, channel) {
		return this.hookHandler.runMessageSent(to, content, success, error, channel);
	}
};
//#endregion
export { OutboundCoordinator };

//# sourceMappingURL=outbound-coordinator.js.map