import { c as normalizeOptionalString } from "./string-coerce-Bje8XVt9.js";
import { n as resolveOutboundDurableFinalDeliverySupport } from "./deliver-Jx7sOKHG.js";
import { t as normalizeDeliverableOutboundChannel } from "./channel-resolution-D2Puf2sy.js";
import { t as deriveDurableFinalDeliveryRequirements } from "./capabilities-CV5E_iV8.js";
import { t as buildOutboundSessionContext } from "./session-context-CmK1ipwA.js";
import { t as finalizeInboundContext } from "./inbound-context-Drl61RGg.js";
import { c as clearHistoryEntriesIfEnabled } from "./history-iNmZZd6M.js";
import { t as createChannelReplyPipeline } from "./reply-pipeline-U86TeKHV.js";
import { t as EMPTY_CHANNEL_TURN_DISPATCH_COUNTS } from "./dispatch-result-CLEOmcpN.js";
import { n as listMessageReceiptPlatformIds } from "./receipt-DJZeaN3q.js";
import { t as sendDurableMessageBatch } from "./send-CG-D55pM.js";
import { r as shouldIncludeSupplementalContext } from "./context-visibility-BHnaJUyI.js";
//#region src/channels/turn/delivery-result.ts
function createChannelDeliveryResultFromReceipt(params) {
	const messageIds = listMessageReceiptPlatformIds(params.receipt);
	return {
		...messageIds.length > 0 ? { messageIds } : {},
		receipt: params.receipt,
		...params.threadId ? { threadId: params.threadId } : {},
		...params.replyToId ? { replyToId: params.replyToId } : {},
		...params.visibleReplySent === void 0 ? {} : { visibleReplySent: params.visibleReplySent },
		...params.deliveryIntent ? { deliveryIntent: params.deliveryIntent } : {}
	};
}
//#endregion
//#region src/channels/turn/durable-delivery.ts
function resolveDeliveryTarget(params) {
	return normalizeOptionalString(params.to) ?? normalizeOptionalString(params.ctxPayload.OriginatingTo) ?? normalizeOptionalString(params.ctxPayload.To);
}
function resolveDurableInboundReplyToId(params) {
	if (params.replyToId === null || params.payload.replyToId === null) return null;
	return normalizeOptionalString(params.replyToId) ?? normalizeOptionalString(params.payload.replyToId) ?? normalizeOptionalString(params.ctxPayload.ReplyToIdFull) ?? normalizeOptionalString(params.ctxPayload.ReplyToId);
}
function resolveDurableInboundReplyThreadId(params) {
	if ("threadId" in params) return params.threadId;
	return params.ctxPayload.MessageThreadId;
}
function stringifyThreadId(value) {
	return value == null ? void 0 : String(value);
}
function toDeliveryIntent(intent) {
	return {
		id: intent.id,
		kind: "outbound_queue",
		queuePolicy: intent.queuePolicy
	};
}
function isDurableInboundReplyDeliveryHandled(result) {
	return result.status === "handled_visible" || result.status === "handled_no_send";
}
function throwIfDurableInboundReplyDeliveryFailed(result) {
	if (result.status === "failed") throw result.error;
}
async function deliverInboundReplyWithMessageSendContext(params) {
	if (params.info.kind !== "final") return {
		status: "not_applicable",
		reason: "non_final"
	};
	const channel = normalizeDeliverableOutboundChannel(params.channel);
	const to = resolveDeliveryTarget(params);
	if (!channel) return {
		status: "unsupported",
		reason: "missing_channel"
	};
	if (!to) return {
		status: "unsupported",
		reason: "missing_target"
	};
	const replyToId = resolveDurableInboundReplyToId(params);
	const threadId = resolveDurableInboundReplyThreadId(params);
	const requiredCapabilities = params.requiredCapabilities ?? deriveDurableFinalDeliveryRequirements({
		payload: params.payload,
		replyToId,
		threadId,
		silent: params.silent
	});
	const durability = requiredCapabilities.reconcileUnknownSend === true ? "required" : "best_effort";
	let support;
	try {
		support = await resolveOutboundDurableFinalDeliverySupport({
			cfg: params.cfg,
			channel,
			requirements: requiredCapabilities
		});
	} catch (err) {
		return {
			status: "failed",
			error: err
		};
	}
	if (!support.ok) return {
		status: "unsupported",
		reason: support.reason,
		...support.capability ? { capability: support.capability } : {}
	};
	const session = buildOutboundSessionContext({
		cfg: params.cfg,
		sessionKey: params.ctxPayload.SessionKey,
		policySessionKey: params.ctxPayload.RuntimePolicySessionKey,
		conversationType: params.ctxPayload.ChatType,
		agentId: params.agentId,
		requesterAccountId: params.accountId ?? params.ctxPayload.AccountId,
		requesterSenderId: params.ctxPayload.SenderId ?? params.ctxPayload.From,
		requesterSenderName: params.ctxPayload.SenderName,
		requesterSenderUsername: params.ctxPayload.SenderUsername,
		requesterSenderE164: params.ctxPayload.SenderE164
	});
	const send = await sendDurableMessageBatch({
		cfg: params.cfg,
		channel,
		to,
		accountId: params.accountId,
		payloads: [params.payload],
		threadId,
		replyToId,
		replyToMode: params.replyToMode,
		formatting: params.formatting,
		identity: params.identity,
		deps: params.deps,
		mediaAccess: params.mediaAccess,
		silent: params.silent,
		durability,
		session,
		gatewayClientScopes: params.ctxPayload.GatewayClientScopes
	});
	if (send.status === "failed") return {
		status: "failed",
		error: send.error
	};
	const delivery = createChannelDeliveryResultFromReceipt({
		receipt: send.receipt,
		threadId: stringifyThreadId(threadId),
		...replyToId ? { replyToId } : {},
		visibleReplySent: send.status === "sent",
		...send.deliveryIntent ? { deliveryIntent: toDeliveryIntent(send.deliveryIntent) } : {}
	});
	if (send.status === "suppressed") return {
		status: "handled_no_send",
		reason: "no_visible_result",
		delivery
	};
	return {
		status: "handled_visible",
		delivery
	};
}
/** @deprecated Use `deliverInboundReplyWithMessageSendContext`. */
const deliverDurableInboundReplyPayload = deliverInboundReplyWithMessageSendContext;
//#endregion
//#region src/channels/turn/context.ts
function compactStrings(values) {
	const compacted = values.filter((value) => Boolean(value));
	return compacted.length > 0 ? compacted : void 0;
}
function mediaTranscribedIndexes(media) {
	const indexes = media.map((item, index) => item.transcribed ? index : void 0).filter((index) => index !== void 0);
	return indexes.length > 0 ? indexes : void 0;
}
function commandAuthorized(access) {
	const commands = access?.commands;
	if (!commands) return;
	return commands.authorizers.some((entry) => entry.allowed);
}
function keepSupplementalContext(params) {
	if (!params.mode || params.mode === "all") return true;
	if (params.senderAllowed === void 0) return false;
	return shouldIncludeSupplementalContext({
		mode: params.mode,
		kind: params.kind,
		senderAllowed: params.senderAllowed
	});
}
function filterChannelTurnSupplementalContext(params) {
	const supplemental = params.supplemental;
	if (!supplemental) return;
	const quote = keepSupplementalContext({
		mode: params.contextVisibility,
		kind: "quote",
		senderAllowed: supplemental.quote?.senderAllowed
	}) ? supplemental.quote : void 0;
	const forwarded = keepSupplementalContext({
		mode: params.contextVisibility,
		kind: "forwarded",
		senderAllowed: supplemental.forwarded?.senderAllowed
	}) ? supplemental.forwarded : void 0;
	const thread = keepSupplementalContext({
		mode: params.contextVisibility,
		kind: "thread",
		senderAllowed: supplemental.thread?.senderAllowed
	}) ? supplemental.thread : void 0;
	return {
		...supplemental,
		quote,
		forwarded,
		thread
	};
}
function buildChannelTurnContext(params) {
	const media = params.media ?? [];
	const supplemental = filterChannelTurnSupplementalContext({
		supplemental: params.supplemental,
		contextVisibility: params.contextVisibility
	});
	return finalizeInboundContext({
		Body: params.message.body ?? params.message.rawBody,
		BodyForAgent: params.message.bodyForAgent ?? params.message.rawBody,
		InboundHistory: params.message.inboundHistory,
		RawBody: params.message.rawBody,
		CommandBody: params.message.commandBody ?? params.message.rawBody,
		BodyForCommands: params.message.commandBody ?? params.message.rawBody,
		From: params.from,
		To: params.reply.to,
		SessionKey: params.route.dispatchSessionKey ?? params.route.routeSessionKey,
		AccountId: params.route.accountId ?? params.accountId,
		ParentSessionKey: params.route.parentSessionKey,
		ModelParentSessionKey: params.route.modelParentSessionKey,
		MessageSid: params.messageId,
		MessageSidFull: params.messageIdFull,
		ReplyToId: params.reply.replyToId ?? supplemental?.quote?.id,
		ReplyToIdFull: params.reply.replyToIdFull ?? supplemental?.quote?.fullId,
		ReplyToBody: supplemental?.quote?.body,
		ReplyToSender: supplemental?.quote?.sender,
		ReplyToIsQuote: supplemental?.quote?.isQuote,
		ForwardedFrom: supplemental?.forwarded?.from,
		ForwardedFromType: supplemental?.forwarded?.fromType,
		ForwardedFromId: supplemental?.forwarded?.fromId,
		ForwardedDate: supplemental?.forwarded?.date,
		ThreadStarterBody: supplemental?.thread?.starterBody,
		ThreadHistoryBody: supplemental?.thread?.historyBody,
		ThreadLabel: supplemental?.thread?.label,
		MediaPath: media[0]?.path,
		MediaUrl: media[0]?.url ?? media[0]?.path,
		MediaType: media[0]?.contentType ?? media[0]?.kind,
		MediaPaths: compactStrings(media.map((item) => item.path)),
		MediaUrls: compactStrings(media.map((item) => item.url ?? item.path)),
		MediaTypes: compactStrings(media.map((item) => item.contentType ?? item.kind)),
		MediaTranscribedIndexes: mediaTranscribedIndexes(media),
		ChatType: params.conversation.kind,
		ConversationLabel: params.conversation.label,
		GroupSubject: params.conversation.kind !== "direct" ? params.conversation.label : void 0,
		GroupSpace: params.conversation.spaceId,
		GroupSystemPrompt: supplemental?.groupSystemPrompt,
		UntrustedStructuredContext: Array.isArray(supplemental?.untrustedContext) ? supplemental.untrustedContext.map((payload, index) => ({
			label: `context ${index + 1}`,
			payload
		})) : void 0,
		SenderName: params.sender.name ?? params.sender.displayLabel,
		SenderId: params.sender.id,
		SenderUsername: params.sender.username,
		SenderTag: params.sender.tag,
		MemberRoleIds: params.sender.roles,
		Timestamp: params.timestamp,
		Provider: params.provider ?? params.channel,
		Surface: params.surface ?? params.provider ?? params.channel,
		WasMentioned: params.access?.mentions?.wasMentioned,
		CommandAuthorized: commandAuthorized(params.access),
		MessageThreadId: params.reply.messageThreadId ?? params.conversation.threadId,
		NativeChannelId: params.reply.nativeChannelId ?? params.conversation.nativeChannelId,
		OriginatingChannel: params.channel,
		OriginatingTo: params.reply.originatingTo,
		ThreadParentId: params.reply.threadParentId ?? params.conversation.parentId,
		...params.extra
	});
}
//#endregion
//#region src/channels/turn/kernel.ts
const DEFAULT_EVENT_CLASS = {
	kind: "message",
	canStartAgentTurn: true
};
/**
* @deprecated Compatibility assembly for legacy buffered reply dispatchers.
* New channel plugins should expose `defineChannelMessageAdapter(...)` from
* `openclaw/plugin-sdk/channel-message` and route send/receive behavior through
* the message lifecycle helpers.
*/
function createChannelTurnReplyPipeline(params) {
	return createChannelReplyPipeline(params);
}
function isAdmission(value) {
	if (!value || typeof value !== "object") return false;
	const kind = value.kind;
	return kind === "dispatch" || kind === "observeOnly" || kind === "handled" || kind === "drop";
}
function normalizePreflight(value) {
	if (!value) return {};
	if (isAdmission(value)) return { admission: value };
	return value;
}
function emit(params) {
	params.log?.({
		channel: params.channel,
		accountId: params.accountId,
		...params.event
	});
}
function createNoopChannelTurnDeliveryAdapter() {
	return { deliver: async () => ({ visibleReplySent: false }) };
}
function clearPendingHistoryAfterTurn(params) {
	if (!params?.isGroup || !params.historyKey || !params.historyMap || params.limit === void 0) return;
	clearHistoryEntriesIfEnabled({
		historyMap: params.historyMap,
		historyKey: params.historyKey,
		limit: params.limit
	});
}
function resolveAssembledReplyPipeline(params) {
	if (!params.replyPipeline) return {
		dispatcherOptions: params.dispatcherOptions,
		replyOptions: params.replyOptions
	};
	const { onModelSelected, ...replyPipeline } = createChannelReplyPipeline({
		cfg: params.cfg,
		agentId: params.agentId,
		channel: params.channel,
		accountId: params.accountId,
		...params.replyPipeline
	});
	return {
		dispatcherOptions: {
			...replyPipeline,
			...params.dispatcherOptions
		},
		replyOptions: {
			onModelSelected,
			...params.replyOptions
		}
	};
}
function resolveObserveOnlyDispatchResult(params) {
	return params.observeOnlyDispatchResult ?? {
		queuedFinal: false,
		counts: EMPTY_CHANNEL_TURN_DISPATCH_COUNTS
	};
}
async function dispatchAssembledChannelTurn(params) {
	const replyPipeline = resolveAssembledReplyPipeline(params);
	return await runPreparedChannelTurnCore({
		channel: params.channel,
		accountId: params.accountId,
		routeSessionKey: params.routeSessionKey,
		storePath: params.storePath,
		ctxPayload: params.ctxPayload,
		recordInboundSession: params.recordInboundSession,
		record: params.record,
		history: params.history,
		admission: params.admission,
		log: params.log,
		messageId: params.messageId,
		runDispatch: async () => await params.dispatchReplyWithBufferedBlockDispatcher({
			ctx: params.ctxPayload,
			cfg: params.cfg,
			dispatcherOptions: {
				...replyPipeline.dispatcherOptions,
				deliver: async (payload, info) => {
					const preparedPayload = params.delivery.preparePayload ? await params.delivery.preparePayload(payload, info) : payload;
					const durableOptions = typeof params.delivery.durable === "function" ? await params.delivery.durable(preparedPayload, info) : params.delivery.durable;
					if (durableOptions) {
						const durable = await deliverInboundReplyWithMessageSendContext({
							cfg: params.cfg,
							channel: params.channel,
							accountId: params.accountId,
							agentId: params.agentId,
							ctxPayload: params.ctxPayload,
							payload: preparedPayload,
							info,
							...durableOptions
						});
						throwIfDurableInboundReplyDeliveryFailed(durable);
						if (isDurableInboundReplyDeliveryHandled(durable)) {
							await params.delivery.onDelivered?.(preparedPayload, info, durable.delivery);
							return durable.delivery;
						}
					}
					const result = await params.delivery.deliver(preparedPayload, info);
					await params.delivery.onDelivered?.(preparedPayload, info, result);
					return result;
				},
				onError: params.delivery.onError
			},
			replyOptions: replyPipeline.replyOptions,
			replyResolver: params.replyResolver
		})
	}, { suppressObserveOnlyDispatch: false });
}
function isPreparedChannelTurn(value) {
	return "runDispatch" in value;
}
async function dispatchResolvedChannelTurn(params) {
	if (isPreparedChannelTurn(params)) return await runPreparedChannelTurn(params);
	return await dispatchAssembledChannelTurn(params);
}
async function runPreparedChannelTurnCore(params, options) {
	const admission = params.admission ?? { kind: "dispatch" };
	emit({
		...params,
		event: {
			stage: "record",
			event: "start",
			messageId: params.messageId,
			sessionKey: params.ctxPayload.SessionKey ?? params.routeSessionKey,
			admission: admission.kind
		}
	});
	try {
		await params.recordInboundSession({
			storePath: params.storePath,
			sessionKey: params.ctxPayload.SessionKey ?? params.routeSessionKey,
			ctx: params.ctxPayload,
			groupResolution: params.record?.groupResolution,
			createIfMissing: params.record?.createIfMissing,
			updateLastRoute: params.record?.updateLastRoute,
			onRecordError: params.record?.onRecordError ?? (() => void 0),
			trackSessionMetaTask: params.record?.trackSessionMetaTask
		});
		emit({
			...params,
			event: {
				stage: "record",
				event: "done",
				messageId: params.messageId,
				sessionKey: params.ctxPayload.SessionKey ?? params.routeSessionKey,
				admission: admission.kind
			}
		});
	} catch (err) {
		emit({
			...params,
			event: {
				stage: "record",
				event: "error",
				messageId: params.messageId,
				sessionKey: params.ctxPayload.SessionKey ?? params.routeSessionKey,
				admission: admission.kind,
				error: err
			}
		});
		try {
			await params.onPreDispatchFailure?.(err);
		} catch {}
		throw err;
	}
	emit({
		...params,
		event: {
			stage: "dispatch",
			event: "start",
			messageId: params.messageId,
			sessionKey: params.ctxPayload.SessionKey ?? params.routeSessionKey,
			admission: admission.kind
		}
	});
	let dispatchResult;
	try {
		dispatchResult = options.suppressObserveOnlyDispatch && admission.kind === "observeOnly" ? resolveObserveOnlyDispatchResult(params) : await params.runDispatch();
	} catch (err) {
		emit({
			...params,
			event: {
				stage: "dispatch",
				event: "error",
				messageId: params.messageId,
				sessionKey: params.ctxPayload.SessionKey ?? params.routeSessionKey,
				admission: admission.kind,
				error: err
			}
		});
		throw err;
	}
	emit({
		...params,
		event: {
			stage: "dispatch",
			event: "done",
			messageId: params.messageId,
			sessionKey: params.ctxPayload.SessionKey ?? params.routeSessionKey,
			admission: admission.kind
		}
	});
	clearPendingHistoryAfterTurn(params.history);
	return {
		admission,
		dispatched: true,
		ctxPayload: params.ctxPayload,
		routeSessionKey: params.routeSessionKey,
		dispatchResult
	};
}
async function runPreparedChannelTurn(params) {
	return await runPreparedChannelTurnCore(params, { suppressObserveOnlyDispatch: true });
}
async function runChannelTurn(params) {
	emit({
		...params,
		event: {
			stage: "ingest",
			event: "start"
		}
	});
	const input = await params.adapter.ingest(params.raw);
	if (!input) {
		const admission = {
			kind: "drop",
			reason: "ingest-null"
		};
		emit({
			...params,
			event: {
				stage: "ingest",
				event: "drop",
				admission: admission.kind,
				reason: admission.reason
			}
		});
		return {
			admission,
			dispatched: false
		};
	}
	emit({
		...params,
		event: {
			stage: "ingest",
			event: "done",
			messageId: input.id
		}
	});
	const eventClass = await params.adapter.classify?.(input) ?? DEFAULT_EVENT_CLASS;
	if (!eventClass.canStartAgentTurn) {
		const admission = {
			kind: "handled",
			reason: `event:${eventClass.kind}`
		};
		emit({
			...params,
			event: {
				stage: "classify",
				event: "handled",
				messageId: input.id,
				admission: admission.kind,
				reason: admission.reason
			}
		});
		return {
			admission,
			dispatched: false
		};
	}
	const preflight = normalizePreflight(await params.adapter.preflight?.(input, eventClass));
	const preflightAdmission = preflight.admission;
	if (preflightAdmission && preflightAdmission.kind !== "dispatch" && preflightAdmission.kind !== "observeOnly") {
		emit({
			...params,
			event: {
				stage: "preflight",
				event: preflightAdmission.kind === "handled" ? "handled" : "drop",
				messageId: input.id,
				admission: preflightAdmission.kind,
				reason: preflightAdmission.reason
			}
		});
		return {
			admission: preflightAdmission,
			dispatched: false
		};
	}
	const resolved = await params.adapter.resolveTurn(input, eventClass, preflight);
	emit({
		...params,
		accountId: resolved.accountId ?? params.accountId,
		event: {
			stage: "assemble",
			event: "done",
			messageId: input.id,
			sessionKey: resolved.routeSessionKey,
			admission: resolved.admission?.kind ?? "dispatch"
		}
	});
	const admission = resolved.admission ?? preflightAdmission ?? { kind: "dispatch" };
	let result;
	try {
		result = {
			...await dispatchResolvedChannelTurn(admission.kind === "observeOnly" ? {
				...resolved,
				delivery: createNoopChannelTurnDeliveryAdapter(),
				admission,
				log: params.log,
				messageId: input.id
			} : {
				...resolved,
				admission,
				log: params.log,
				messageId: input.id
			}),
			admission
		};
	} catch (err) {
		const failedResult = {
			admission,
			dispatched: false,
			ctxPayload: resolved.ctxPayload,
			routeSessionKey: resolved.routeSessionKey
		};
		try {
			await params.adapter.onFinalize?.(failedResult);
		} catch {}
		emit({
			...params,
			accountId: resolved.accountId ?? params.accountId,
			event: {
				stage: "finalize",
				event: "done",
				messageId: input.id,
				sessionKey: resolved.routeSessionKey,
				admission: admission.kind
			}
		});
		throw err;
	}
	try {
		await params.adapter.onFinalize?.(result);
		emit({
			...params,
			accountId: resolved.accountId ?? params.accountId,
			event: {
				stage: "finalize",
				event: "done",
				messageId: input.id,
				sessionKey: resolved.routeSessionKey,
				admission: admission.kind
			}
		});
	} catch (err) {
		emit({
			...params,
			accountId: resolved.accountId ?? params.accountId,
			event: {
				stage: "finalize",
				event: "error",
				messageId: input.id,
				sessionKey: resolved.routeSessionKey,
				admission: admission.kind,
				error: err
			}
		});
		throw err;
	}
	return result;
}
async function runResolvedChannelTurn(params) {
	return await runChannelTurn({
		channel: params.channel,
		accountId: params.accountId,
		raw: params.raw,
		log: params.log,
		adapter: {
			ingest: (raw) => typeof params.input === "function" ? params.input(raw) : params.input,
			resolveTurn: params.resolveTurn
		}
	});
}
//#endregion
export { runPreparedChannelTurn as a, filterChannelTurnSupplementalContext as c, isDurableInboundReplyDeliveryHandled as d, throwIfDurableInboundReplyDeliveryFailed as f, runChannelTurn as i, deliverDurableInboundReplyPayload as l, createNoopChannelTurnDeliveryAdapter as n, runResolvedChannelTurn as o, createChannelDeliveryResultFromReceipt as p, dispatchAssembledChannelTurn as r, buildChannelTurnContext as s, createChannelTurnReplyPipeline as t, deliverInboundReplyWithMessageSendContext as u };
