import { c as normalizeOptionalString } from "./string-coerce-DW4mBlAt.js";
import { r as truncateUtf16Safe } from "./utf16-slice-lH-m0h6-.js";
import "./agent-scope-uF6sww3K.js";
import { r as resolveAgentConfig } from "./agent-scope-config-BGhyaQxC.js";
import { pt as loadSessionEntry, x as appendTranscriptEvent } from "./session-accessor-DqjaXZhO.js";
import { l as resolveStorePath } from "./paths-CPV1Wjwt.js";
import { r as readSessionUpdatedAt } from "./store-BWa9kagF.js";
import { r as isControlCommandMessage } from "./command-detection-DeiERBOY.js";
import "./sessions-D4rOtzjL.js";
import "./mentions-CH7-HW19.js";
import { a as resolveEnvelopeFormatOptions } from "./envelope-BbS7Y-iE.js";
import { n as resolveInboundDebounceMs, t as createInboundDebouncer } from "./inbound-debounce--XfWeOIJ.js";
import { r as dispatchChannelInboundTurn } from "./kernel-FrSJWC7d.js";
import { r as filterChannelInboundSupplementalContext, t as buildChannelInboundEventContext } from "./context-Chu5Lu77.js";
import "./inbound-reply-dispatch-BTV6UBC4.js";
import { t as createChannelInboundEnvelopeBuilder } from "./envelope-BtG0YVnA.js";
import "./direct-dm-NEQZSeUA.js";
//#region src/channels/inbound-debounce-policy.ts
/**
* Channel inbound debounce policy.
*
* Decides when text events can be delayed/merged before agent dispatch.
*/
/** Returns true when an inbound text event is safe to debounce before dispatch. */
function shouldDebounceTextInbound(params) {
	if (params.allowDebounce === false) return false;
	if (params.hasMedia) return false;
	const text = normalizeOptionalString(params.text) ?? "";
	if (!text) return false;
	return !isControlCommandMessage(text, params.cfg, params.commandOptions);
}
/** Creates a channel-scoped inbound debouncer using config/default debounce timing. */
function createChannelInboundDebouncer(params) {
	const debounceMs = resolveInboundDebounceMs({
		cfg: params.cfg,
		channel: params.channel,
		overrideMs: params.debounceMsOverride
	});
	const { cfg: _cfg, channel: _channel, debounceMsOverride: _override, ...rest } = params;
	return {
		debounceMs,
		debouncer: createInboundDebouncer({
			debounceMs,
			...rest
		})
	};
}
//#endregion
//#region src/channels/session-envelope.ts
/** Resolves envelope options and previous timestamp for one inbound channel session. */
function resolveInboundSessionEnvelopeContext(params) {
	const storePath = resolveStorePath(params.cfg.session?.store, { agentId: params.agentId });
	return {
		storePath,
		envelopeOptions: resolveEnvelopeFormatOptions(params.cfg),
		previousTimestamp: readSessionUpdatedAt({
			storePath,
			sessionKey: params.sessionKey
		})
	};
}
//#endregion
//#region src/channels/inbound-event/classification.ts
/**
* Channel inbound event classifier.
*
* Decides whether group/channel activity should wake the agent or remain a passive room event.
*/
/**
* Classifies an inbound channel event as an actionable request or passive room event.
*/
function classifyChannelInboundEvent(params) {
	if (params.unmentionedGroupPolicy !== "room_event") return "user_request";
	if (params.conversation.kind !== "group" && params.conversation.kind !== "channel") return "user_request";
	if (params.wasMentioned === true || params.hasControlCommand === true || params.hasAbortRequest === true || params.commandSource === "native") return "user_request";
	return "room_event";
}
/**
* Resolves the configured policy for unmentioned group/channel inbound events.
*/
function resolveUnmentionedGroupInboundPolicy(params) {
	const agentGroupChat = params.agentId ? resolveAgentConfig(params.cfg, params.agentId)?.groupChat : void 0;
	if (agentGroupChat && Object.hasOwn(agentGroupChat, "unmentionedInbound")) return agentGroupChat.unmentionedInbound ?? "user_request";
	return params.cfg.messages?.groupChat?.unmentionedInbound ?? "user_request";
}
//#endregion
//#region src/channels/feedback-reflection.ts
const DEFAULT_CHANNEL_FEEDBACK_REFLECTION_COOLDOWN_MS = 3e5;
const MAX_RESPONSE_CHARS = 500;
const MAX_COOLDOWN_ENTRIES = 500;
const lastReflectionBySession = /* @__PURE__ */ new Map();
async function recordChannelFeedbackEvent(params) {
	const storePath = resolveStorePath(params.cfg.session?.store, { agentId: params.agentId });
	const entry = loadSessionEntry({
		agentId: params.agentId,
		sessionKey: params.sessionKey,
		storePath
	});
	if (!entry?.sessionId) return false;
	await appendTranscriptEvent({
		agentId: params.agentId,
		sessionId: entry.sessionId,
		sessionKey: params.sessionKey,
		storePath
	}, params.event);
	return true;
}
function buildReflectionPrompt(params) {
	const response = params.thumbedDownResponse;
	const truncated = response && response.length > MAX_RESPONSE_CHARS ? `${truncateUtf16Safe(response, MAX_RESPONSE_CHARS)}...` : response;
	return [
		"A user indicated your previous response wasn't helpful.",
		truncated ? `\nYour response was:\n> ${truncated}` : void 0,
		params.userComment ? `\nUser's comment: "${params.userComment}"` : void 0,
		"\nBriefly reflect: what could you improve? Consider tone, length, accuracy, relevance, and specificity. Reply with one JSON object only: {\"learning\":\"...\",\"followUp\":false,\"userMessage\":\"\"}. Keep learning to 1-2 sentences. Set followUp only when the user needs a direct reply."
	].filter(Boolean).join("\n");
}
function parseReflectionResponse(text) {
	const trimmed = text.trim();
	const candidates = [trimmed, ...trimmed.match(/```(?:json)?\s*([\s\S]*?)```/i)?.slice(1, 2) ?? []];
	for (const candidate of candidates) try {
		const value = JSON.parse(candidate.trim());
		const learning = typeof value.learning === "string" ? value.learning.trim() : "";
		if (!learning) continue;
		return {
			learning,
			followUp: value.followUp === true || typeof value.followUp === "string" && ["true", "yes"].includes(value.followUp.trim().toLowerCase()),
			userMessage: (typeof value.userMessage === "string" ? value.userMessage.trim() : "") || void 0
		};
	} catch {}
	return trimmed ? {
		learning: trimmed,
		followUp: false
	} : null;
}
async function runChannelFeedbackReflection(params) {
	const cooldownMs = params.cooldownMs ?? 3e5;
	if (Date.now() - (lastReflectionBySession.get(params.sessionKey) ?? Number.NEGATIVE_INFINITY) < cooldownMs) return { status: "cooldown" };
	const prompt = buildReflectionPrompt(params);
	const timestamp = Date.now();
	const body = createChannelInboundEnvelopeBuilder({
		cfg: params.cfg,
		route: {
			agentId: params.agentId,
			sessionKey: params.sessionKey
		}
	})({
		channel: params.channelLabel,
		from: "system",
		body: prompt,
		timestamp
	});
	const target = `conversation:${params.conversationId}`;
	const ctxPayload = buildChannelInboundEventContext({
		channel: params.channel,
		accountId: params.accountId,
		messageId: `feedback-reflection:${timestamp}`,
		timestamp,
		from: `${params.channel}:system:${params.conversationId}`,
		sender: {
			id: "system",
			name: "system"
		},
		conversation: {
			kind: params.conversationKind,
			id: params.conversationId
		},
		route: {
			agentId: params.agentId,
			accountId: params.accountId,
			routeSessionKey: params.sessionKey,
			dispatchSessionKey: params.sessionKey
		},
		reply: {
			to: target,
			originatingTo: target
		},
		message: {
			body,
			bodyForAgent: prompt,
			rawBody: prompt,
			commandBody: prompt
		},
		access: { commands: { authorized: false } }
	});
	const responses = [];
	await dispatchChannelInboundTurn({
		cfg: params.cfg,
		channel: params.channel,
		accountId: params.accountId,
		route: {
			agentId: params.agentId,
			sessionKey: params.sessionKey
		},
		ctxPayload,
		record: { onRecordError: params.onRecordError },
		delivery: {
			deliver: async (payload) => {
				if (payload.text) responses.push(payload.text);
				return { visibleReplySent: false };
			},
			onError: (error) => params.onDispatchError?.(error)
		},
		replyPipeline: {}
	});
	const response = responses.join("\n");
	const parsed = parseReflectionResponse(response);
	if (!parsed) return { status: "empty" };
	lastReflectionBySession.set(params.sessionKey, Date.now());
	if (lastReflectionBySession.size > MAX_COOLDOWN_ENTRIES) {
		for (const [key, time] of lastReflectionBySession) if (Date.now() - time >= cooldownMs) lastReflectionBySession.delete(key);
	}
	return {
		status: "complete",
		learning: parsed.learning,
		storePath: resolveStorePath(params.cfg.session?.store, { agentId: params.agentId }),
		followUp: parsed.followUp,
		userMessage: parsed.userMessage,
		responseLength: response.trim().length
	};
}
//#endregion
//#region src/plugin-sdk/channel-inbound.ts
/**
* Builds inbound-event context for callers still passing `inboundTurnKind`.
*
* @deprecated Use `buildChannelInboundEventContext`.
*/
function buildChannelTurnContext(params) {
	const inboundEventKind = params.message.inboundEventKind ?? params.message.inboundTurnKind;
	const ctx = buildChannelInboundEventContext({
		...params,
		message: {
			...params.message,
			...inboundEventKind ? { inboundEventKind } : {}
		}
	});
	return {
		...ctx,
		InboundTurnKind: ctx.InboundEventKind
	};
}
/**
* Deprecated supplemental-context filter alias retained for channel SDK compatibility.
*
* @deprecated Use `filterChannelInboundSupplementalContext`.
*/
const filterChannelTurnSupplementalContext = filterChannelInboundSupplementalContext;
//#endregion
export { runChannelFeedbackReflection as a, resolveInboundSessionEnvelopeContext as c, recordChannelFeedbackEvent as i, createChannelInboundDebouncer as l, filterChannelTurnSupplementalContext as n, classifyChannelInboundEvent as o, DEFAULT_CHANNEL_FEEDBACK_REFLECTION_COOLDOWN_MS as r, resolveUnmentionedGroupInboundPolicy as s, buildChannelTurnContext as t, shouldDebounceTextInbound as u };
