import { c as defaultRuntime } from "./subsystem-_lp1q2Mi.js";
import { C as resolveSendPolicy, Pt as loadSessionEntry, Un as resolveUserTimezone, tt as formatZonedTimestamp, vr as onAgentEvent, xr as resolveAgentIdentity, yr as registerAgentRunContext } from "./loader-364nXh92.js";
import { r as loadConfig } from "./config-VoJVjSgT.js";
import { F as resolveAgentIdFromSessionKey, N as normalizeAgentId, c as resolveDefaultAgentId, s as resolveAgentWorkspaceDir, t as listAgentIds } from "./agent-scope-u760_XKU.js";
import { Bt as ErrorCodes, Vt as errorShape, a as validateAgentWaitParams, i as validateAgentParams, n as formatValidationErrors, r as validateAgentIdentityParams } from "./client-BdG39O7o.js";
import { i as isGatewayMessageChannel, n as isDeliverableMessageChannel, t as INTERNAL_MESSAGE_CHANNEL, u as normalizeMessageChannel } from "./message-channel-B4I_vLJb.js";
import { O as resolveAgentMainSessionKey, ct as normalizeSessionDeliveryFields, g as updateSessionStore, k as resolveExplicitAgentSessionKey } from "./sandbox-Cnho92pr.js";
import { m as detectMime } from "./routes--IwjIOyE.js";
import { a as loadAgentIdentity } from "./agents.config-BoatZ0qi.js";
import { n as resolveAssistantAvatarUrl } from "./control-ui-shared-Cck-xUYB.js";
import { i as resolveAgentOutboundTarget, r as resolveAgentDeliveryPlan, t as agentCommand } from "./agent-AE36cAfG.js";
import { t as formatForLog } from "./ws-log-QHwbnBaO.js";
import { randomUUID } from "node:crypto";

//#region ../../src/gateway/assistant-identity.ts
const MAX_ASSISTANT_NAME = 50;
const MAX_ASSISTANT_AVATAR = 200;
const MAX_ASSISTANT_EMOJI = 16;
const DEFAULT_ASSISTANT_IDENTITY = {
	agentId: "main",
	name: "Assistant",
	avatar: "A"
};
function coerceIdentityValue(value, maxLength) {
	if (typeof value !== "string") return;
	const trimmed = value.trim();
	if (!trimmed) return;
	if (trimmed.length <= maxLength) return trimmed;
	return trimmed.slice(0, maxLength);
}
function isAvatarUrl(value) {
	return /^https?:\/\//i.test(value) || /^data:image\//i.test(value);
}
function looksLikeAvatarPath(value) {
	if (/[\\/]/.test(value)) return true;
	return /\.(png|jpe?g|gif|webp|svg|ico)$/i.test(value);
}
function normalizeAvatarValue(value) {
	if (!value) return;
	const trimmed = value.trim();
	if (!trimmed) return;
	if (isAvatarUrl(trimmed)) return trimmed;
	if (looksLikeAvatarPath(trimmed)) return trimmed;
	if (!/\s/.test(trimmed) && trimmed.length <= 4) return trimmed;
}
function normalizeEmojiValue(value) {
	if (!value) return;
	const trimmed = value.trim();
	if (!trimmed) return;
	if (trimmed.length > MAX_ASSISTANT_EMOJI) return;
	let hasNonAscii = false;
	for (let i = 0; i < trimmed.length; i += 1) if (trimmed.charCodeAt(i) > 127) {
		hasNonAscii = true;
		break;
	}
	if (!hasNonAscii) return;
	if (isAvatarUrl(trimmed) || looksLikeAvatarPath(trimmed)) return;
	return trimmed;
}
function resolveAssistantIdentity(params) {
	const agentId = normalizeAgentId(params.agentId ?? resolveDefaultAgentId(params.cfg));
	const workspaceDir = params.workspaceDir ?? resolveAgentWorkspaceDir(params.cfg, agentId);
	const configAssistant = params.cfg.ui?.assistant;
	const agentIdentity = resolveAgentIdentity(params.cfg, agentId);
	const fileIdentity = workspaceDir ? loadAgentIdentity(workspaceDir) : null;
	return {
		agentId,
		name: coerceIdentityValue(configAssistant?.name, MAX_ASSISTANT_NAME) ?? coerceIdentityValue(agentIdentity?.name, MAX_ASSISTANT_NAME) ?? coerceIdentityValue(fileIdentity?.name, MAX_ASSISTANT_NAME) ?? DEFAULT_ASSISTANT_IDENTITY.name,
		avatar: [
			coerceIdentityValue(configAssistant?.avatar, MAX_ASSISTANT_AVATAR),
			coerceIdentityValue(agentIdentity?.avatar, MAX_ASSISTANT_AVATAR),
			coerceIdentityValue(agentIdentity?.emoji, MAX_ASSISTANT_AVATAR),
			coerceIdentityValue(fileIdentity?.avatar, MAX_ASSISTANT_AVATAR),
			coerceIdentityValue(fileIdentity?.emoji, MAX_ASSISTANT_AVATAR)
		].map((candidate) => normalizeAvatarValue(candidate)).find(Boolean) ?? DEFAULT_ASSISTANT_IDENTITY.avatar,
		emoji: [
			coerceIdentityValue(agentIdentity?.emoji, MAX_ASSISTANT_EMOJI),
			coerceIdentityValue(fileIdentity?.emoji, MAX_ASSISTANT_EMOJI),
			coerceIdentityValue(agentIdentity?.avatar, MAX_ASSISTANT_EMOJI),
			coerceIdentityValue(fileIdentity?.avatar, MAX_ASSISTANT_EMOJI)
		].map((candidate) => normalizeEmojiValue(candidate)).find(Boolean)
	};
}

//#endregion
//#region ../../src/gateway/chat-attachments.ts
function normalizeMime(mime) {
	if (!mime) return;
	return mime.split(";")[0]?.trim().toLowerCase() || void 0;
}
async function sniffMimeFromBase64(base64) {
	const trimmed = base64.trim();
	if (!trimmed) return;
	const take = Math.min(256, trimmed.length);
	const sliceLen = take - take % 4;
	if (sliceLen < 8) return;
	try {
		return await detectMime({ buffer: Buffer.from(trimmed.slice(0, sliceLen), "base64") });
	} catch {
		return;
	}
}
function isImageMime(mime) {
	return typeof mime === "string" && mime.startsWith("image/");
}
/**
* Parse attachments and extract images as structured content blocks.
* Returns the message text and an array of image content blocks
* compatible with Claude API's image format.
*/
async function parseMessageWithAttachments(message, attachments, opts) {
	const maxBytes = opts?.maxBytes ?? 5e6;
	const log = opts?.log;
	if (!attachments || attachments.length === 0) return {
		message,
		images: []
	};
	const images = [];
	for (const [idx, att] of attachments.entries()) {
		if (!att) continue;
		const mime = att.mimeType ?? "";
		const content = att.content;
		const label = att.fileName || att.type || `attachment-${idx + 1}`;
		if (typeof content !== "string") throw new Error(`attachment ${label}: content must be base64 string`);
		let sizeBytes = 0;
		let b64 = content.trim();
		const dataUrlMatch = /^data:[^;]+;base64,(.*)$/.exec(b64);
		if (dataUrlMatch) b64 = dataUrlMatch[1];
		if (b64.length % 4 !== 0 || /[^A-Za-z0-9+/=]/.test(b64)) throw new Error(`attachment ${label}: invalid base64 content`);
		try {
			sizeBytes = Buffer.from(b64, "base64").byteLength;
		} catch {
			throw new Error(`attachment ${label}: invalid base64 content`);
		}
		if (sizeBytes <= 0 || sizeBytes > maxBytes) throw new Error(`attachment ${label}: exceeds size limit (${sizeBytes} > ${maxBytes} bytes)`);
		const providedMime = normalizeMime(mime);
		const sniffedMime = normalizeMime(await sniffMimeFromBase64(b64));
		if (sniffedMime && !isImageMime(sniffedMime)) {
			log?.warn(`attachment ${label}: detected non-image (${sniffedMime}), dropping`);
			continue;
		}
		if (!sniffedMime && !isImageMime(providedMime)) {
			log?.warn(`attachment ${label}: unable to detect image mime type, dropping`);
			continue;
		}
		if (sniffedMime && providedMime && sniffedMime !== providedMime) log?.warn(`attachment ${label}: mime mismatch (${providedMime} -> ${sniffedMime}), using sniffed`);
		images.push({
			type: "image",
			data: b64,
			mimeType: sniffedMime ?? providedMime ?? mime
		});
	}
	return {
		message,
		images
	};
}

//#endregion
//#region ../../src/gateway/server-methods/agent-job.ts
const AGENT_RUN_CACHE_TTL_MS = 10 * 6e4;
const agentRunCache = /* @__PURE__ */ new Map();
const agentRunStarts = /* @__PURE__ */ new Map();
let agentRunListenerStarted = false;
function pruneAgentRunCache(now = Date.now()) {
	for (const [runId, entry] of agentRunCache) if (now - entry.ts > AGENT_RUN_CACHE_TTL_MS) agentRunCache.delete(runId);
}
function recordAgentRunSnapshot(entry) {
	pruneAgentRunCache(entry.ts);
	agentRunCache.set(entry.runId, entry);
}
function ensureAgentRunListener() {
	if (agentRunListenerStarted) return;
	agentRunListenerStarted = true;
	onAgentEvent((evt) => {
		if (!evt) return;
		if (evt.stream !== "lifecycle") return;
		const phase = evt.data?.phase;
		if (phase === "start") {
			const startedAt = typeof evt.data?.startedAt === "number" ? evt.data.startedAt : void 0;
			agentRunStarts.set(evt.runId, startedAt ?? Date.now());
			return;
		}
		if (phase !== "end" && phase !== "error") return;
		const startedAt = typeof evt.data?.startedAt === "number" ? evt.data.startedAt : agentRunStarts.get(evt.runId);
		const endedAt = typeof evt.data?.endedAt === "number" ? evt.data.endedAt : void 0;
		const error = typeof evt.data?.error === "string" ? evt.data.error : void 0;
		agentRunStarts.delete(evt.runId);
		recordAgentRunSnapshot({
			runId: evt.runId,
			status: phase === "error" ? "error" : "ok",
			startedAt,
			endedAt,
			error,
			ts: Date.now()
		});
	});
}
function getCachedAgentRun(runId) {
	pruneAgentRunCache();
	return agentRunCache.get(runId);
}
async function waitForAgentJob(params) {
	const { runId, timeoutMs } = params;
	ensureAgentRunListener();
	const cached = getCachedAgentRun(runId);
	if (cached) return cached;
	if (timeoutMs <= 0) return null;
	return await new Promise((resolve) => {
		let settled = false;
		const finish = (entry) => {
			if (settled) return;
			settled = true;
			clearTimeout(timer);
			unsubscribe();
			resolve(entry);
		};
		const unsubscribe = onAgentEvent((evt) => {
			if (!evt || evt.stream !== "lifecycle") return;
			if (evt.runId !== runId) return;
			const phase = evt.data?.phase;
			if (phase !== "end" && phase !== "error") return;
			const cached = getCachedAgentRun(runId);
			if (cached) {
				finish(cached);
				return;
			}
			const startedAt = typeof evt.data?.startedAt === "number" ? evt.data.startedAt : agentRunStarts.get(evt.runId);
			const endedAt = typeof evt.data?.endedAt === "number" ? evt.data.endedAt : void 0;
			const error = typeof evt.data?.error === "string" ? evt.data.error : void 0;
			const snapshot = {
				runId: evt.runId,
				status: phase === "error" ? "error" : "ok",
				startedAt,
				endedAt,
				error,
				ts: Date.now()
			};
			recordAgentRunSnapshot(snapshot);
			finish(snapshot);
		});
		const timer = setTimeout(() => finish(null), Math.max(1, timeoutMs));
	});
}
ensureAgentRunListener();

//#endregion
//#region ../../src/gateway/server-methods/agent-timestamp.ts
/**
* Cron jobs inject "Current time: ..." into their messages.
* Skip injection for those.
*/
const CRON_TIME_PATTERN = /Current time: /;
/**
* Matches a leading `[... YYYY-MM-DD HH:MM ...]` envelope — either from
* channel plugins or from a previous injection. Uses the same YYYY-MM-DD
* HH:MM format as {@link formatZonedTimestamp}, so detection stays in sync
* with the formatting.
*/
const TIMESTAMP_ENVELOPE_PATTERN = /^\[.*\d{4}-\d{2}-\d{2} \d{2}:\d{2}/;
/**
* Injects a compact timestamp prefix into a message if one isn't already
* present. Uses the same `YYYY-MM-DD HH:MM TZ` format as channel envelope
* timestamps ({@link formatZonedTimestamp}), keeping token cost low (~7
* tokens) and format consistent across all agent contexts.
*
* Used by the gateway `agent` and `chat.send` handlers to give TUI, web,
* spawned subagents, `sessions_send`, and heartbeat wake events date/time
* awareness — without modifying the system prompt (which is cached).
*
* Channel messages (Discord, Telegram, etc.) already have timestamps via
* envelope formatting and take a separate code path — they never reach
* these handlers, so there is no double-stamping risk. The detection
* pattern is a safety net for edge cases.
*
* @see https://github.com/moltbot/moltbot/issues/3658
*/
function injectTimestamp(message, opts) {
	if (!message.trim()) return message;
	if (TIMESTAMP_ENVELOPE_PATTERN.test(message)) return message;
	if (CRON_TIME_PATTERN.test(message)) return message;
	const now = opts?.now ?? /* @__PURE__ */ new Date();
	const timezone = opts?.timezone ?? "UTC";
	const formatted = formatZonedTimestamp(now, timezone);
	if (!formatted) return message;
	return `[${new Intl.DateTimeFormat("en-US", {
		timeZone: timezone,
		weekday: "short"
	}).format(now)} ${formatted}] ${message}`;
}
/**
* Build TimestampInjectionOptions from an EdwinPAIConfig.
*/
function timestampOptsFromConfig(cfg) {
	return { timezone: resolveUserTimezone(cfg.agents?.defaults?.userTimezone) };
}

//#endregion
//#region ../../src/gateway/server-methods/agent.ts
const agentHandlers = {
	agent: async ({ params, respond, context }) => {
		const p = params;
		if (!validateAgentParams(p)) {
			respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid agent params: ${formatValidationErrors(validateAgentParams.errors)}`));
			return;
		}
		const request = p;
		const cfg = loadConfig();
		const idem = request.idempotencyKey;
		const groupIdRaw = typeof request.groupId === "string" ? request.groupId.trim() : "";
		const groupChannelRaw = typeof request.groupChannel === "string" ? request.groupChannel.trim() : "";
		const groupSpaceRaw = typeof request.groupSpace === "string" ? request.groupSpace.trim() : "";
		let resolvedGroupId = groupIdRaw || void 0;
		let resolvedGroupChannel = groupChannelRaw || void 0;
		let resolvedGroupSpace = groupSpaceRaw || void 0;
		let spawnedByValue = typeof request.spawnedBy === "string" ? request.spawnedBy.trim() : void 0;
		const cached = context.dedupe.get(`agent:${idem}`);
		if (cached) {
			respond(cached.ok, cached.payload, cached.error, { cached: true });
			return;
		}
		const normalizedAttachments = request.attachments?.map((a) => ({
			type: typeof a?.type === "string" ? a.type : void 0,
			mimeType: typeof a?.mimeType === "string" ? a.mimeType : void 0,
			fileName: typeof a?.fileName === "string" ? a.fileName : void 0,
			content: typeof a?.content === "string" ? a.content : ArrayBuffer.isView(a?.content) ? Buffer.from(a.content.buffer, a.content.byteOffset, a.content.byteLength).toString("base64") : void 0
		})).filter((a) => a.content) ?? [];
		let message = request.message.trim();
		let images = [];
		if (normalizedAttachments.length > 0) try {
			const parsed = await parseMessageWithAttachments(message, normalizedAttachments, {
				maxBytes: 5e6,
				log: context.logGateway
			});
			message = parsed.message.trim();
			images = parsed.images;
		} catch (err) {
			respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, String(err)));
			return;
		}
		message = injectTimestamp(message, timestampOptsFromConfig(cfg));
		const isKnownGatewayChannel = (value) => isGatewayMessageChannel(value);
		const channelHints = [request.channel, request.replyChannel].filter((value) => typeof value === "string").map((value) => value.trim()).filter(Boolean);
		for (const rawChannel of channelHints) {
			const normalized = normalizeMessageChannel(rawChannel);
			if (normalized && normalized !== "last" && !isKnownGatewayChannel(normalized)) {
				respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid agent params: unknown channel: ${String(normalized)}`));
				return;
			}
		}
		const agentIdRaw = typeof request.agentId === "string" ? request.agentId.trim() : "";
		const agentId = agentIdRaw ? normalizeAgentId(agentIdRaw) : void 0;
		if (agentId) {
			if (!listAgentIds(cfg).includes(agentId)) {
				respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid agent params: unknown agent id "${request.agentId}"`));
				return;
			}
		}
		const requestedSessionKeyRaw = typeof request.sessionKey === "string" && request.sessionKey.trim() ? request.sessionKey.trim() : void 0;
		const requestedSessionKey = requestedSessionKeyRaw ?? resolveExplicitAgentSessionKey({
			cfg,
			agentId
		});
		if (agentId && requestedSessionKeyRaw) {
			const sessionAgentId = resolveAgentIdFromSessionKey(requestedSessionKeyRaw);
			if (sessionAgentId !== agentId) {
				respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid agent params: agent "${request.agentId}" does not match session key agent "${sessionAgentId}"`));
				return;
			}
		}
		let resolvedSessionId = request.sessionId?.trim() || void 0;
		let sessionEntry;
		let bestEffortDeliver = false;
		let cfgForAgent;
		if (requestedSessionKey) {
			const { cfg, storePath, entry, canonicalKey } = loadSessionEntry(requestedSessionKey);
			cfgForAgent = cfg;
			const now = Date.now();
			const sessionId = entry?.sessionId ?? randomUUID();
			const labelValue = request.label?.trim() || entry?.label;
			spawnedByValue = spawnedByValue || entry?.spawnedBy;
			let inheritedGroup;
			if (spawnedByValue && (!resolvedGroupId || !resolvedGroupChannel || !resolvedGroupSpace)) try {
				const parentEntry = loadSessionEntry(spawnedByValue)?.entry;
				inheritedGroup = {
					groupId: parentEntry?.groupId,
					groupChannel: parentEntry?.groupChannel,
					groupSpace: parentEntry?.space
				};
			} catch {
				inheritedGroup = void 0;
			}
			resolvedGroupId = resolvedGroupId || inheritedGroup?.groupId;
			resolvedGroupChannel = resolvedGroupChannel || inheritedGroup?.groupChannel;
			resolvedGroupSpace = resolvedGroupSpace || inheritedGroup?.groupSpace;
			const deliveryFields = normalizeSessionDeliveryFields(entry);
			const nextEntry = {
				sessionId,
				updatedAt: now,
				thinkingLevel: entry?.thinkingLevel,
				verboseLevel: entry?.verboseLevel,
				reasoningLevel: entry?.reasoningLevel,
				systemSent: entry?.systemSent,
				sendPolicy: entry?.sendPolicy,
				skillsSnapshot: entry?.skillsSnapshot,
				deliveryContext: deliveryFields.deliveryContext,
				lastChannel: deliveryFields.lastChannel ?? entry?.lastChannel,
				lastTo: deliveryFields.lastTo ?? entry?.lastTo,
				lastAccountId: deliveryFields.lastAccountId ?? entry?.lastAccountId,
				modelOverride: entry?.modelOverride,
				providerOverride: entry?.providerOverride,
				label: labelValue,
				spawnedBy: spawnedByValue,
				channel: entry?.channel ?? request.channel?.trim(),
				groupId: resolvedGroupId ?? entry?.groupId,
				groupChannel: resolvedGroupChannel ?? entry?.groupChannel,
				space: resolvedGroupSpace ?? entry?.space,
				cliSessionIds: entry?.cliSessionIds,
				claudeCliSessionId: entry?.claudeCliSessionId
			};
			sessionEntry = nextEntry;
			if (resolveSendPolicy({
				cfg,
				entry,
				sessionKey: requestedSessionKey,
				channel: entry?.channel,
				chatType: entry?.chatType
			}) === "deny") {
				respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "send blocked by session policy"));
				return;
			}
			resolvedSessionId = sessionId;
			const canonicalSessionKey = canonicalKey;
			const mainSessionKey = resolveAgentMainSessionKey({
				cfg,
				agentId: resolveAgentIdFromSessionKey(canonicalSessionKey)
			});
			if (storePath) await updateSessionStore(storePath, (store) => {
				store[canonicalSessionKey] = nextEntry;
			});
			if (canonicalSessionKey === mainSessionKey || canonicalSessionKey === "global") {
				context.addChatRun(idem, {
					sessionKey: requestedSessionKey,
					clientRunId: idem
				});
				bestEffortDeliver = true;
			}
			registerAgentRunContext(idem, { sessionKey: requestedSessionKey });
		}
		const runId = idem;
		const wantsDelivery = request.deliver === true;
		const explicitTo = typeof request.replyTo === "string" && request.replyTo.trim() ? request.replyTo.trim() : typeof request.to === "string" && request.to.trim() ? request.to.trim() : void 0;
		const explicitThreadId = typeof request.threadId === "string" && request.threadId.trim() ? request.threadId.trim() : void 0;
		const deliveryPlan = resolveAgentDeliveryPlan({
			sessionEntry,
			requestedChannel: request.replyChannel ?? request.channel,
			explicitTo,
			explicitThreadId,
			accountId: request.replyAccountId ?? request.accountId,
			wantsDelivery
		});
		const resolvedChannel = deliveryPlan.resolvedChannel;
		const deliveryTargetMode = deliveryPlan.deliveryTargetMode;
		const resolvedAccountId = deliveryPlan.resolvedAccountId;
		let resolvedTo = deliveryPlan.resolvedTo;
		if (!resolvedTo && isDeliverableMessageChannel(resolvedChannel)) {
			const fallback = resolveAgentOutboundTarget({
				cfg: cfgForAgent ?? cfg,
				plan: deliveryPlan,
				targetMode: "implicit",
				validateExplicitTarget: false
			});
			if (fallback.resolvedTarget?.ok) resolvedTo = fallback.resolvedTo;
		}
		const deliver = request.deliver === true && resolvedChannel !== INTERNAL_MESSAGE_CHANNEL;
		const accepted = {
			runId,
			status: "accepted",
			acceptedAt: Date.now()
		};
		context.dedupe.set(`agent:${idem}`, {
			ts: Date.now(),
			ok: true,
			payload: accepted
		});
		respond(true, accepted, void 0, { runId });
		const resolvedThreadId = explicitThreadId ?? deliveryPlan.resolvedThreadId;
		agentCommand({
			message,
			images,
			to: resolvedTo,
			sessionId: resolvedSessionId,
			sessionKey: requestedSessionKey,
			thinking: request.thinking,
			deliver,
			deliveryTargetMode,
			channel: resolvedChannel,
			accountId: resolvedAccountId,
			threadId: resolvedThreadId,
			runContext: {
				messageChannel: resolvedChannel,
				accountId: resolvedAccountId,
				groupId: resolvedGroupId,
				groupChannel: resolvedGroupChannel,
				groupSpace: resolvedGroupSpace,
				currentThreadTs: resolvedThreadId != null ? String(resolvedThreadId) : void 0
			},
			groupId: resolvedGroupId,
			groupChannel: resolvedGroupChannel,
			groupSpace: resolvedGroupSpace,
			spawnedBy: spawnedByValue,
			timeout: request.timeout?.toString(),
			bestEffortDeliver,
			messageChannel: resolvedChannel,
			runId,
			lane: request.lane,
			extraSystemPrompt: request.extraSystemPrompt
		}, defaultRuntime, context.deps).then((result) => {
			const payload = {
				runId,
				status: "ok",
				summary: "completed",
				result
			};
			context.dedupe.set(`agent:${idem}`, {
				ts: Date.now(),
				ok: true,
				payload
			});
			respond(true, payload, void 0, { runId });
		}).catch((err) => {
			const error = errorShape(ErrorCodes.UNAVAILABLE, String(err));
			const payload = {
				runId,
				status: "error",
				summary: String(err)
			};
			context.dedupe.set(`agent:${idem}`, {
				ts: Date.now(),
				ok: false,
				payload,
				error
			});
			respond(false, payload, error, {
				runId,
				error: formatForLog(err)
			});
		});
	},
	"agent.identity.get": ({ params, respond }) => {
		if (!validateAgentIdentityParams(params)) {
			respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid agent.identity.get params: ${formatValidationErrors(validateAgentIdentityParams.errors)}`));
			return;
		}
		const p = params;
		const agentIdRaw = typeof p.agentId === "string" ? p.agentId.trim() : "";
		const sessionKeyRaw = typeof p.sessionKey === "string" ? p.sessionKey.trim() : "";
		let agentId = agentIdRaw ? normalizeAgentId(agentIdRaw) : void 0;
		if (sessionKeyRaw) {
			const resolved = resolveAgentIdFromSessionKey(sessionKeyRaw);
			if (agentId && resolved !== agentId) {
				respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid agent.identity.get params: agent "${agentIdRaw}" does not match session key agent "${resolved}"`));
				return;
			}
			agentId = resolved;
		}
		const cfg = loadConfig();
		const identity = resolveAssistantIdentity({
			cfg,
			agentId
		});
		const avatarValue = resolveAssistantAvatarUrl({
			avatar: identity.avatar,
			agentId: identity.agentId,
			basePath: cfg.gateway?.controlUi?.basePath
		}) ?? identity.avatar;
		respond(true, {
			...identity,
			avatar: avatarValue
		}, void 0);
	},
	"agent.wait": async ({ params, respond }) => {
		if (!validateAgentWaitParams(params)) {
			respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid agent.wait params: ${formatValidationErrors(validateAgentWaitParams.errors)}`));
			return;
		}
		const p = params;
		const runId = p.runId.trim();
		const snapshot = await waitForAgentJob({
			runId,
			timeoutMs: typeof p.timeoutMs === "number" && Number.isFinite(p.timeoutMs) ? Math.max(0, Math.floor(p.timeoutMs)) : 3e4
		});
		if (!snapshot) {
			respond(true, {
				runId,
				status: "timeout"
			});
			return;
		}
		respond(true, {
			runId,
			status: snapshot.status,
			startedAt: snapshot.startedAt,
			endedAt: snapshot.endedAt,
			error: snapshot.error
		});
	}
};

//#endregion
export { parseMessageWithAttachments as i, injectTimestamp as n, timestampOptsFromConfig as r, agentHandlers as t };