import { o as subscribeAll } from "./event-log-DMRSRkMD.js";
import { randomUUID } from "node:crypto";

//#region src/sessions/koi-utterance-trigger.ts
/**
* Bridge: when a non-koi utterance lands on the session log, trigger the
* koi to start a turn. Same code path as `chat.send` — we invoke the
* existing handler with synthesized params instead of duplicating its
* 200-line setup. The session log thereby becomes the single canonical
* source of incoming user input; the runtime's chat-driven dispatch
* stays as the execution engine.
*
* Idempotency: each utterance event has a stable `eventId`, used as the
* chat.send idempotencyKey so duplicate events don't double-fire.
*
* Targeting: we honor the @-mention list on the utterance.
*   - No mentions          → broadcast: this koi fires.
*   - Mentions present, we are mentioned → fire.
*   - Mentions present, we are NOT mentioned → skip (a different koi is
*     being addressed). This is what makes multi-koi-per-session work
*     without every koi shouting over every message.
*
* "Are we mentioned" is resolved by remembering our own participantId
* per session — captured on the participant_join event whose externalId
* matches SKYKOI_KOI_ID. Until that join is observed, we fall back to
* broadcast behavior so single-koi sessions keep working.
*
* Filter: utterances from participants tagged "koi" / "sub-koi" / "tool"
* are skipped (those are koi outputs, not inputs). Utterances tagged as
* "synthesized" or with empty text are also skipped.
*/
/**
* Subscribe globally — every session created on this gateway routes
* its utterances through here. Runs as part of gateway bootstrap.
*
* Note: event-log's `subscribe()` is per-session, so we install a
* listener on each session lazily when its first event flows. To keep
* the implementation simple, we also tap onKoiEvent (which already
* fires globally) as the discovery path: each `KoiEventPayload`
* carries a sessionKey that we use to attach a per-session listener
* the first time we see it.
*/
function startKoiUtteranceTrigger(opts) {
	const wired = /* @__PURE__ */ new Set();
	const seenEventIds = /* @__PURE__ */ new Set();
	const TRACK_LIMIT = 5e3;
	const selfPidBySession = /* @__PURE__ */ new Map();
	const voiceContextBySession = /* @__PURE__ */ new Map();
	const dispatchUtterance = async (event) => {
		if (event.kind === "participant_join" && opts.selfKoiId) {
			const c = event.content || {};
			if (c.kind === "koi" && c.externalId === opts.selfKoiId && c.participantId) selfPidBySession.set(event.sessionId, c.participantId);
			return;
		}
		if (event.kind === "system") {
			const c = event.content || {};
			if (c.event === "voice_call_started") voiceContextBySession.set(event.sessionId, true);
			else if (c.event === "voice_call_ended") voiceContextBySession.delete(event.sessionId);
			return;
		}
		if (event.kind !== "utterance") return;
		if (new Set([
			"koi",
			"sub-koi",
			"tool",
			"voice-koi"
		]).has(event.participantId)) return;
		if (event.participantId.startsWith("koi:")) return;
		const source = event.source;
		if (source === "model" || source === "tool" || source === "synthesized" || source === "system") return;
		const text = ((event.content || {}).text || "").toString().trim();
		if (!text) return;
		const mentions = event.mentions || [];
		if (mentions.length > 0) {
			const selfPid = selfPidBySession.get(event.sessionId);
			if (!selfPid) return;
			if (!mentions.includes(selfPid)) return;
		}
		const dedupeKey = `${event.sessionId}:${event.eventId}`;
		if (seenEventIds.has(dedupeKey)) return;
		seenEventIds.add(dedupeKey);
		if (seenEventIds.size > TRACK_LIMIT) {
			const drop = seenEventIds.size - TRACK_LIMIT;
			const it = seenEventIds.values();
			for (let i = 0; i < drop; i++) seenEventIds.delete(it.next().value);
		}
		const requestId = randomUUID();
		const idempotencyKey = `session:${dedupeKey}`;
		const finalMessage = voiceContextBySession.get(event.sessionId) === true ? `(The user is on a voice call right now. Keep your reply short and conversational, no markdown, no code blocks, no raw URLs read aloud. If the response would be long or formatted, share the result through tools/chat instead of speaking it.)\n\n${text}` : text;
		const params = {
			sessionKey: event.sessionId,
			message: finalMessage,
			idempotencyKey
		};
		try {
			await opts.chatSendHandler({
				req: {
					type: "req",
					id: requestId,
					method: "chat.send",
					params
				},
				params,
				client: null,
				isWebchatConnect: () => false,
				respond: () => void 0,
				context: opts.context
			});
		} catch (err) {
			console.warn(`[session-trigger] chat.send dispatch failed: ${err?.message || String(err)}`);
		}
	};
	const offAll = subscribeAll((ev) => {
		dispatchUtterance(ev);
	});
	return () => {
		offAll();
		wired.clear();
		seenEventIds.clear();
		voiceContextBySession.clear();
	};
}

//#endregion
export { startKoiUtteranceTrigger };