UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

1,481 lines • 69.5 kB
import { c as normalizeOptionalLowercaseString, l as normalizeOptionalString, o as normalizeLowercaseStringOrEmpty } from "./string-coerce-6kR-86XC.mjs";
import { r as truncateUtf16Safe } from "./utf16-slice-D_ngcYKd.mjs";
import { n as createLazyPromise, t as createLazyImportLoader } from "./lazy-promise-DGqyc4Y4.mjs";
import { t as formatErrorMessage } from "./errors-BSdPMFao.mjs";
import { p as isDiagnosticsEnabled } from "./diagnostic-events-BflWr4hm.mjs";
import { a as resolveAgentDir, d as resolveAgentWorkspaceDir } from "./agent-scope-config-BU4qc9bA.mjs";
import { l as resolveAgentIdFromSessionKey } from "./session-key-BC4m_Ly5.mjs";
import { l as resolveSessionStorePathCore } from "./paths-CAzSovN1.mjs";
import { g as resolveSessionAgentId } from "./agent-scope-DWB4x52T.mjs";
import { d as toolPolicyRestrictsTools } from "./tool-policy-C57wduz9.mjs";
import { a as generateSecureUuid } from "./secure-random-BCtWGJkH.mjs";
import { r as logVerbose } from "./globals-D2Pr1KIl.mjs";
import { n as recordRuntimeActionDecision } from "./runtime-action-decision-B2u479yP.mjs";
import { a as copyReplyPayloadMetadata, b as markReplyPayloadAsTtsSupplement, h as isReplyPayloadStatusNotice, v as isReplyPayloadTtsSupplement } from "./reply-payload-DBtQZ6PK.mjs";
import { n as resolveConfiguredTtsMode, s as shouldCleanTtsDirectiveText } from "./tts-config-DUaomMGA.mjs";
import { c as readChannelContextAdmissionEvidence } from "./admission-evidence-DswNfzJ7.mjs";
import { r as prepareChannelParticipantObservation } from "./host-context-builder-108SEu6-.mjs";
import { c as getAdmittedRunDelegatedAuthority, i as closeAdmittedRunDelegatedAuthority } from "./admitted-run-context-CbCp9bj_.mjs";
import { k as createVerifiedConversationContextStreamFilter } from "./user-facing-text-D-rB13dP.mjs";
import { n as classifySessionStateActor } from "./session-state-events-BGkmW_BE.mjs";
import { a as toAcpRuntimeError, n as AcpRuntimeError } from "./errors-DgzXBC-n.mjs";
import { t as formatAcpRuntimeErrorText } from "./error-text-BQmtA3jo.mjs";
import { o as isSessionIdentityPending, u as resolveSessionIdentityFromMeta } from "./session-identity-CVaIbgV4.mjs";
import { i as resolveAcpThreadSessionDetailLines } from "./session-identifiers-BA-B9pR8.mjs";
import "./errors-BwfgFFv3.mjs";
import { n as QuestionAnswerUnconfirmedError, t as PreparedQuestionAnswerRefusedError } from "./gateway-question-dispatch-BWeOPi_b.mjs";
import { r as hasInboundMediaForUnderstanding } from "./inbound-media-DUVrGcUq.mjs";
import { d as markDiagnosticSessionProgress } from "./diagnostic-DRiDG71y.mjs";
import { r as normalizeReplyPayloadOutcome } from "./normalize-reply-XuQItfL2.mjs";
import { g as shouldRetryReplyDispatch, h as resolveRoutedReplyDeliveryOutcome, o as prepareReplyPayloadForDispatcher, r as captureReplyDispatchDeliveryOutcome, s as waitForReplyDispatcherIdle, t as attachReplyDispatchUndeliveredFallback } from "./reply-dispatcher-BmqXN2zF.mjs";
import { a as hasOutboundReplyContent } from "./reply-payload-DPu0E58h.mjs";
import { n as resolveRoutedDeliveryThreadId } from "./routed-delivery-thread-C_JedswS.mjs";
import { n as assertPreparedConversationBindingRouteCurrent } from "./session-conversation-binding-BwjBYTbJ.mjs";
import { a as isCaptionedFinalTextPayload, i as cleanDeferredFinalText, n as needsTtsFallback, o as mergeDeferredFinalText, r as buildCaptionedFinalTextFallback, s as shouldDeferFinalTtsText } from "./dispatch-from-config.finalize-CsJLJ2Jj.mjs";
import { t as createTtsDirectiveTextStreamCleaner } from "./directives-3iKZ4j4C.mjs";
import { t as resolveStatusTtsSnapshot } from "./status-config-CKklK1Su.mjs";
import { _ as hasBlockReplyDeliveryCustody, h as createBlockReplySource, m as createBlockReplyPipeline, y as setBlockReplyDelivery } from "./dispatch-from-config.payloads-1RlO-_zV.mjs";
import { i as claimPreparedPendingAgentQuestionAnswer } from "./gateway-question-BNvMdOLy.mjs";
import { n as formatToolSummary, o as resolveToolDisplay } from "./tool-display-DLY_VyQb.mjs";
import { z as truncateUtf16WithEllipsis } from "./streaming-Cxwv6IM7.mjs";
import { i as buildAgentRunTerminalOutcomeFromLifecycleEvent } from "./agent-run-terminal-outcome-Cos6IjJ5.mjs";
import { a as resolveReplyDeliveryAccountId, o as resolveReplyToMode, t as createReplyDeliveryContext } from "./reply-threading-DFqTQgY8.mjs";
import { r as createChannelReplyTransform } from "./reply-transform-Cog0xfyl.mjs";
import { r as getGatewayLocalUserIngress } from "./local-user-ingress-DFM7fYdS.mjs";
import { n as resolveAcpAgentPolicyError, r as resolveAcpDispatchPolicyError } from "./policy-BmNqd5mz.mjs";
import { r as prefixSystemMessage } from "./system-message-C34aMvlW.mjs";
import { t as EmbeddedBlockChunker } from "./embedded-agent-block-chunker-CZM6S5oy.mjs";
import { t as stripExtractedFileImageMetadata } from "./extracted-file-images-CdmNdoIK.mjs";
import { a as appendRecentHistoryImageContext, i as resolveInlineAgentImageAttachments, n as loadAgentTurnMediaRuntime, r as resolveAgentTurnAttachments, t as collectDescribedImageAttachmentIndexes } from "./agent-turn-attachments-B0qu940D.mjs";
import { t as recordAcceptedSessionParticipantInput } from "./session-participant-input-recording-DGnI1Z-E.mjs";
import { t as createLazyAcpElicitationHandler } from "./acp-elicitation-handler-lazy-ByiT-JxC.mjs";
import { t as resolveAcpToolTerminalOutcome } from "./tool-status-CzNYEZFN.mjs";
import { n as resolveAcpProjectionSettings, r as resolveAcpStreamingConfig, t as isAcpTagVisible } from "./acp-stream-settings-Ce8ojoES.mjs";
import { t as prepareChannelRunAdmission } from "./channel-run-admission-BIUDHYgO.mjs";
//#region src/auto-reply/reply/acp-projector.ts
const ACP_BLOCK_REPLY_TIMEOUT_MS = 15e3;
const ACP_LIVE_IDLE_FLUSH_FLOOR_MS = 750;
const ACP_LIVE_IDLE_MIN_CHARS = 80;
const ACP_LIVE_SOFT_FLUSH_CHARS = 220;
const ACP_LIVE_HARD_FLUSH_CHARS = 480;
const HIDDEN_BOUNDARY_TAGS = /* @__PURE__ */ new Set(["tool_call", "tool_call_update"]);
function shouldInsertSeparator(params) {
	if (!params.previousTail || /^\s/.test(params.nextText)) return false;
	return params.separator === " " ? !/\s$/.test(params.previousTail) : !params.previousTail.endsWith("\n");
}
function shouldFlushLiveBufferOnBoundary(text) {
	return text.length >= ACP_LIVE_HARD_FLUSH_CHARS || text.endsWith("\n\n") || /[.!?][)"'`]*\s$/.test(text) || text.length >= ACP_LIVE_SOFT_FLUSH_CHARS && /\s$/.test(text);
}
function shouldFlushLiveBufferOnIdle(text) {
	return text.length >= ACP_LIVE_IDLE_MIN_CHARS || /[.!?][)"'`]*$/.test(text.trimEnd()) || text.includes("\n");
}
function renderToolSummaryText(event, shouldSendFullToolDetails) {
	const detailParts = [];
	const commandBearing = normalizeOptionalLowercaseString(event.kind) === "execute";
	const title = shouldSendFullToolDetails || !commandBearing ? normalizeOptionalString(event.title) : void 0;
	if (title) detailParts.push(title);
	const status = normalizeOptionalString(event.status);
	if (status) detailParts.push(`status=${status}`);
	const fallback = shouldSendFullToolDetails || !commandBearing ? normalizeOptionalString(event.text) : void 0;
	if (detailParts.length === 0 && fallback) detailParts.push(fallback);
	const display = resolveToolDisplay({
		name: "tool_call",
		meta: detailParts.join(" · ") || "tool call"
	});
	return formatToolSummary(display);
}
function createAcpReplyProjector(params) {
	const settings = resolveAcpProjectionSettings(params.cfg);
	const hiddenBoundarySeparator = settings.hiddenBoundarySeparator === "space" ? " " : "\n\n";
	const streaming = resolveAcpStreamingConfig({
		cfg: params.cfg,
		provider: params.provider,
		accountId: params.accountId,
		deliveryMode: settings.deliveryMode
	});
	const blockReplyPipeline = createBlockReplyPipeline({
		onBlockReply: async (payload) => {
			await params.deliver("block", payload);
		},
		timeoutMs: ACP_BLOCK_REPLY_TIMEOUT_MS,
		coalescing: settings.deliveryMode === "live" ? void 0 : streaming.coalescing
	});
	const chunker = new EmbeddedBlockChunker(streaming.chunking);
	const filterConversationContext = createVerifiedConversationContextStreamFilter(params.getConversationContext);
	const liveIdleFlushMs = Math.max(streaming.coalescing.idleMs, ACP_LIVE_IDLE_FLUSH_FLOOR_MS);
	let emittedOutputChars = 0;
	let truncationNoticeEmitted = false;
	let lastStatusHash;
	let lastToolHash;
	let lastUsageTuple;
	let lastVisibleOutputTail;
	let pendingHiddenBoundary = false;
	let liveBufferText = "";
	let finalOnlyOutputText = "";
	let liveIdleTimer;
	const pendingToolDeliveries = [];
	const toolLifecycleById = /* @__PURE__ */ new Map();
	const shouldSendToolSummaries = () => params.shouldSendToolSummariesNow?.() ?? params.shouldSendToolSummaries;
	const clearLiveIdleTimer = () => {
		if (!liveIdleTimer) return;
		clearTimeout(liveIdleTimer);
		liveIdleTimer = void 0;
	};
	const drainChunker = (force) => {
		if (settings.deliveryMode === "final_only" && !force) return;
		chunker.drain({
			force,
			emit: (chunk) => {
				blockReplyPipeline.enqueue({ text: chunk });
			}
		});
	};
	const flushLiveBuffer = (opts) => {
		if (settings.deliveryMode !== "live" || !liveBufferText) return;
		if (opts?.idle && !shouldFlushLiveBufferOnIdle(liveBufferText)) return;
		const text = liveBufferText;
		liveBufferText = "";
		chunker.append(text);
		drainChunker(opts?.force === true);
	};
	const scheduleLiveIdleFlush = () => {
		if (settings.deliveryMode !== "live") return;
		if (!liveBufferText) return;
		clearLiveIdleTimer();
		liveIdleTimer = setTimeout(() => {
			flushLiveBuffer({
				force: true,
				idle: true
			});
			if (liveBufferText) scheduleLiveIdleFlush();
		}, liveIdleFlushMs);
	};
	const flushBufferedToolDeliveries = async (force) => {
		if (!(settings.deliveryMode === "final_only" && force)) return;
		if (!shouldSendToolSummaries()) {
			pendingToolDeliveries.length = 0;
			return;
		}
		for (const entry of pendingToolDeliveries.splice(0)) await params.deliver("tool", entry.payload, entry.meta);
	};
	const flush = async (force = false) => {
		if (settings.deliveryMode === "live") {
			clearLiveIdleTimer();
			flushLiveBuffer({ force: true });
		}
		await flushBufferedToolDeliveries(force);
		if (settings.deliveryMode === "final_only") {
			if (force && finalOnlyOutputText.trim().length > 0) {
				const text = finalOnlyOutputText;
				finalOnlyOutputText = "";
				await params.deliver("final", { text });
			}
		} else drainChunker(force);
		await blockReplyPipeline.flush({ force });
	};
	const emitSystemStatus = async (text, opts) => {
		if (!shouldSendToolSummaries()) return;
		const bounded = truncateUtf16WithEllipsis(text.trim(), settings.maxSessionUpdateChars);
		if (!bounded) return;
		const formatted = prefixSystemMessage(bounded);
		const hash = formatted.trim();
		if (settings.repeatSuppression && opts?.dedupe !== false && lastStatusHash === hash) return;
		if (settings.deliveryMode === "final_only") pendingToolDeliveries.push({ payload: { text: formatted } });
		else {
			await flush(true);
			await params.deliver("tool", { text: formatted });
		}
		lastStatusHash = hash;
	};
	const markHiddenToolBoundary = (event) => {
		if (!event.tag || !HIDDEN_BOUNDARY_TAGS.has(event.tag)) return;
		const status = normalizeOptionalLowercaseString(event.status);
		const isTerminal = resolveAcpToolTerminalOutcome(status) !== void 0;
		pendingHiddenBoundary = pendingHiddenBoundary || event.tag === "tool_call" || isTerminal;
	};
	const emitToolSummary = async (event) => {
		if (!shouldSendToolSummaries()) {
			markHiddenToolBoundary(event);
			return;
		}
		const renderedToolSummary = renderToolSummaryText(event, params.shouldSendFullToolDetails);
		const toolSummary = truncateUtf16WithEllipsis(renderedToolSummary, settings.maxSessionUpdateChars);
		const hash = renderedToolSummary.trim();
		const toolCallId = normalizeOptionalString(event.toolCallId);
		const status = normalizeOptionalLowercaseString(event.status);
		const isTerminal = resolveAcpToolTerminalOutcome(status) !== void 0;
		const isStart = status === "in_progress" || event.tag === "tool_call";
		if (settings.repeatSuppression) {
			if (toolCallId) {
				const state = toolLifecycleById.get(toolCallId) ?? {
					started: false,
					terminal: false
				};
				if (isTerminal && state.terminal) return;
				if (isStart && state.started) return;
				if (state.lastRenderedHash === hash) return;
				if (isStart) state.started = true;
				if (isTerminal) state.terminal = true;
				state.lastRenderedHash = hash;
				toolLifecycleById.set(toolCallId, state);
			} else if (lastToolHash === hash) return;
		}
		const deliveryMeta = {
			...toolCallId ? { toolCallId } : {},
			allowEdit: Boolean(toolCallId && event.tag === "tool_call_update")
		};
		if (settings.deliveryMode === "final_only") {
			pendingToolDeliveries.push({
				payload: { text: toolSummary },
				meta: deliveryMeta
			});
			markHiddenToolBoundary(event);
		} else {
			await flush(true);
			await params.deliver("tool", { text: toolSummary }, deliveryMeta);
		}
		lastToolHash = hash;
	};
	const emitTruncationNotice = async () => {
		if (truncationNoticeEmitted) return;
		truncationNoticeEmitted = true;
		await emitSystemStatus("output truncated", { dedupe: false });
	};
	const onEvent = async (event) => {
		params.onProgress?.();
		if (event.type === "text_delta") {
			if (event.stream && event.stream !== "output") return;
			if (!isAcpTagVisible(settings, event.tag)) return;
			let text = event.text;
			if (!text) return;
			if (pendingHiddenBoundary && shouldInsertSeparator({
				separator: hiddenBoundarySeparator,
				previousTail: lastVisibleOutputTail,
				nextText: text
			})) text = `${hiddenBoundarySeparator}${text}`;
			pendingHiddenBoundary = false;
			if (emittedOutputChars >= settings.maxOutputChars) {
				await emitTruncationNotice();
				return;
			}
			const remaining = settings.maxOutputChars - emittedOutputChars;
			const accepted = remaining < text.length ? truncateUtf16Safe(text, remaining) : text;
			if (accepted.length > 0) {
				emittedOutputChars += accepted.length;
				const safeText = filterConversationContext(accepted);
				lastVisibleOutputTail = safeText.slice(-1) || lastVisibleOutputTail;
				if (settings.deliveryMode === "live") {
					liveBufferText += safeText;
					if (shouldFlushLiveBufferOnBoundary(liveBufferText)) {
						clearLiveIdleTimer();
						flushLiveBuffer({ force: true });
					} else scheduleLiveIdleFlush();
				} else finalOnlyOutputText += safeText;
			}
			if (accepted.length < text.length) {
				emittedOutputChars = settings.maxOutputChars;
				await emitTruncationNotice();
			}
			return;
		}
		if (event.type === "status") {
			if (!isAcpTagVisible(settings, event.tag)) return;
			if (event.tag === "usage_update" && settings.repeatSuppression) {
				const usageTuple = typeof event.used === "number" && typeof event.size === "number" ? `${event.used}/${event.size}` : event.text.trim();
				if (usageTuple === lastUsageTuple) return;
				lastUsageTuple = usageTuple;
			}
			await emitSystemStatus(event.text);
			return;
		}
		if (event.type === "tool_call") {
			if (!isAcpTagVisible(settings, event.tag)) {
				markHiddenToolBoundary(event);
				return;
			}
			await emitToolSummary(event);
		}
	};
	return {
		onEvent,
		flush
	};
}
//#endregion
//#region src/auto-reply/reply/dispatch-acp-payload.ts
const dispatchAcpTtsRuntimeLoader = createLazyImportLoader(() => import("./tts.runtime.js"));
function prepareAcpDeliveryPayload(params) {
	if (!params.routed) return prepareReplyPayloadForDispatcher(params.dispatcher, params.kind, params.payload);
	return normalizeReplyPayloadOutcome(params.payload, { transformReplyPayload: createChannelReplyTransform({
		messaging: params.messaging,
		cfg: params.cfg,
		accountId: params.accountId
	}) });
}
async function maybeApplyAcpTts(params) {
	if (params.skipTts) return params.payload;
	if (isReplyPayloadStatusNotice(params.payload)) return params.payload;
	const ttsStatus = resolveStatusTtsSnapshot({
		cfg: params.cfg,
		sessionAuto: params.ttsAuto,
		agentId: params.agentId,
		channelId: params.channel,
		accountId: params.accountId
	});
	if (!ttsStatus) return params.payload;
	if (ttsStatus.autoMode === "inbound" && !params.inboundAudio) return params.payload;
	if (params.kind !== "final" && resolveConfiguredTtsMode(params.cfg, {
		agentId: params.agentId,
		channelId: params.channel,
		accountId: params.accountId
	}) === "final") return params.payload;
	const { maybeApplyTtsToPayload } = await dispatchAcpTtsRuntimeLoader.load();
	const applied = await maybeApplyTtsToPayload({
		payload: params.payload,
		cfg: params.cfg,
		channel: params.channel,
		kind: params.kind,
		inboundAudio: params.inboundAudio,
		ttsAuto: params.ttsAuto,
		agentId: params.agentId,
		accountId: params.accountId
	});
	return copyReplyPayloadMetadata(params.payload, applied);
}
const channelPluginRuntimeLoader$1 = createLazyImportLoader(() => import("./plugins-DxaiQKnF.mjs"));
async function shouldTreatDeliveredTextAsVisible(params) {
	if (!normalizeOptionalString(params.text)) return false;
	if (params.kind === "final") return true;
	const channelId = normalizeOptionalLowercaseString(params.channel);
	if (!channelId) return false;
	const { getChannelPlugin } = await channelPluginRuntimeLoader$1.load();
	const outbound = getChannelPlugin(channelId)?.outbound;
	const visibilityOverride = outbound?.shouldTreatDeliveredTextAsVisible ?? outbound?.shouldTreatRoutedTextAsVisible;
	if (visibilityOverride) return visibilityOverride({
		kind: params.kind,
		text: params.text
	});
	return false;
}
function getAcpBlockTranscriptText(blocks, pendingBlockSource, confirmedOnly = false) {
	const deliveredSources = new Set(blocks.filter((block) => block.delivered).map((block) => block.source));
	const recoveredSources = new Set(blocks.filter((block) => block.delivered === "final").map((block) => block.source));
	recoveredSources.delete(pendingBlockSource);
	for (const block of blocks) if (block.payload.text && !block.delivered) recoveredSources.delete(block.source);
	return blocks.flatMap((block) => {
		const sourceConfirmed = block.source ? block.source.complete && deliveredSources.has(block.source) || recoveredSources.has(block.source) : block.delivered;
		const text = !confirmedOnly || sourceConfirmed ? block.transcriptText : block.delivered ? block.payload.text : void 0;
		return text ? [text] : [];
	}).join("\n");
}
function joinAcpBlockText(blocks) {
	let text = "";
	let previousSource;
	for (const block of blocks) if (block.payload.text) {
		if (text && (!block.source || block.source !== previousSource)) text += "\n";
		text += block.payload.text;
		previousSource = block.source;
	}
	return text;
}
async function recoverAcpBlockText(state, params) {
	if (state.deliveredAnswerFinalToUser || !params.shouldRouteToOriginating && state.queuedUntrackedVisibleTextDeliveries > 0 && !params.suppressBlockUserDelivery && state.deliveredVisibleText && !state.failedVisibleTextDelivery) return false;
	const blocks = state.blockTexts.filter((block) => block.needsFinalDelivery && (!params.onlyUndelivered || !block.delivered));
	let queued = false;
	for (const block of blocks) {
		if (params.abortSignal?.aborted) break;
		if (block.source && !params.suppressBlockUserDelivery && await shouldTreatDeliveredTextAsVisible({
			channel: params.channel,
			kind: "block",
			text: block.payload.text
		}) && hasBlockReplyDeliveryCustody(await block.source.settle())) continue;
		if (params.abortSignal?.aborted) break;
		queued = await block.deliver("final", true) || queued;
		if (block.delivered !== "final") break;
	}
	return queued;
}
function buildAcpTextContinuation(payload, text) {
	return copyReplyPayloadMetadata(payload, {
		text,
		replyToId: payload.replyToId,
		replyToTag: payload.replyToTag,
		replyToCurrent: payload.replyToCurrent,
		isCommentary: payload.isCommentary,
		isReasoning: payload.isReasoning
	});
}
//#endregion
//#region src/auto-reply/reply/dispatch-acp-delivery.ts
const routeReplyRuntimeLoader = createLazyImportLoader(() => import("./route-reply.runtime.js"));
const channelPluginRuntimeLoader = createLazyImportLoader(() => import("./plugins-DxaiQKnF.mjs"));
const messageActionRuntimeLoader = createLazyImportLoader(() => import("./message-action-runner-B0l0dehx.mjs"));
function createAcpDispatchDeliveryCoordinator(params) {
	const directChannel = normalizeOptionalLowercaseString(params.ctx.Provider ?? params.ctx.Surface);
	const routedChannel = normalizeOptionalLowercaseString(params.originatingChannel);
	const deliverySessionKey = normalizeOptionalString(params.sessionKey) ?? params.ctx.SessionKey;
	const explicitAccountId = normalizeOptionalString(params.originatingAccountId) ?? normalizeOptionalString(params.ctx.AccountId);
	const resolvedAccountId = resolveReplyDeliveryAccountId(params.cfg, routedChannel ?? directChannel, explicitAccountId);
	const routedReplyDelivery = params.originatingChannel ? createReplyDeliveryContext(resolveReplyToMode(params.cfg, params.originatingChannel, resolvedAccountId, params.originatingChatType ?? params.ctx.ChatType), params.originatingChatType ?? params.ctx.ChatType) : void 0;
	const state = {
		startedReplyLifecycle: false,
		blockTexts: [],
		accumulatedBlockTtsText: "",
		accumulatedFinalText: "",
		accumulatedDeliveredFinalText: "",
		pendingTranscriptOutcomes: [],
		cleanBlockTtsDirectiveText: shouldCleanTtsDirectiveText({
			cfg: params.cfg,
			ttsAuto: params.sessionTtsAuto,
			agentId: params.agentId,
			channelId: params.ttsChannel,
			accountId: resolvedAccountId
		}) ? createTtsDirectiveTextStreamCleaner() : void 0,
		deliveredFinalReply: false,
		pendingAnswerDelivery: false,
		pendingFinalTtsMedia: false,
		deliveredAnswerFinalToUser: false,
		deliveredFinalTtsMedia: false,
		deliveredVisibleText: false,
		failedVisibleTextDelivery: false,
		queuedUntrackedVisibleTextDeliveries: 0,
		settledUntrackedVisibleText: false,
		routedCounts: {
			tool: 0,
			block: 0,
			final: 0
		},
		suppressionReason: void 0,
		toolMessageByCallId: /* @__PURE__ */ new Map()
	};
	let hasPendingDirectBlockReplyDelivery = false;
	let pendingBlockSource;
	let drainBlockText;
	const settleDirectVisibleText = async () => {
		await waitForReplyDispatcherIdle({ waitForIdle: async () => {
			await Promise.all(state.pendingTranscriptOutcomes);
		} }, params.abortSignal);
		if (params.abortSignal?.aborted) return;
		hasPendingDirectBlockReplyDelivery = false;
		if (state.settledUntrackedVisibleText || state.queuedUntrackedVisibleTextDeliveries === 0) return;
		state.settledUntrackedVisibleText = true;
		const receipt = await waitForReplyDispatcherIdle(params.dispatcher, params.abortSignal);
		if (!receipt) return;
		const visibleCounts = [receipt.counts.block, receipt.counts.final];
		state.failedVisibleTextDelivery ||= visibleCounts.some((counts) => counts.failedBeforeSend + counts.failedAfterSend > 0);
		state.deliveredVisibleText ||= visibleCounts.some((counts) => counts.delivered + counts.failedAfterSend > 0);
	};
	const startReplyLifecycleOnce = async () => {
		if (state.startedReplyLifecycle) return;
		state.startedReplyLifecycle = true;
		if (params.suppressReplyLifecycle) return;
		Promise.resolve(params.onReplyStart?.()).catch((error) => {
			logVerbose(`dispatch-acp: reply lifecycle start failed: ${formatErrorMessage(error)}`);
		});
	};
	const tryEditToolMessage = async (payload, toolCallId) => {
		const handle = state.toolMessageByCallId.get(toolCallId);
		if (!handle?.messageId) return false;
		const message = normalizeOptionalString(payload.text);
		if (!message) return false;
		try {
			const { runMessageAction } = await messageActionRuntimeLoader.load();
			await runMessageAction({
				cfg: params.cfg,
				action: "edit",
				params: {
					channel: handle.channel,
					to: handle.to,
					threadId: handle.threadId,
					messageId: handle.messageId,
					message
				},
				defaultAccountId: handle.accountId,
				sessionKey: params.ctx.SessionKey,
				requesterAccountId: params.ctx.AccountId
			});
			state.routedCounts.tool += 1;
			return true;
		} catch (error) {
			logVerbose(`dispatch-acp: tool message edit failed for ${toolCallId}: ${formatErrorMessage(error)}`);
			return false;
		}
	};
	const deliver = async (kind, payload, meta) => {
		if (kind === "block") setBlockReplyDelivery(Promise.resolve({ outcome: "cancelled" }));
		const transcriptSource = meta?.transcriptSource;
		const coveredBlocks = kind === "final" ? state.blockTexts.filter((block) => transcriptSource?.kind === "blocks" || block.needsFinalDelivery) : [];
		const coverFinalBlockText = (source, blocks = coveredBlocks) => {
			if (!source.text?.trim() || source.isCommentary || source.isReasoning || isReplyPayloadStatusNotice(source)) return;
			for (const block of blocks) block.needsFinalDelivery = false;
		};
		let visiblePayload = payload;
		if (!params.suppressUserDelivery) {
			const routed = params.shouldRouteToOriginating && routedChannel !== void 0;
			const messaging = routed ? (await channelPluginRuntimeLoader.load()).getChannelPlugin(routedChannel)?.messaging : void 0;
			const prepared = prepareAcpDeliveryPayload({
				cfg: params.cfg,
				dispatcher: params.dispatcher,
				kind,
				payload,
				routed,
				...messaging ? { messaging } : {},
				accountId: resolvedAccountId
			});
			if (prepared.kind === "suppress") {
				if (prepared.reason === "channel_transform") {
					state.suppressionReason = prepared.reason;
					coverFinalBlockText(payload);
					if (kind === "block" && payload.text?.trim()) setBlockReplyDelivery(Promise.resolve({ outcome: "channel-transform" }), payload);
				}
				return false;
			}
			visiblePayload = prepared.payload;
		}
		const isStatusNotice = isReplyPayloadStatusNotice(visiblePayload);
		const rawBlockPayloadText = kind === "block" ? normalizeOptionalString(visiblePayload.text) : void 0;
		const rawBlockText = isStatusNotice ? void 0 : rawBlockPayloadText;
		let blockSource;
		if (rawBlockPayloadText) {
			const joinsBufferedTtsDirective = state.cleanBlockTtsDirectiveText?.hasBufferedDirectiveText() === true;
			if (rawBlockText) {
				if (state.accumulatedBlockTtsText.length > 0 && !joinsBufferedTtsDirective) state.accumulatedBlockTtsText += "\n";
				state.accumulatedBlockTtsText += rawBlockText;
			}
			if (state.cleanBlockTtsDirectiveText && rawBlockText) {
				if (!visiblePayload.isCommentary && !visiblePayload.isReasoning) blockSource = pendingBlockSource ?? createBlockReplySource();
				const text = state.cleanBlockTtsDirectiveText.push(rawBlockPayloadText);
				if (blockSource) {
					const hasPendingText = state.cleanBlockTtsDirectiveText.hasBufferedDirectiveText();
					blockSource.setComplete(!hasPendingText);
					pendingBlockSource = hasPendingText ? blockSource : void 0;
				}
				visiblePayload = copyReplyPayloadMetadata(visiblePayload, {
					...visiblePayload,
					text: text.trim() ? text : void 0
				});
			}
		}
		const rawFinalText = kind === "final" && !isStatusNotice ? normalizeOptionalString(visiblePayload.text) : void 0;
		if (rawFinalText && !transcriptSource) {
			if (state.accumulatedFinalText.length > 0) state.accumulatedFinalText += "\n";
			state.accumulatedFinalText += rawFinalText;
		}
		const transcriptFinalText = !transcriptSource ? rawFinalText : transcriptSource.kind === "final" ? transcriptSource.text : void 0;
		const sendPrepared = async (preparedPayload, deliveredBlock, skipTts = meta?.skipTts, sendKind = kind) => {
			const recoveringBlock = sendKind === "final" && kind === "block";
			const finalBlocks = recoveringBlock && deliveredBlock ? [deliveredBlock] : coveredBlocks;
			let outgoingPayload = recoveringBlock ? deliveredBlock?.delivered || params.suppressBlockUserDelivery ? buildAcpTextContinuation(preparedPayload, preparedPayload.text) : copyReplyPayloadMetadata(preparedPayload, { ...preparedPayload }) : preparedPayload;
			if (!hasOutboundReplyContent(outgoingPayload, { trimText: true })) return false;
			await startReplyLifecycleOnce();
			if (params.suppressUserDelivery) return false;
			if (sendKind === "block" && params.suppressBlockUserDelivery && !isStatusNotice && !outgoingPayload.isReasoning && !outgoingPayload.isCommentary) {
				if (!Boolean(outgoingPayload.mediaUrl || outgoingPayload.mediaUrls?.length || outgoingPayload.presentation || outgoingPayload.interactive || outgoingPayload.channelData)) return false;
				outgoingPayload = copyReplyPayloadMetadata(outgoingPayload, {
					...outgoingPayload,
					text: void 0
				});
			}
			const appliedTtsPayload = await maybeApplyAcpTts({
				payload: outgoingPayload,
				cfg: params.cfg,
				agentId: params.agentId,
				channel: params.ttsChannel,
				accountId: resolvedAccountId,
				kind: sendKind,
				inboundAudio: params.inboundAudio,
				ttsAuto: params.sessionTtsAuto,
				skipTts
			});
			const finalVisibleTextSource = sendKind === "final" && params.suppressBlockUserDelivery && state.cleanBlockTtsDirectiveText ? skipTts || outgoingPayload.isError || isReplyPayloadTtsSupplement(outgoingPayload) ? outgoingPayload.text : mergeDeferredFinalText(state.accumulatedBlockTtsText, outgoingPayload.text) : void 0;
			const ttsPayload = finalVisibleTextSource !== void 0 ? copyReplyPayloadMetadata(appliedTtsPayload, {
				...appliedTtsPayload,
				text: cleanDeferredFinalText(finalVisibleTextSource) || void 0
			}) : appliedTtsPayload;
			const hasFinalTtsMedia = sendKind === "final" && isReplyPayloadTtsSupplement(ttsPayload);
			const isAnswerBearingFinal = sendKind === "final" && (isCaptionedFinalTextPayload(outgoingPayload) || hasFinalTtsMedia && Boolean(ttsPayload.text?.trim()));
			const recordPendingDelivery = (tracksVisibleText) => {
				if (deliveredBlock && tracksVisibleText) deliveredBlock.needsFinalDelivery = false;
				const pendingAnswer = tracksVisibleText && sendKind !== "tool" && !isStatusNotice && !ttsPayload.isCommentary && !ttsPayload.isReasoning;
				state.pendingAnswerDelivery ||= pendingAnswer;
				coverFinalBlockText(ttsPayload, finalBlocks);
				state.pendingFinalTtsMedia ||= hasFinalTtsMedia;
			};
			const recordFinalReply = () => {
				if (sendKind === "final") {
					state.deliveredFinalReply = true;
					state.deliveredAnswerFinalToUser ||= !recoveringBlock && isAnswerBearingFinal && (!transcriptSource || transcriptSource.kind === "final");
					state.deliveredFinalTtsMedia ||= hasFinalTtsMedia;
					coverFinalBlockText(ttsPayload, finalBlocks);
				}
			};
			const recordDeliveredReply = (tracksVisibleText) => {
				if (deliveredBlock) deliveredBlock.delivered = sendKind === "final" ? "final" : "block";
				if ((rawFinalText || hasFinalTtsMedia) && transcriptSource && transcriptSource.kind !== "final") for (const block of finalBlocks) block.delivered = "final";
				else if (transcriptFinalText) state.accumulatedDeliveredFinalText = state.accumulatedDeliveredFinalText ? `${state.accumulatedDeliveredFinalText}\n${transcriptFinalText}` : transcriptFinalText;
				recordFinalReply();
				if (tracksVisibleText) {
					state.deliveredVisibleText = true;
					if (deliveredBlock) deliveredBlock.needsFinalDelivery = false;
				}
			};
			if (params.shouldRouteToOriginating && params.originatingChannel && params.originatingTo) {
				const toolCallId = normalizeOptionalString(meta?.toolCallId);
				if (sendKind === "tool" && meta?.allowEdit === true && toolCallId) {
					if (await tryEditToolMessage(ttsPayload, toolCallId)) return true;
				}
				const tracksVisibleText = await shouldTreatDeliveredTextAsVisible({
					channel: routedChannel,
					kind: sendKind,
					text: ttsPayload.text
				});
				const { routeReply } = await routeReplyRuntimeLoader.load();
				const threadId = params.originatingThreadId ?? resolveRoutedDeliveryThreadId({
					ctx: params.ctx,
					sessionKey: deliverySessionKey
				});
				const result = await routeReply({
					payload: ttsPayload,
					channel: params.originatingChannel,
					to: params.originatingTo,
					agentId: params.agentId,
					sessionKey: deliverySessionKey,
					...deliverySessionKey !== params.ctx.SessionKey ? { policySessionKey: params.ctx.SessionKey } : {},
					accountId: resolvedAccountId,
					requesterSenderId: params.ctx.SenderId,
					requesterSenderName: params.ctx.SenderName,
					requesterSenderUsername: params.ctx.SenderUsername,
					requesterSenderE164: params.ctx.SenderE164,
					threadId,
					replyDelivery: routedReplyDelivery,
					cfg: params.cfg,
					abortSignal: params.abortSignal,
					mirror: false,
					replyKind: sendKind,
					runId: params.runId
				});
				const outcome = resolveRoutedReplyDeliveryOutcome(result);
				const pending = outcome === "recovery-owned" || outcome === "failed-deliver";
				if (sendKind === "block") setBlockReplyDelivery(Promise.resolve({
					outcome,
					pending
				}), ttsPayload);
				if (deliveredBlock && result.suppressed && (tracksVisibleText || outcome === "channel-transform" && ttsPayload.text?.trim())) deliveredBlock.needsFinalDelivery = false;
				if (pending) {
					recordPendingDelivery(tracksVisibleText);
					return true;
				}
				if (shouldRetryReplyDispatch(outcome) && hasFinalTtsMedia && ttsPayload.text?.trim()) {
					if (!result.suppressed) logVerbose(`dispatch-acp: route-reply (acp/${sendKind}) failed: ${result.error ?? "unknown error"}`);
					return await deliver("final", { text: ttsPayload.text }, {
						skipTts: true,
						transcriptSource: transcriptSource ?? {
							kind: "final",
							text: rawFinalText ?? ""
						}
					});
				}
				if (!result.delivered && !result.suppressed) {
					if (tracksVisibleText) state.failedVisibleTextDelivery = true;
					logVerbose(`dispatch-acp: route-reply (acp/${sendKind}) failed: ${result.error ?? "unknown error"}`);
					return false;
				}
				if (result.suppressed) {
					if (outcome === "channel-transform") coverFinalBlockText(ttsPayload, finalBlocks);
					if (sendKind === "final") state.deliveredFinalReply = true;
					if (tracksVisibleText) state.deliveredVisibleText = true;
					return true;
				}
				if (!result.ok) logVerbose(`dispatch-acp: route-reply (acp/${sendKind}) partially failed after delivery: ${result.error ?? "unknown error"}`);
				if (sendKind === "tool" && meta?.toolCallId && result.messageId) state.toolMessageByCallId.set(meta.toolCallId, {
					channel: params.originatingChannel,
					accountId: resolvedAccountId,
					to: params.originatingTo,
					...threadId != null ? { threadId } : {},
					messageId: result.messageId
				});
				recordDeliveredReply(tracksVisibleText);
				state.routedCounts[sendKind] += 1;
				return true;
			}
			if (sendKind === "tool" && hasPendingDirectBlockReplyDelivery) {
				hasPendingDirectBlockReplyDelivery = false;
				await waitForReplyDispatcherIdle(params.dispatcher, params.abortSignal);
			}
			const tracksVisibleText = await shouldTreatDeliveredTextAsVisible({
				channel: directChannel,
				kind: sendKind,
				text: ttsPayload.text
			});
			const transcriptOutcome = sendKind !== "tool" ? captureReplyDispatchDeliveryOutcome(ttsPayload) : void 0;
			if (hasFinalTtsMedia && ttsPayload.text?.trim()) attachReplyDispatchUndeliveredFallback(ttsPayload, buildCaptionedFinalTextFallback(ttsPayload));
			const delivered = sendKind === "tool" ? params.dispatcher.sendToolResult(ttsPayload) : sendKind === "block" ? params.dispatcher.sendBlockReply(ttsPayload) : params.dispatcher.sendFinalReply(ttsPayload);
			if (sendKind === "block") setBlockReplyDelivery(delivered && transcriptOutcome?.isTracked() ? transcriptOutcome.promise.then((outcome) => ({
				outcome,
				pending: transcriptOutcome.hasPendingDelivery()
			})) : Promise.resolve(delivered ? {
				outcome: "failed-deliver",
				pending: true
			} : { outcome: "cancelled" }), ttsPayload);
			if (delivered && transcriptOutcome?.isTracked()) {
				const settlement = transcriptOutcome.promise.then((outcome) => {
					if (transcriptOutcome.hasPendingDelivery()) recordPendingDelivery(tracksVisibleText);
					else if (outcome === "delivered") recordDeliveredReply(tracksVisibleText);
					else {
						if (!shouldRetryReplyDispatch(outcome)) {
							if (deliveredBlock && ttsPayload.text?.trim()) deliveredBlock.needsFinalDelivery = false;
							coverFinalBlockText(ttsPayload, finalBlocks);
						}
						if (tracksVisibleText) {
							state.failedVisibleTextDelivery ||= outcome === "failed-before-deliver" || outcome === "failed-deliver";
							state.deliveredVisibleText ||= outcome === "failed-deliver";
						}
					}
				});
				state.pendingTranscriptOutcomes.push(settlement);
				if (sendKind === "final") await settlement;
			} else if (delivered) {
				recordFinalReply();
				if (tracksVisibleText) {
					state.queuedUntrackedVisibleTextDeliveries += 1;
					state.settledUntrackedVisibleText = false;
				}
			} else if (!delivered && tracksVisibleText) state.failedVisibleTextDelivery = true;
			if (sendKind === "block" && delivered) hasPendingDirectBlockReplyDelivery = true;
			return delivered;
		};
		const recordBlock = (preparedPayload, transcriptText) => {
			const block = {
				payload: preparedPayload,
				transcriptText,
				source: blockSource,
				needsFinalDelivery: Boolean(preparedPayload.text),
				deliver: (sendKind, skipTts) => sendPrepared(block.payload, block, skipTts, sendKind)
			};
			state.blockTexts.push(block);
			return block;
		};
		const blockText = rawBlockPayloadText ? recordBlock(visiblePayload, rawBlockText) : void 0;
		if (state.cleanBlockTtsDirectiveText && rawBlockText) drainBlockText = state.cleanBlockTtsDirectiveText.hasBufferedDirectiveText() ? async (text) => {
			const tailPayload = buildAcpTextContinuation(visiblePayload, text);
			const tailBlock = recordBlock(tailPayload);
			const sendTail = async () => {
				setBlockReplyDelivery(Promise.resolve({ outcome: "cancelled" }), tailPayload);
				return params.abortSignal?.aborted ? false : await tailBlock.deliver("block", true);
			};
			return blockSource ? await blockSource.run(sendTail) ?? false : await sendTail();
		} : void 0;
		const send = () => blockText ? blockText.deliver("block") : sendPrepared(visiblePayload, void 0);
		return blockSource ? await blockSource.run(send) ?? false : await send();
	};
	return {
		startReplyLifecycle: startReplyLifecycleOnce,
		deliver,
		flushBlockText: async () => {
			if (params.abortSignal?.aborted) return;
			const text = state.cleanBlockTtsDirectiveText?.flush();
			const drain = drainBlockText;
			drainBlockText = void 0;
			pendingBlockSource?.setComplete(true);
			pendingBlockSource = void 0;
			if (text?.trim() && drain) await drain(text);
		},
		getAccumulatedVisibleBlockText: () => joinAcpBlockText(state.blockTexts),
		recoverBlockText: async (options) => {
			if (!params.suppressBlockUserDelivery) await settleDirectVisibleText();
			return await recoverAcpBlockText(state, {
				...params,
				channel: routedChannel ?? directChannel,
				onlyUndelivered: options?.onlyUndelivered
			});
		},
		getAccumulatedBlockTtsText: () => state.accumulatedBlockTtsText,
		getAccumulatedTranscriptText: () => state.accumulatedFinalText || getAcpBlockTranscriptText(state.blockTexts, pendingBlockSource),
		resolveAccumulatedDeliveredTranscriptText: async () => {
			await Promise.all(state.pendingTranscriptOutcomes);
			await Promise.all(state.blockTexts.flatMap((block) => block.source ? [block.source.settle()] : []));
			return state.accumulatedDeliveredFinalText || getAcpBlockTranscriptText(state.blockTexts, pendingBlockSource, true);
		},
		settleVisibleText: settleDirectVisibleText,
		hasDeliveredFinalReply: () => state.deliveredFinalReply,
		hasPendingAnswerDelivery: () => state.pendingAnswerDelivery,
		hasPendingFinalTtsMedia: () => state.pendingFinalTtsMedia,
		hasDeliveredAnswerFinalToUser: () => state.deliveredAnswerFinalToUser,
		hasDeliveredFinalTtsMedia: () => state.deliveredFinalTtsMedia,
		hasDeliveredVisibleText: () => state.deliveredVisibleText,
		hasFailedVisibleTextDelivery: () => state.failedVisibleTextDelivery,
		getDeliverySuppressionReason: () => state.suppressionReason,
		getRoutedCounts: () => ({ ...state.routedCounts }),
		applyRoutedCounts: (counts) => {
			counts.tool += state.routedCounts.tool;
			counts.block += state.routedCounts.block;
			counts.final += state.routedCounts.final;
		}
	};
}
//#endregion
//#region src/auto-reply/reply/dispatch-acp-finalize.ts
const loadDispatchAcpTtsRuntime = createLazyPromise(() => import("./tts.runtime.js"));
const loadDispatchAcpManagerRuntime$1 = createLazyPromise(() => import("./dispatch-acp-manager.runtime.js"));
async function finalizeAcpTurnOutput(params) {
	const ttsMode = resolveConfiguredTtsMode(params.cfg, {
		agentId: params.agentId,
		channelId: params.ttsChannel,
		accountId: params.ttsAccountId
	});
	const accumulatedBlockTtsText = params.delivery.getAccumulatedBlockTtsText();
	const hasAccumulatedBlockText = accumulatedBlockTtsText.trim().length > 0;
	const ttsStatus = resolveStatusTtsSnapshot({
		cfg: params.cfg,
		sessionAuto: params.sessionTtsAuto,
		agentId: params.agentId,
		channelId: params.ttsChannel,
		accountId: params.ttsAccountId
	});
	const canAttemptFinalTts = ttsStatus != null && !(ttsStatus.autoMode === "inbound" && !params.inboundAudio);
	const shouldDeferVisibleTextForTts = params.shouldDeferVisibleTextForTts && ttsMode === "final" && hasAccumulatedBlockText && canAttemptFinalTts;
	const accumulatedVisibleBlockText = shouldDeferVisibleTextForTts ? cleanDeferredFinalText(accumulatedBlockTtsText) : params.delivery.getAccumulatedVisibleBlockText();
	if (!shouldDeferVisibleTextForTts) await params.delivery.settleVisibleText();
	if (params.abortSignal?.aborted) return false;
	let queuedFinal = params.delivery.hasPendingAnswerDelivery() || params.delivery.hasPendingFinalTtsMedia() || params.delivery.hasDeliveredVisibleText() && !params.delivery.hasFailedVisibleTextDelivery();
	if (ttsMode === "final" && hasAccumulatedBlockText && canAttemptFinalTts && !params.delivery.hasPendingFinalTtsMedia() && !params.delivery.hasDeliveredFinalTtsMedia()) try {
		const { maybeApplyTtsToPayload } = await loadDispatchAcpTtsRuntime();
		if (params.abortSignal?.aborted) return queuedFinal;
		const ttsSyntheticReply = await maybeApplyTtsToPayload({
			payload: { text: accumulatedBlockTtsText },
			cfg: params.cfg,
			channel: params.ttsChannel,
			kind: "final",
			inboundAudio: params.inboundAudio,
			ttsAuto: params.sessionTtsAuto,
			agentId: params.agentId,
			accountId: params.ttsAccountId
		});
		if (ttsSyntheticReply.mediaUrl) {
			const finalTtsPayload = markReplyPayloadAsTtsSupplement(shouldDeferVisibleTextForTts ? {
				...ttsSyntheticReply,
				text: accumulatedVisibleBlockText || void 0,
				trustedLocalMedia: true
			} : {
				...ttsSyntheticReply,
				text: void 0,
				trustedLocalMedia: true
			}, accumulatedBlockTtsText, shouldDeferVisibleTextForTts ? void 0 : { visibleTextAlreadyDelivered: true });
			const delivered = await params.delivery.deliver("final", finalTtsPayload, { transcriptSource: { kind: "blocks" } });
			queuedFinal = queuedFinal || delivered;
		} else if (shouldDeferVisibleTextForTts && ttsSyntheticReply.text?.trim() || needsTtsFallback(true, accumulatedVisibleBlockText, ttsSyntheticReply.text)) {
			const delivered = await params.delivery.deliver("final", { text: ttsSyntheticReply.text }, {
				skipTts: true,
				transcriptSource: { kind: "blocks" }
			});
			queuedFinal = queuedFinal || delivered;
		}
	} catch (err) {
		logVerbose(`dispatch-acp: accumulated ACP block TTS failed: ${formatErrorMessage(err)}`);
	}
	queuedFinal = await params.delivery.recoverBlockText({ onlyUndelivered: ttsMode === "all" }) || queuedFinal;
	if (params.shouldEmitResolvedIdentityNotice) {
		const { readAcpSessionEntryAsync } = await loadDispatchAcpManagerRuntime$1();
		const currentSession = await readAcpSessionEntryAsync({
			cfg: params.cfg,
			sessionKey: params.sessionKey,
			agentId: params.agentId
		});
		if (params.abortSignal?.aborted) return queuedFinal;
		const currentMeta = currentSession?.acp;
		const identityAfterTurn = resolveSessionIdentityFromMeta(currentMeta);
		if (!isSessionIdentityPending(identityAfterTurn)) {
			const resolvedDetails = resolveAcpThreadSessionDetailLines({
				sessionKey: params.sessionKey,
				meta: currentMeta
			});
			if (resolvedDetails.length > 0) {
				const delivered = await params.delivery.deliver("final", {
					text: prefixSystemMessage(["Session ids resolved.", ...resolvedDetails].join("\n")),
					isStatusNotice: true
				});
				queuedFinal = queuedFinal || delivered;
			}
		}
	}
	return queuedFinal;
}
//#endregion
//#region src/auto-reply/reply/dispatch-acp.ts
const loadDispatchAcpManagerRuntime = createLazyPromise(() => import("./dispatch-acp-manager.runtime.js"));
const loadDispatchAcpAuditRuntime = createLazyPromise(() => import("./acp-lifecycle-C9XFqg-G.mjs"));
function appendOrderedAcpAttachments(params) {
	for (const [index, attachment] of params.attachments.entries()) params.entries.push({
		attachment,
		sourceIndex: params.sourceIndexes?.[index],
		sequence: params.entries.length
	});
}
function resolveMergedAcpAttachments(entries) {
	return entries.toSorted((left, right) => {
		if (left.sourceIndex !== void 0 && right.sourceIndex !== void 0) return left.sourceIndex - right.sourceIndex || left.sequence - right.sequence;
		return left.sequence - right.sequence;
	}).map((entry) => entry.attachment);
}
const loadDispatchAcpTranscriptRuntime = createLazyPromise(() => import("./dispatch-acp-transcript.runtime.js"));
function resolveAcpRequestId(ctx) {
	const id = ctx.MessageSidFull ?? ctx.MessageSid ?? ctx.MessageSidFirst ?? ctx.MessageSidLast;
	if (typeof id === "string") {
		const normalizedId = normalizeOptionalString(id);
		if (normalizedId) return normalizedId;
	}
	if (typeof id === "number" || typeof id === "bigint") return String(id);
	return generateSecureUuid();
}
function resolveAcpTurnText(params) {
	if (params.sourceReplyDeliveryMode !== "message_tool_only") return params.promptText;
	const guidance = prefixSystemMessage([
		"Source channel delivery is private by default for this turn.",
		"Normal ACP final output will not be automatically posted to the source channel.",
		"To send visible output, use message(action=send). The target defaults to the current source channel."
	].join(" "));
	return params.promptText ? `${guidance}\n\n${params.promptText}` : guidance;
}
function isRestrictiveRuntimeToolsAllow(toolsAllow) {
	if (toolsAllow === void 0) return false;
	return !toolsAllow.some((entry) => normalizeLowercaseStringOrEmpty(entry) === "*");
}
async function hasBoundConversationForSession(params) {
	const channel = normalizeOptionalLowercaseString(params.channelRaw) ?? "";
	if (!channel) return false;
	const accountId = normalizeOptionalLowercaseString(params.accountIdRaw) ?? "";
	const configuredDefaultAccountId = params.cfg.channels?.[channel]?.defaultAccount;
	const normalizedAccountId = accountId || normalizeOptionalLowercaseString(configuredDefaultAccountId) || "default";
	const { listSessionBindingsBySessionAsync } = await loadDispatchAcpManagerRuntime();
	return (await listSessionBindingsBySessionAsync(params.sessionKey)).some((binding) => {
		const bindingChannel = normalizeOptionalLowercaseString(binding.conversation.channel) ?? "";
		const bindingAccountId = normalizeOptionalLowercaseString(binding.conversation.accountId) ?? "";
		const conversationId = normalizeOptionalString(binding.conversation.conversationId) ?? "";
		return bindingChannel === channel && (bindingAccountId || "default") === normalizedAccountId && conversationId.length > 0;
	});
}
function finishAcpDispatchAttempt(params) {
	const counts = params.dispatcher.getQueuedCounts();
	params.delivery.applyRoutedCounts(counts);
	const suppressionReason = counts.tool + counts.block + counts.final > 0 || params.queuedFinal ? void 0 : params.delivery.getDeliverySuppressionReason();
	const acpStats = params.getStats();
	if (params.outcome.kind === "ok") {
		logVerbose(`acp-dispatch: session=${params.sessionKey} outcome=ok latencyMs=${Date.now() - params.startedAt} queueDepth=${acpStats.turns.queueDepth} activeRuntimes=${acpStats.runtimeCache.activeSessions}`);
		params.recordProcessed("completed", { reason: suppressionReason ?? "acp_dispatch" });
	} else {
		logVerbose(`acp-dispatch: session=${params.sessionKey} outcome=error code=${params.outcome.error.code} latencyMs=${Date.now() - params.startedAt} queueDepth=${acpStats.turns.queueDepth} activeRuntimes=${acpStats.runtimeCache.activeSessions}`);
		params.recordProcessed("completed", { reason: `acp_error:${normalizeLowercaseStringOrEmpty(params.outcome.error.code)}` });
	}
	params.markIdle("message_completed");
	return {
		queuedFinal: params.queuedFinal,
		counts
	};
}
async function tryDispatchAcpReplyCore(params) {
	const sessionKey = normalizeOptionalString(params.sessionKey);
	if (!sessionKey || params.bypassForCommand) return null;
	prepareChannelParticipantObservation(params.ctx);
	const inputRecorder = params.userTurnTranscriptRecorder;
	const assertInputCurrent = () => {
		params.abortSignal?.throwIfAborted();
		inputRecorder?.withPendingInput?.(() => {});
	};
	const { getAcpSessionManager, maybeUnbindStaleBoundConversations } = await loadDispatchAcpManagerRuntime();
	const acpManager = getAcpSessionManager();
	const acpResolution = await acpManager.resolveSessionAsync({
		cfg: params.cfg,
		sessionKey,
		agentId: resolveSessionAgentId({
			config: params.cfg,
			sessionKey,
			fallbackAgentId: params.ctx.AgentId
		}),
		assertCurrent: assertInputCurrent
	});
	assertInputCurrent();
	if (acpResolution.kind === "none") return null;
	const canonicalSessionKey = acpResolution.sessionKey;
	const transcriptSessionId = acpResolution.kind === "ready" ? acpResolution.entry?.sessionId : void 0;
	const acpAgentId = acpResolution.agentId;
	const participantTarget = {
		agentId: acpAgentId,
		sessionKey: canonicalSessionKey,
		storePath: resolveSessionStorePathCore(params.cfg.session?.store, { agentId: acpAgentId }),
		onError: (error) => logVerbose(`dispatch-acp: participant persistence failed: ${formatErrorMessage(error)}`)
	};
	const progressSessionKeys = isDiagnosticsEnabled(params.cfg) ? Array.from(new Set([
		params.ctx.SessionKey,
		sessionKey,
		canonicalSessionKey
	].map((key) => normalizeOptionalString(key)).filter((key) => Boolean(key)))) : [];
	const markAcpProgress = progressSessionKeys.length > 0 ? () => {
		for (const key of progressSessionKeys) markDiagnosticSessionProgress({ sessionKey: key });
	} : void 0;
	const identityPendingBeforeTurn = isSessionIdentityPending(resolveSessionIdentityFromMeta(acpResolution.kind === "ready" ? acpResolution.meta : void 0));
	const shouldEmitResolvedIdentityNotice = !params.suppressUserDelivery && identityPendingBeforeTurn && (Boolean(params.ctx.MessageThreadId != null && (normalizeOptionalString(String(params.ctx.MessageThreadId)) ?? "")) || await hasBoundConversationForSession({
		cfg: params.cfg,
		sessionKey: canonicalSessionKey,
		channelRaw: params.ctx.OriginatingChannel ?? params.ctx.Surface ?? params.ctx.Provider,
		accountIdRaw: params.ctx.AccountId
	}));
	const resolvedAcpAgent = acpResolution.kind === "ready" ? normalizeOptionalString(acpResolution.meta.agent) ?? normalizeOptionalString(params.cfg.acp?.defaultAgent) ?? resolveAgentIdFromSessionKey(canonicalSessionKey) : resolveAgentIdFromSessionKey(canonicalSessionKey);
	const normalizedDispatchChannel = normalizeOptionalLowercaseString(params.ctx.OriginatingChannel ?? params.ctx.Surface ?? params.ctx.Provider);
	const explicitDispatchAccountId = normalizeOptionalString(params.ctx.AccountId);
	const dispatchChannels = params.cfg.channels;
	const defaultDispatchAccount = normalizedDispatchChannel == null ? void 0 : dispatchChannels?.[normalizedDispatchChannel]?.defaultAccount;
	const effectiveDispatchAccountId = explicitDispatchAccountId ?? normalizeOptionalString(defaultDispatchAccount);
	const shouldDeferVisibleTextForTts = shouldDeferFinalTtsText({
		cfg: params.cfg,
		ttsAuto: params.sessionTtsAuto,
		agentId: acpAgentId,
		channelId: params.ttsChannel,
		accountId: effectiveDispatchAccountId,
		inboundAudio: params.inboundAudio
	});
	let queuedFinal = false;
	const delivery = createAcpDispatchDeliveryCoordinator({
		cfg: params.cfg,
		agentId: acpAgentId,
		ctx: params.ctx,
		dispatcher: params.dispatcher,
		inboundAudio: params.inboundAudio,
		sessionKey: canonicalSessionKey,
		sessionTtsAuto: params.sessionTtsAuto,
		ttsChannel: params.ttsChannel,
		suppressUserDelivery: params.suppressUserDelivery,
		suppressBlockUserDelivery: shouldDeferVisibleTextForTts,
		suppressReplyLifecycle: params.suppressReplyLifecycle,
		shouldRouteToOriginating: params.shouldRouteToOriginating,
		originatingChannel: params.originatingChannel,
		originatingTo: params.originatingTo,
		originatingAccountId: params.originatingAccountId,
		originatingThreadId: params.originatingThreadId,
		originatingChatType: params.originatingChatType,
		onReplyStart: params.onReplyStart,
		abortSignal: params.abortSignal,
		runId: params.runId
	});
	const pendingAnswerText = params.ctx.agentText.trim();
	const persistInput = inputRecorder ? async () => {
		assertInputCurrent();
		await inputRecorder.persistApproved();
		assertInputCurrent();
		if (!inputRecorder.hasPersisted()) throw new Error("ACP input must be durably committed before dispatch.");
	} : void 0;
	try {
		if (pendingAnswerText && !params.images?.length && !params.extractedFileImages?.length && !hasInboundMediaForUnderstanding(params.ctx) && await claimPreparedPendingAgentQuestionAnswer({
			sessionKey: acpResolution.sessionKey,
			text: pendingAnswerText,
			sourceRecorder: inputRecorder,
			authority: {
				kind: "run",
				assertCurrent: assertInputCurrent
			}
		}, () => assertPreparedConversationBindingRouteCurrent(params.ctx))) {
			recordAcceptedSessionParticipantInput(params.ctx, participantTarget);
			const counts = params.dispatcher.getQueuedCounts();
			params.recordProcessed("completed", { reason: "acp_question_answer" });
			params.markIdle("message_completed");
			return {
				queuedFinal: false,
				counts
			};
		}
	} catch (error) {
		if (!(error instanceof QuestionAnswerUnconfirmedError) && !(error instanceof PreparedQuestionAnswerRefusedError)) throw error;
		params.recordProcessed("error", {
			reason: error instanceof QuestionAnswerUnconfirmedError ? "acp_question_answer_unconfirmed" : "acp_question_answer_refused",
			error: error.message
		});
		const queuedNotice = await delivery.deliver("final", {
			text: error.message,
			isError: true
		}).catch((deliveryError) => {
			logVerbose(`dispatch-acp: question notice delivery failed: ${formatErrorMessage(deliveryError)}`);
			return false;
		});
		params.markIdle("message_error");
		const counts = params.dispatcher.getQueuedCounts();
		delivery.applyRoutedCounts(counts);
		return {
			queuedFinal: queuedNotice,
			counts
		};
	}
	const deliverDeferredTextFallback = async () => shouldDeferVisibleTextForTts ? await delivery.recoverBlockText() : false;
	const projector = createAcpReplyProjector({
		cfg: params.cfg,
		shouldSendToolSummaries: params.shouldSendToolSummaries,
		shouldSendToolSummariesNow: params.shouldSendToolSummariesNow,
		shouldSendFullToolDetails: params.shouldSendFullToolDetails,
		deliver: delivery.deliver,
		getConversationContext: () => params.ctx.agentText,
		onProgress: markAcpProgress,
		provider: params.ctx.Surface ?? params.ctx.Provider,
		accountId: effectiveDispatchAccountId
	});
	const acpDispatchStartedAt = Date.now();
	const finishAttempt = (options) => finishAcpDispatchAttempt({
		...options,
		dispatcher: params.dispatcher,
		delivery,
		getStats: () => acpManager.getObservabilitySnapshot(),
		sessionKey,
		startedAt: acpDispatchStartedAt,
		recordProcessed: params.recordProcessed,
		markIdle: params.markIdle
	});
	const requestId = resolveAcpRequestId(params.ctx);
	const existingRunId = normalizeOptionalString(params.runId);
	const auditOnly = existingRunId === void 0;
	let completionSource;
	const auditRunId = existingRunId ?? generateSecureUuid();
	const auditRuntime = await loadDispatchAcpAuditRuntime();
	const auditToolTracker = auditRuntime.createAcpToolLifecycleTracker();
	let auditStarted = false;
	let auditFinished = false;
	let auditTerminalOutcome;
	let auditStopReason;
	let auditResultStatus;
	let runtimeTurnWasCancelled = false;
	let assistantTranscript;
	let terminalOutcome;
	let auditEndFields;
	const resolveAuditEndFields = () => auditEndFields ??= auditRuntime.resolveAcpLifecycleEndFields(params.abortSignal, auditStopReason, auditResultStatus);
	const emitAuditStart = () => {
		if (auditStarted) return;
		auditStarted = true;
		const completionOwner = params.onAgentRunStart?.(auditRunId, void 0, {
			completionSource: "reply-dispatch",
			getResult: () => ({
				assistantTranscript,
				terminalOutcome
			})
		});
		completionSource = completionOwner === "reply-dispatch" ? completionOwner : void 0;
		auditRuntime.emitAcpLifecycleStart({
			runId: auditRunId,
			sessionKey: canonicalSessionKey,
			agentId: acpAgentId,
			startedAt: Date.now(),
			auditOnly,
			completionSource
		});
	};
	const emitAuditEnd = () => {
		if (auditFinished) return;
		emitAuditStart();
		auditFinished = true;
		terminalOutcome = auditRuntime.emitAcpLifecycleEnd({
			runId: auditRunId,
			toolTracker: auditToolTracker,
			sessionKey: canonicalSessionKey,
			agentId: acpAgentId,
			endFields: resolveAuditEndFields(),
			auditOnly,
			completionSource
		});
	};
	const emitAuditError = (error) => {
		if (auditFinished) return;
		emitAuditStart();
		auditFinished = true;
		terminalOutcome = auditRuntime.emitAcpLifecycleError({
			runId: auditRunId,
			toolTracker: auditToolTracker,
			sessionKey: canonicalSessionKey,
			agentId: acpAgentId,
			...params.abortSignal ? { abortSignal: params.abortSignal } : {},
			...auditTerminalOutcome ? { terminalOutcome: auditTerminalOutcome } : {},
			auditOnly,
			completionSource,
			error
		});
	};
	let transcriptPromptText = "";
	let turnDispatched = false;
	let transcriptPersistenceAttempted = false;
	const persistTranscript = async (finalText) => {
		if (transcriptPersistenceAttempted) return;
		transcriptPersistenceAttempted = true;
		terminalOutcome ??= buildAgentRunTerminalOutcomeFromLifecycleEvent({
			phase: "end",
			data: resolveAuditEndFields()
		});
		const { persistAcpDispatchTranscript } = await loadDispatchAcpTranscriptRuntime();
		assistantTranscript = await persistAcpDispatchTranscript({
			cfg: params.cfg,
			sessionKey: canonicalSessionKey,
			agentId: acpAgentId,
			expectedSessionId: transcriptSessionId,
			promptText: transcriptPromptText,
			finalText,
			terminalOutcome,
			meta: acpResolution.kind === "ready" ? acpResolution.meta : void 0,
			threadId: params.ctx.MessageThreadId,
			userTurnTranscriptRecorder: params.userTurnTranscriptRecorder,
			prepareAssistantTranscriptMessage: params.prepareAssistantTranscriptMessage,
			assistantIdempotencyKey: existingRunId
		});
	};
	let admittedRunContext;
	let nativeActionEvidenceRecorded = false;
	const recordUnsupportedNativeActionEvidence = () => {
		if (nativeActionEvidenceRecorded) return;
		nativeActionEvidenceRecorded = true;
		recordRuntimeActionDecision({
			token: admittedRunContext?.executionIdentityToken,
			family: "native-runtime",
			operation: "action-evidence",
			outcome: "not-applicable",
			coverageState: "unsupported",
			reasonCode: "native_action_callback_unsupported",
			owner: "acp-runtime",
			decisionBoundary: "acp-runtime.prompt-submitted",
			summary: "ACP runtime action evidence is unsupported because the adapter exposes no authoritative native-action callback.",
			missingEvidence: ["native.action_callback"],
			remediation: [{
				code: "instrument_native_action_callback",
				text: "Instrument an authoritative native-action callback in the ACP adapter before claiming action evidence."
			}]
		});
	};
	try {
		const dispatchPolicyError = resolveAcpDispatchPolicyError(params.cfg);
		if (dispatchPolicyError) {
			auditTerminalOutcome = "blocked";
			throw dispatchPolicyError;
		}
		if (isRestrictiveRuntimeToolsAllow(params.toolsAllow) || toolPolicyRestrictsTools(params.ctx.ConversationToolPolicy)) {
			auditTerminalOutcome = "blocked";
			throw new AcpRuntimeError("ACP_DISPATCH_DISABLED", "This session's bound runtime cannot enforce its permission or tool policy; use an embedded runtime for this restricted conversation.");
		}
		if (acpResolution.kind === "stale") {
			emitAuditError(acpResolution.error);
			await maybeUnbindStaleBoundConversations({
				targetSessionKey: canonicalSessionKey,
				error: acpResolution.error
			});
			return finishAttempt({
				queuedFinal: await delivery.deliver("final", {
					text: formatAcpRuntimeErrorText(acpResolution.error),
					isError: true
				}),
				outcome: {
					kind: "error",
					error: acpResolution.error
				}
			});
		}
		const agentPolicyError = resolveAcpAgentPolicyError(params.cfg, resolvedAcpAgent);
		if (agentPolicyError) {
			auditTerminalOutcome = "blocked";
			throw agentPolicyError;
		}
		const resolvedTurnAttachments = await resolveAgentTurnAttachments({
			ctx: params.ctx,
			cfg: params.cfg,
			includeAttachmentIndexes: true
		});
		let extractedFileImages = params.extractedFileImages ?? [];
		if (hasInboundMediaForUnderstanding(params.ctx) && !params.ctx.MediaUnderstanding?.length) try {
			const { applyMediaUnderstanding } = await loadAgentTurnMediaRuntime();
			const mediaResult = await applyMediaUnderstanding({
				ctx: params.ctx,
				cfg: params.cfg,
				deliveredImageIndexes: new Set(resolvedTurnAttachments.attachmentIndexes ?? []),
				agentId: acpAgentId,
				agentDir: resolveAgentDir(params.cfg, acpAgentId),
				workspaceDir: resolveAgentWorkspaceDir(params.cfg, acpAgentId)
			});
			if (mediaResult.extractedFileImages.length > 0) extractedFileImages = [...extractedFileImages, ...mediaResult.extractedFileImages];
		} catch (err) {
			logVerbose(`dispatch-acp: media understanding failed, proceeding with raw content: ${formatErrorMessage(err)}`);
		}
		const promptText = params.ctx.agentText.trim();
		const describedImageIndexes = collectDescribedImageAttachmentIndexes(params.ctx);
		const recentHistoryStart = resolvedTurnAttachments.attachments.length - resolvedTurnAttachments.recentHistoryImages.length;
		const mediaAttachmentEntries = resolvedTurnAttachments.attachments.flatMap((attachment, index) => {
			const sourceIndex = resolvedTurnAttachments.attachmentIndexes?.[index];
			return sourceIndex !== void 0 && !describedImageIndexes.has(sourceIndex) && (describedImageIndexes.size === 0 || index < recentHistoryStart) ? [{
				attachment,
				sourceIndex
			}] : [];
		});
		const mediaAttachments = mediaAttachmentEntries.map((entry) => entry.attachment);
		const recentHistoryImages = describedImageIndexes.size === 0 ? resolvedTurnAttachments.recentHistoryImages : [];
		const inlineAttachments = resolveInlineAgentImageAttachments(params.images);
		const extractedAttachments = resolveInlineAgentImageAttachments(extractedFileImages.map(stripExtractedFileImageMetadata));
		const useMediaAttachments = mediaAttachments.length > 0 && !(mediaAttachments.length === recentHistoryImages.length && (inlineAttachments.length > 0 || extractedAttachments.length > 0));
		const attachmentEntries = [];
		if (useMediaAttachments) appendOrderedAcpAttachments({
			entries: attachmentEntries,
			attachments: mediaAttachments,
			sourceIndexes: mediaAttachmentEntries.map((entry) => entry.sourceIndex)
		});
		else appendOrderedAcpAttachments({
			entries: attachmentEntries,
			attachments: inlineAttachments
		});
		appendOrderedAcpAttachments({
			entries: attachmentEntries,
			attachments: extractedAttachments,
			sourceIndexes: extractedFileImages.map((image) => image.attachmentIndex)
		});
		const attachments = resolveMergedAcpAttachments(attachmentEntries);
		const turnPromptText = useMediaAttachments ? appendRecentHistoryImageContext({
			promptText,
			images: recentHistoryImages
		}) : promptText;
		transcriptPromptText = turnPromptText;
		if (!turnPromptText && attachments.length === 0) {
			const counts = params.dispatcher.getQueuedCounts();
			delivery.applyRoutedCounts(counts);
			params.recordProcessed("completed", { reason: "acp_empty_prompt" });
			params.markIdle("message_completed");
			return {
				queuedFinal: false,
				counts
			};
		}
		emitAuditStart();
		try {
			await delivery.startReplyLifecycle();
		} catch (error) {
			logVerbose(`dispatch-acp: start reply lifecycle failed: ${formatErrorMessage(error)}`);
		}
		admittedRunContext = await prepareChannelRunAdmission({
			cfg: params.cfg,
			runId: requestId,
			agentId: acpAgentId,
			ingressKind: "acp",
			boundary: "auto-reply.acp",
			evidence: readChannelContextAdmissionEvidence(params.ctx),
			gatewayLocalUserIngress: getGatewayLocalUserIngress(params.ctx)
		}).admit("acp");
		recordAcceptedSessionParticipantInput(params.ctx, participantTarget);
		const turnAdmission = admittedRunContext;
		const onElicitation = createLazyAcpElicitationHandler({
			sourceSessionKey: sessionKey,
			targetSessionKey: canonicalSessionKey,
			outerRequestId: requestId,
			agentId: acpAgentId,
			runId: auditRunId,
			delivery,
			isActive: () => params.abortSignal?.aborted !== true && admittedRunContext === turnAdmission && getAdmittedRunDelegatedAuthority(turnAdmission) !== void 0
		});
		await persistInput?.();
		await assertPreparedConversationBindingRouteCurrent(params.ctx);
		assertInputCurrent();
		if (getAdmittedRunDelegatedAuthority(turnAdmission) === void 0) throw new Error("ACP turn admission ended before input dispatch.");
		turnDispatched = true;
		await acpManager.runTurn({
			admittedRunContext,
			cfg: params.cfg,
			sessionKey: canonicalSessionKey,
			agentId: acpAgentId,
			provenance: classifySessionStateActor({
				inputProvenance: params.ctx.InputProvenance,
				sessionEffects: params.ctx.InboundEventKind === "room_event" ? "internal" : "visible"
			}).actorType,
			text: resolveAcpTurnText({
				promptText: turnPromptText,
				sourceReplyDeliveryMode: params.sourceReplyDeliveryMode
			}),
			attachments: attachments.length > 0 ? attachments : void 0,
			mode: "prompt",
			requestId,
			...params.abortSignal ? { signal: params.abortSignal } : {},
			onElicitation,
			onLifecycle: recordUnsupportedNativeActionEvidence,
			onEvent: async (event) => {
				auditRuntime.emitAcpRuntimeEvent({
					runId: auditRunId,
					toolTracker: auditToolTracker,
					sessionKey: canonicalSessionKey,
					agentId: acpAgentId,
					...params.abortSignal ? { abortSignal: params.abortSignal } : {},
					auditOnly,
					event
				});
				if (event.type === "done") {
					auditStopReason = event.stopReason;
					auditResultStatus = event.status;
					runtimeTurnWasCancelled = event.status === "cancelled";
				}
				await projector.onEvent(event);
			}
		});
		await projector.flush(true);
		await delivery.flushBlockText();
		if (!runtimeTurnWasCancelled && !params.abortSignal?.aborted) queuedFinal = await finalizeAcpTurnOutput({
			cfg: params.cfg,
			sessionKey: canonicalSessionKey,
			agentId: acpAgentId,
			delivery,
			inboundAudio: params.inboundAudio,
			sessionTtsAuto: params.sessionTtsAuto,
			ttsChannel: params.ttsChannel,
			ttsAccountId: effectiveDispatchAccountId,
			shouldDeferVisibleTextForTts,
			shouldEmitResolvedIdentityNotice,
			abortSignal: params.abortSignal
		}) || queuedFinal;
		if (runtimeTurnWasCancelled || params.abortSignal?.aborted) {
			queuedFinal = await deliverDeferredTextFallback() || queuedFinal;
			await persistTranscript(await delivery.resolveAccumulatedDeliveredTranscriptText());
			queuedFinal = delivery.hasPendingAnswerDelivery() || delivery.hasPendingFinalTtsMedia() || delivery.hasDeliveredFinalReply() || queuedFinal;
			const counts = params.dispatcher.getQueuedCounts();
			delivery.applyRoutedCounts(counts);
			params.recordProcessed("completed", { reason: "acp_aborted" });
			params.markIdle("message_aborted");
			emitAuditEnd();
			return {
				queuedFinal,
				counts
			};
		}
		await persistTranscript(delivery.getAccumulatedTranscriptText());
		const result = finishAttempt({
			queuedFinal,
			outcome: { kind: "ok" }
		});
		emitAuditEnd();
		return result;
	} catch (err) {
		const acpError = toAcpRuntimeError({
			error: err,
			fallbackCode: "ACP_TURN_FAILED",
			fallbackMessage: "ACP turn failed before completion."
		});
		emitAuditError(acpError);
		await projector.flush(true);
		await delivery.flushBlockText();
		queuedFinal = await deliverDeferredTextFallback() || queuedFinal;
		await maybeUnbindStaleBoundConversations({
			targetSessionKey: canonicalSessionKey,
			error: acpError
		});
		const errorText = formatAcpRuntimeErrorText(acpError);
		const partialText = delivery.getAccumulatedTranscriptText();
		const delivered = await delivery.deliver("final", {
			text: errorText,
			isError: true
		});
		if (turnDispatched) await persistTranscript(partialText ? `${partialText}\n\n${errorText}` : errorText);
		queuedFinal = queuedFinal || delivered;
		return finishAttempt({
			queuedFinal,
			outcome: {
				kind: "error",
				error: acpError
			}
		});
	} finally {
		if (admittedRunContext) closeAdmittedRunDelegatedAuthority(admittedRunContext);
	}
}
//#endregion
export { tryDispatchAcpReplyCore };