import { createLogger } from "../../utils/logger/index.js";
import { init_logger } from "../../utils/logger.js";
import { getAllProviders, getModelsByProvider, getProviderDisplayName, init_providers, isProviderConfiguredSync } from "../../providers/index.js";
import { commandRegistry } from "../../chat-commands/registry.js";
import { createCommandContext } from "../../chat-commands/context.js";
import "../../chat-commands/index.js";
//#region src/agent/messaging/command-handler.ts
init_providers();
init_logger();
const log = createLogger("CommandHandler");
/** Gateway console direct stream uses SSE tokens; there is no ChannelPlugin outbound for `webchat`. */
function shouldSkipBusOutboundForChannel(channel) {
	return channel === "webchat";
}
var CommandHandler = class {
	config;
	bus;
	sessionStore;
	sessionConfigStore;
	applySessionThinkingLevel;
	getCurrentModel;
	switchModelForSession;
	invalidateAgentSession;
	abortSessionTurn;
	compactSession;
	btwQuery;
	getSessionContextReport;
	getPersistentGoalApisForCommand;
	resetSession;
	constructor(handlerConfig) {
		this.config = handlerConfig.config;
		this.bus = handlerConfig.bus;
		this.sessionStore = handlerConfig.sessionStore;
		this.sessionConfigStore = handlerConfig.sessionConfigStore;
		this.applySessionThinkingLevel = handlerConfig.applySessionThinkingLevel;
		this.getCurrentModel = handlerConfig.getCurrentModel;
		this.switchModelForSession = handlerConfig.switchModelForSession;
		this.invalidateAgentSession = handlerConfig.invalidateAgentSession;
		this.abortSessionTurn = handlerConfig.abortSessionTurn;
		this.compactSession = handlerConfig.compactSession;
		this.btwQuery = handlerConfig.btwQuery;
		this.getSessionContextReport = handlerConfig.getSessionContextReport;
		this.getPersistentGoalApisForCommand = handlerConfig.getPersistentGoalApisForCommand;
		this.resetSession = handlerConfig.resetSession;
	}
	/** Replace config reference after hot reload or gateway PATCH so commands see current defaults. */
	updateAgentConfig(config) {
		this.config = config;
	}
	/**
	* Build the unified command context shared by all execute paths.
	* When `recorder` is set, every reply text is also captured (for SSE / CLI aggregation).
	*/
	buildCommandContext(context, recorder) {
		const skipBusOutbound = shouldSkipBusOutboundForChannel(context.channel);
		return createCommandContext({
			sessionKey: context.sessionKey,
			source: context.channel,
			channelId: context.channel,
			chatId: context.chatId,
			senderId: context.senderId,
			isGroup: context.isGroup,
			config: this.config,
			bus: this.bus,
			sessionStore: this.sessionStore,
			sessionConfigStore: this.sessionConfigStore,
			applySessionThinkingLevel: this.applySessionThinkingLevel,
			replyHandler: async (text, _options) => {
				recorder?.(text);
				if (skipBusOutbound) return;
				await this.bus.publishOutbound({
					channel: context.channel,
					chat_id: context.chatId,
					content: text,
					type: "message"
				});
			},
			typingHandler: async (typing) => {
				if (skipBusOutbound) return;
				await this.bus.publishOutbound({
					channel: context.channel,
					chat_id: context.chatId,
					type: typing ? "typing_on" : "typing_off"
				});
			},
			supportedFeatures: ["markdown", "typing"],
			getCurrentModel: this.getCurrentModel,
			switchModel: async (modelId) => {
				return this.switchModelForSession(context.sessionKey, modelId);
			},
			listModels: async () => {
				const providers = getAllProviders();
				const models = [];
				for (const providerId of providers) if (isProviderConfiguredSync(providerId)) {
					const providerModels = getModelsByProvider(providerId);
					for (const m of providerModels) models.push({
						id: `${m.provider}/${m.id}`,
						name: m.name || m.id,
						provider: getProviderDisplayName(providerId)
					});
				}
				return models;
			},
			getUsage: async () => {
				const messages = await this.sessionStore.load(context.sessionKey);
				let promptTokens = 0;
				let completionTokens = 0;
				for (const msg of messages) if ("usage" in msg && msg.usage) {
					const usage = msg.usage;
					promptTokens += usage.input || 0;
					completionTokens += usage.output || 0;
				}
				return {
					promptTokens,
					completionTokens,
					totalTokens: promptTokens + completionTokens,
					messageCount: messages.length
				};
			},
			invalidateAgentSession: this.invalidateAgentSession,
			resetSession: this.resetSession ? async (sk) => {
				await this.resetSession(sk);
			} : void 0,
			abortCurrentTurn: this.abortSessionTurn ? async () => {
				await this.abortSessionTurn(context.sessionKey);
			} : void 0,
			compactSession: this.compactSession,
			btwQuery: this.btwQuery,
			getSessionContextReport: this.getSessionContextReport,
			persistentGoalApis: this.getPersistentGoalApisForCommand({
				sessionKey: context.sessionKey,
				channel: context.channel,
				chatId: context.chatId,
				inboundMetadata: context.inboundMetadata
			})
		});
	}
	/**
	* Execute a command using the unified command system
	*/
	async executeCommand(commandName, args, context) {
		if (!commandRegistry.has(commandName)) return false;
		log.info({
			command: commandName,
			sessionKey: context.sessionKey
		}, "Executing command via new system");
		const cmdCtx = this.buildCommandContext(context);
		const result = await commandRegistry.execute(commandName, cmdCtx, args);
		if (result.content && !shouldSkipBusOutboundForChannel(context.channel)) await this.bus.publishOutbound({
			channel: context.channel,
			chat_id: context.chatId,
			content: result.content,
			type: "message"
		});
		return true;
	}
	/**
	* Run command and return all user-visible text (ctx.reply + result.content) for SSE/CLI.
	* Same bus side effects as {@link executeCommand}.
	*/
	async executeCommandAndAggregateReply(commandName, args, context) {
		if (!commandRegistry.has(commandName)) return {
			handled: false,
			aggregatedText: ""
		};
		log.info({
			command: commandName,
			sessionKey: context.sessionKey
		}, "Executing command (aggregate reply)");
		const segments = [];
		const wrapped = this.buildCommandContext(context, (text) => segments.push(text));
		const result = await commandRegistry.execute(commandName, wrapped, args);
		if (result.content) {
			segments.push(result.content);
			if (!shouldSkipBusOutboundForChannel(context.channel)) await this.bus.publishOutbound({
				channel: context.channel,
				chat_id: context.chatId,
				content: result.content,
				type: "message"
			});
		}
		return {
			handled: true,
			aggregatedText: segments.filter((s) => s && s.trim()).join("\n\n")
		};
	}
};
//#endregion
export { CommandHandler };

//# sourceMappingURL=command-handler.js.map