import "./tool-display-DMBmPBI-.js";
import "./paths-WcYpcMAY.js";
import { _ as defaultRuntime } from "./entry.js";
import "./auth-profiles-CaNK0cUT.js";
import { d as normalizeMainKey } from "./session-key-Fto5gqqw.js";
import "./exec-CmvuUufz.js";
import { f as resolveSessionAgentId } from "./agent-scope-E2aR3Ci_.js";
import "./github-copilot-token-CKOQDjEZ.js";
import "./emitter-BKo8D7Oj.js";
import "./sqlite-CVrhPija.js";
import "./reliability-BnL75OlZ.js";
import "./pi-model-discovery-Cd63DV2B.js";
import "./model-nrbMzlbz.js";
import "./frontmatter-C3D7gE4Z.js";
import "./skills-UwTwDeDw.js";
import "./manifest-registry-AgGLF4V7.js";
import "./validation-B00oOkJ3.js";
import { o as loadConfig } from "./config-CoCeK5B2.js";
import "./client-DtQEs2_8.js";
import "./call-CkgY6Bq7.js";
import "./message-channel-DKXbq8GH.js";
import { gn as enqueueSystemEvent, rn as requestHeartbeatNow } from "./subagent-registry-BSfrC7gZ.js";
import { u as updateSessionStore } from "./sessions-CFJLtXRH.js";
import "./tokens-D-4TlWcT.js";
import "./heartbeat-XNOTz34R.js";
import "./accounts-CNwod9BY.js";
import "./accounts-C3i8BBiA.js";
import "./normalize-CRf-uoB2.js";
import "./bindings-B1hNZin3.js";
import "./helpers-C1I2eYI4.js";
import { g as resolveOutboundTarget } from "./allow-list-C1QL6XM_.js";
import "./logging-BiuZwq5D.js";
import "./client-BrCP9C3o.js";
import { r as normalizeChannelId } from "./plugins-DMpYx7kb.js";
import "./targets-DvKzWjKi.js";
import "./with-timeout-xvreNRIp.js";
import "./diagnostic-CvuUUG_n.js";
import "./diagnostic-session-state-Dom6CWV9.js";
import "./lanes-6YZC1uc5.js";
import "./reply-payloads-DHkZZC3s.js";
import "./route-reply-0Dr2Q7BS.js";
import "./tts-CLI3TzKv.js";
import "./image-ops-D0pBQqKh.js";
import "./audio-xR21p4sl.js";
import "./pi-embedded-helpers-D-q9ylFX.js";
import "./sandbox-C79rEJ_w.js";
import "./chrome-BTLLz_O2.js";
import "./tailscale-V4nwZFBO.js";
import "./auth-Beo6eR0R.js";
import "./server-context-T5wFkzWi.js";
import "./routes-Ch0tqbTH.js";
import "./redact-2qrQ_uJt.js";
import "./errors-BlUw40-o.js";
import "./paths-Cb76_eC_.js";
import "./ssrf-BEPhIfOk.js";
import "./store-Bz34pm8z.js";
import "./ports-BsKQWWi9.js";
import "./trash-Cn3CsF86.js";
import "./dock-BvRrtCWj.js";
import "./accounts-CdYdWklO.js";
import "./paths-CDTtU20P.js";
import "./tool-images-B4eVOjD0.js";
import "./thinking-C03RqJxq.js";
import "./models-config-By95gdZa.js";
import "./chat-trace-C0wGIaWO.js";
import "./response-prefix-template-C1xl3U-G.js";
import "./tool-tracking-CtNygH0q.js";
import "./memory-cli-85mo64bb.js";
import "./manager-CFXUPO5v.js";
import "./gemini-auth-dH8SvNUd.js";
import "./session-files-0TqN4M7r.js";
import "./retry-Bdf0L_dZ.js";
import "./hybrid-DSp5ZIQe.js";
import "./common-CxpUJZLb.js";
import "./send-BozJfiUK.js";
import "./chunk-CNObemR3.js";
import "./markdown-tables-Cex18ib0.js";
import "./fetch-guard-BLrk92iq.js";
import "./fetch-DFo2YRBT.js";
import "./media-PPft1pQs.js";
import "./ir-CRxad2DW.js";
import "./render-JkHfM4BD.js";
import "./commands-registry-Ckq8wOdS.js";
import "./sessions-helpers-NrlH9x7w.js";
import "./pi-embedded-utils-BCSBp2bL.js";
import "./external-content-pnoCpvzd.js";
import "./runner-Dbz36vkv.js";
import "./image-tool.helpers-Bb25eyJc.js";
import "./image-DnnIE6E3.js";
import "./model-catalog-c_sp3sxR.js";
import { c as resolveGatewaySessionStoreTarget, o as loadSessionEntry, s as pruneLegacyStoreKeys } from "./session-utils-CwXLwy_N.js";
import "./workspace-dirs-Dm1vImLQ.js";
import "./skill-commands-B6qVCgej.js";
import "./fetch-CbnJhXfl.js";
import "./pairing-store-BHSQgDEs.js";
import "./exec-approvals-DGBfDg6X.js";
import "./gateway-CWnGRD4E.js";
import "./nodes-utils-DKSV6jwI.js";
import "./session-cost-usage-COXOEQzN.js";
import "./typed-schema-NhH7oBv2.js";
import "./knowledge-graph-njh3pt-F.js";
import "./model-fallback-N2p6k-x8.js";
import "./send-DpN8GxWR.js";
import "./channel-activity-Renq827a.js";
import "./tables-BD7LbCl1.js";
import "./server-startup-users-Dzui320y.js";
import "./resolve-route-CjJJ3gIl.js";
import "./replies-f6OSxuwk.js";
import "./reply-prefix-CjtRvYlq.js";
import "./pi-tools.policy-CW2flz_o.js";
import "./send-CdF3YtrB.js";
import "./onboard-helpers-HmtMUhNQ.js";
import "./prompt-style-BNQCf62G.js";
import "./onboard-helpers-CMBWpQU-.js";
import "./outbound-attachment-BhyLC0RY.js";
import "./targets-Df3HJYGb.js";
import "./pairing-labels-B7JDegCR.js";
import "./send-oTQYKgod.js";
import "./send-BxB9sGGU.js";
import "./proxy-BWmn1C2E.js";
import "./links-BcL73qkF.js";
import "./cli-utils-CpDxBFOW.js";
import "./help-format-CSTLKmFP.js";
import "./progress-Dh4aWkCA.js";
import "./active-listener-Bqb34bVB.js";
import { t as deliverOutboundPayloads } from "./deliver-i2e9PiiC.js";
import "./delivery-queue-Db61Fz6r.js";
import { t as createOutboundSendDeps } from "./outbound-send-deps-BOpEOnYa.js";
import { a as normalizeRpcAttachmentsToChatAttachments, n as registerApnsToken, o as parseMessageWithAttachments, s as formatForLog } from "./push-apns-Dh_mDDQD.js";
import { t as agentCommand } from "./agent-DW-bfNgA.js";
import { randomUUID } from "node:crypto";

//#region src/gateway/server-core/server-node-events.ts
const MAX_EXEC_EVENT_OUTPUT_CHARS = 180;
const VOICE_TRANSCRIPT_DEDUPE_WINDOW_MS = 1500;
const MAX_RECENT_VOICE_TRANSCRIPTS = 200;
const AGENT_REQUEST_DEDUPE_WINDOW_MS = 2e3;
const MAX_RECENT_AGENT_REQUESTS = 200;
const recentVoiceTranscripts = /* @__PURE__ */ new Map();
const recentAgentRequests = /* @__PURE__ */ new Map();
function normalizeNonEmptyString(value) {
	if (typeof value !== "string") return null;
	const trimmed = value.trim();
	return trimmed.length > 0 ? trimmed : null;
}
function normalizeFiniteInteger(value) {
	return typeof value === "number" && Number.isFinite(value) ? Math.trunc(value) : null;
}
function resolveVoiceTranscriptFingerprint(obj, text) {
	const eventId = normalizeNonEmptyString(obj.eventId) ?? normalizeNonEmptyString(obj.providerEventId) ?? normalizeNonEmptyString(obj.transcriptId);
	if (eventId) return `event:${eventId}`;
	const callId = normalizeNonEmptyString(obj.providerCallId) ?? normalizeNonEmptyString(obj.callId);
	const sequence = normalizeFiniteInteger(obj.sequence) ?? normalizeFiniteInteger(obj.seq);
	if (callId && sequence !== null) return `call-seq:${callId}:${sequence}`;
	const eventTimestamp = normalizeFiniteInteger(obj.timestamp) ?? normalizeFiniteInteger(obj.ts) ?? normalizeFiniteInteger(obj.eventTimestamp);
	if (callId && eventTimestamp !== null) return `call-ts:${callId}:${eventTimestamp}`;
	if (eventTimestamp !== null) return `timestamp:${eventTimestamp}|text:${text}`;
	return `text:${text}`;
}
function shouldDropDuplicateVoiceTranscript(params) {
	const previous = recentVoiceTranscripts.get(params.sessionKey);
	if (previous && previous.fingerprint === params.fingerprint && params.now - previous.ts <= VOICE_TRANSCRIPT_DEDUPE_WINDOW_MS) return true;
	recentVoiceTranscripts.set(params.sessionKey, {
		fingerprint: params.fingerprint,
		ts: params.now
	});
	if (recentVoiceTranscripts.size > MAX_RECENT_VOICE_TRANSCRIPTS) {
		const cutoff = params.now - VOICE_TRANSCRIPT_DEDUPE_WINDOW_MS * 2;
		for (const [key, value] of recentVoiceTranscripts) {
			if (value.ts < cutoff) recentVoiceTranscripts.delete(key);
			if (recentVoiceTranscripts.size <= MAX_RECENT_VOICE_TRANSCRIPTS) break;
		}
		while (recentVoiceTranscripts.size > MAX_RECENT_VOICE_TRANSCRIPTS) {
			const oldestKey = recentVoiceTranscripts.keys().next().value;
			if (oldestKey === void 0) break;
			recentVoiceTranscripts.delete(oldestKey);
		}
	}
	return false;
}
function resolveAgentRequestFingerprint(key, sessionKey, message) {
	if (key) return `key:${key}`;
	return `session:${sessionKey}|msg:${message.slice(0, 200)}`;
}
function shouldDropDuplicateAgentRequest(params) {
	const previous = recentAgentRequests.get(params.sessionKey);
	if (previous && previous.fingerprint === params.fingerprint && params.now - previous.ts <= AGENT_REQUEST_DEDUPE_WINDOW_MS) return true;
	recentAgentRequests.set(params.sessionKey, {
		fingerprint: params.fingerprint,
		ts: params.now
	});
	if (recentAgentRequests.size > MAX_RECENT_AGENT_REQUESTS) {
		const cutoff = params.now - AGENT_REQUEST_DEDUPE_WINDOW_MS * 2;
		for (const [key, value] of recentAgentRequests) {
			if (value.ts < cutoff) recentAgentRequests.delete(key);
			if (recentAgentRequests.size <= MAX_RECENT_AGENT_REQUESTS) break;
		}
		while (recentAgentRequests.size > MAX_RECENT_AGENT_REQUESTS) {
			const oldestKey = recentAgentRequests.keys().next().value;
			if (oldestKey === void 0) break;
			recentAgentRequests.delete(oldestKey);
		}
	}
	return false;
}
function compactExecEventOutput(raw) {
	const normalized = raw.replace(/\s+/g, " ").trim();
	if (!normalized) return "";
	if (normalized.length <= MAX_EXEC_EVENT_OUTPUT_CHARS) return normalized;
	const safe = Math.max(1, MAX_EXEC_EVENT_OUTPUT_CHARS - 1);
	return `${normalized.slice(0, safe)}…`;
}
async function touchSessionStore(params) {
	const { storePath } = params;
	if (!storePath) return;
	await updateSessionStore(storePath, (store) => {
		const target = resolveGatewaySessionStoreTarget({
			cfg: params.cfg,
			key: params.sessionKey,
			store
		});
		pruneLegacyStoreKeys({
			store,
			canonicalKey: target.canonicalKey,
			candidates: target.storeKeys
		});
		store[params.canonicalKey] = {
			sessionId: params.sessionId,
			updatedAt: params.now,
			thinkingLevel: params.entry?.thinkingLevel,
			verboseLevel: params.entry?.verboseLevel,
			reasoningLevel: params.entry?.reasoningLevel,
			systemSent: params.entry?.systemSent,
			sendPolicy: params.entry?.sendPolicy,
			lastChannel: params.entry?.lastChannel,
			lastTo: params.entry?.lastTo
		};
	});
}
function queueSessionStoreTouch(params) {
	touchSessionStore({
		cfg: params.cfg,
		sessionKey: params.sessionKey,
		storePath: params.storePath,
		canonicalKey: params.canonicalKey,
		entry: params.entry,
		sessionId: params.sessionId,
		now: params.now
	}).catch((err) => {
		params.ctx.logGateway.warn("voice session-store update failed: " + formatForLog(err));
	});
}
function parseSessionKeyFromPayloadJSON(payloadJSON) {
	let payload;
	try {
		payload = JSON.parse(payloadJSON);
	} catch {
		return null;
	}
	if (typeof payload !== "object" || payload === null) return null;
	const obj = payload;
	const sessionKey = typeof obj.sessionKey === "string" ? obj.sessionKey.trim() : "";
	return sessionKey.length > 0 ? sessionKey : null;
}
async function sendReceiptAck(params) {
	const resolved = resolveOutboundTarget({
		channel: params.channel,
		to: params.to,
		cfg: params.cfg,
		mode: "explicit"
	});
	if (!resolved.ok) throw new Error(String(resolved.error));
	const agentId = resolveSessionAgentId({
		sessionKey: params.sessionKey,
		config: params.cfg
	});
	await deliverOutboundPayloads({
		cfg: params.cfg,
		channel: params.channel,
		to: resolved.to,
		payloads: [{ text: params.text }],
		agentId,
		bestEffort: true,
		deps: createOutboundSendDeps(params.deps)
	});
}
const handleNodeEvent = async (ctx, nodeId, evt) => {
	switch (evt.event) {
		case "voice.transcript": {
			if (!evt.payloadJSON) return;
			let payload;
			try {
				payload = JSON.parse(evt.payloadJSON);
			} catch {
				return;
			}
			const obj = typeof payload === "object" && payload !== null ? payload : {};
			const text = typeof obj.text === "string" ? obj.text.trim() : "";
			if (!text) return;
			if (text.length > 2e4) return;
			const sessionKeyRaw = typeof obj.sessionKey === "string" ? obj.sessionKey.trim() : "";
			const cfg = loadConfig();
			const rawMainKey = normalizeMainKey(cfg.session?.mainKey);
			const sessionKey = sessionKeyRaw.length > 0 ? sessionKeyRaw : rawMainKey;
			const { storePath, entry, canonicalKey } = loadSessionEntry(sessionKey);
			const now = Date.now();
			if (shouldDropDuplicateVoiceTranscript({
				sessionKey: canonicalKey,
				fingerprint: resolveVoiceTranscriptFingerprint(obj, text),
				now
			})) return;
			const sessionId = entry?.sessionId ?? randomUUID();
			queueSessionStoreTouch({
				ctx,
				cfg,
				sessionKey,
				storePath,
				canonicalKey,
				entry,
				sessionId,
				now
			});
			ctx.addChatRun(sessionId, {
				sessionKey: canonicalKey,
				clientRunId: `voice-${randomUUID()}`
			});
			agentCommand({
				message: text,
				sessionId,
				sessionKey: canonicalKey,
				thinking: "low",
				deliver: false,
				messageChannel: "node",
				inputProvenance: {
					kind: "external_user",
					sourceChannel: "voice",
					sourceTool: "gateway.voice.transcript"
				}
			}, defaultRuntime, ctx.deps).catch((err) => {
				ctx.logGateway.warn(`agent failed node=${nodeId}: ${formatForLog(err)}`);
			});
			return;
		}
		case "agent.request": {
			if (!evt.payloadJSON) return;
			let link = null;
			try {
				link = JSON.parse(evt.payloadJSON);
			} catch {
				return;
			}
			let message = (link?.message ?? "").trim();
			const normalizedAttachments = normalizeRpcAttachmentsToChatAttachments(link?.attachments ?? void 0);
			let images = [];
			if (normalizedAttachments.length > 0) try {
				const parsed = await parseMessageWithAttachments(message, normalizedAttachments, {
					maxBytes: 5e6,
					log: ctx.logGateway
				});
				message = parsed.message.trim();
				images = parsed.images;
			} catch {
				return;
			}
			if (!message) return;
			if (message.length > 2e4) return;
			let channel = normalizeChannelId(typeof link?.channel === "string" ? link.channel.trim() : "") ?? void 0;
			let to = typeof link?.to === "string" && link.to.trim() ? link.to.trim() : void 0;
			const deliverRequested = Boolean(link?.deliver);
			const wantsReceipt = Boolean(link?.receipt);
			const receiptText = (typeof link?.receiptText === "string" ? link.receiptText.trim() : "") || "Just received your iOS share + request, working on it.";
			const sessionKeyRaw = (link?.sessionKey ?? "").trim();
			const sessionKey = sessionKeyRaw.length > 0 ? sessionKeyRaw : `node-${nodeId}`;
			const cfg = loadConfig();
			const { storePath, entry, canonicalKey } = loadSessionEntry(sessionKey);
			const now = Date.now();
			const sessionId = entry?.sessionId ?? randomUUID();
			if (shouldDropDuplicateAgentRequest({
				sessionKey: canonicalKey,
				fingerprint: resolveAgentRequestFingerprint(link?.key ?? null, canonicalKey, message),
				now
			})) return;
			await touchSessionStore({
				cfg,
				sessionKey,
				storePath,
				canonicalKey,
				entry,
				sessionId,
				now
			});
			if (deliverRequested && (!channel || !to)) {
				const entryChannel = typeof entry?.lastChannel === "string" ? normalizeChannelId(entry.lastChannel) : void 0;
				const entryTo = typeof entry?.lastTo === "string" ? entry.lastTo.trim() : "";
				if (!channel && entryChannel) channel = entryChannel;
				if (!to && entryTo) to = entryTo;
			}
			const deliver = deliverRequested && Boolean(channel && to);
			const deliveryChannel = deliver ? channel : void 0;
			const deliveryTo = deliver ? to : void 0;
			if (deliverRequested && !deliver) ctx.logGateway.warn(`agent delivery disabled node=${nodeId}: missing session delivery route (channel=${channel ?? "-"} to=${to ?? "-"})`);
			if (wantsReceipt && deliveryChannel && deliveryTo) sendReceiptAck({
				cfg,
				deps: ctx.deps,
				sessionKey: canonicalKey,
				channel: deliveryChannel,
				to: deliveryTo,
				text: receiptText
			}).catch((err) => {
				ctx.logGateway.warn(`agent receipt failed node=${nodeId}: ${formatForLog(err)}`);
			});
			else if (wantsReceipt) ctx.logGateway.warn(`agent receipt skipped node=${nodeId}: missing delivery route (channel=${deliveryChannel ?? "-"} to=${deliveryTo ?? "-"})`);
			agentCommand({
				message,
				images,
				sessionId,
				sessionKey: canonicalKey,
				thinking: link?.thinking ?? void 0,
				deliver,
				to: deliveryTo,
				channel: deliveryChannel,
				timeout: typeof link?.timeoutSeconds === "number" ? link.timeoutSeconds.toString() : void 0,
				messageChannel: "node"
			}, defaultRuntime, ctx.deps).catch((err) => {
				ctx.logGateway.warn(`agent failed node=${nodeId}: ${formatForLog(err)}`);
			});
			return;
		}
		case "chat.subscribe": {
			if (!evt.payloadJSON) return;
			const sessionKey = parseSessionKeyFromPayloadJSON(evt.payloadJSON);
			if (!sessionKey) return;
			ctx.nodeSubscribe(nodeId, sessionKey);
			return;
		}
		case "chat.unsubscribe": {
			if (!evt.payloadJSON) return;
			const sessionKey = parseSessionKeyFromPayloadJSON(evt.payloadJSON);
			if (!sessionKey) return;
			ctx.nodeUnsubscribe(nodeId, sessionKey);
			return;
		}
		case "exec.started":
		case "exec.finished":
		case "exec.denied": {
			if (!evt.payloadJSON) return;
			let payload;
			try {
				payload = JSON.parse(evt.payloadJSON);
			} catch {
				return;
			}
			const obj = typeof payload === "object" && payload !== null ? payload : {};
			const sessionKey = typeof obj.sessionKey === "string" ? obj.sessionKey.trim() : `node-${nodeId}`;
			if (!sessionKey) return;
			const runId = typeof obj.runId === "string" ? obj.runId.trim() : "";
			const command = typeof obj.command === "string" ? obj.command.trim() : "";
			const exitCode = typeof obj.exitCode === "number" && Number.isFinite(obj.exitCode) ? obj.exitCode : void 0;
			const timedOut = obj.timedOut === true;
			const output = typeof obj.output === "string" ? obj.output.trim() : "";
			const reason = typeof obj.reason === "string" ? obj.reason.trim() : "";
			let text = "";
			if (evt.event === "exec.started") {
				text = `Exec started (node=${nodeId}${runId ? ` id=${runId}` : ""})`;
				if (command) text += `: ${command}`;
			} else if (evt.event === "exec.finished") {
				const exitLabel = timedOut ? "timeout" : `code ${exitCode ?? "?"}`;
				const compactOutput = compactExecEventOutput(output);
				if (!(timedOut || exitCode !== 0 || compactOutput.length > 0)) return;
				text = `Exec finished (node=${nodeId}${runId ? ` id=${runId}` : ""}, ${exitLabel})`;
				if (compactOutput) text += `\n${compactOutput}`;
			} else {
				text = `Exec denied (node=${nodeId}${runId ? ` id=${runId}` : ""}${reason ? `, ${reason}` : ""})`;
				if (command) text += `: ${command}`;
			}
			enqueueSystemEvent(text, {
				sessionKey,
				contextKey: runId ? `exec:${runId}` : "exec"
			});
			requestHeartbeatNow({ reason: "exec-event" });
			return;
		}
		case "push.apns.register": {
			if (!evt.payloadJSON) return;
			let payload;
			try {
				payload = JSON.parse(evt.payloadJSON);
			} catch {
				return;
			}
			const obj = typeof payload === "object" && payload !== null ? payload : {};
			const token = typeof obj.token === "string" ? obj.token : "";
			const topic = typeof obj.topic === "string" ? obj.topic : "";
			const environment = obj.environment;
			try {
				await registerApnsToken({
					nodeId,
					token,
					topic,
					environment
				});
			} catch (err) {
				ctx.logGateway.warn(`push apns register failed node=${nodeId}: ${formatForLog(err)}`);
			}
			return;
		}
		default: return;
	}
};

//#endregion
export { handleNodeEvent };