import { t as avoidTrailingHighSurrogateBreak } from "./utf16-slice-D_ngcYKd.js";
import { l as normalizeOptionalString, o as normalizeLowercaseStringOrEmpty } from "./string-coerce-CIXf7egm.js";
import { d as asPositiveSafeInteger } from "./number-coercion-CLj0HTDM.js";
import { r as formatErrorMessage } from "./errors-Z0lU1pH3.js";
import { r as defaultRuntime } from "./runtime-LRpY2Icg.js";
import { n as getRuntimeConfig } from "./io.runtime-CNGn9TXj.js";
import { At as boolean, Et as array, Rn as string, Tn as object, Xn as union, Zn as unknown, wn as number } from "./schemas-CZ9Toj_c.js";
import "./io-C1WzQdAN.js";
import { i as logWarn } from "./logger-DJxYfLJ7.js";
import { d as onAgentEventForRun, i as emitAgentEvent } from "./agent-events-B7aFxbB8.js";
import { t as bindGatewayContextResolver } from "./gateway-request-scope-Bj2ffrHm.js";
import { m as retainGatewayRootWorkAdmissionContinuation } from "./gateway-work-admission-Cq7eCT39.js";
import { n as estimateBase64DecodedBytes } from "./base64-Vw7DZYSc.js";
import { s as toOpenAiChatCompletionsUsage } from "./usage-DNKCVmJi.js";
import "./stream-message-shared-Cyrn1UHN.js";
import { t as readAgentRunTerminalOutcome } from "./agent-run-terminal-outcome-Ovu6kj_3.js";
import { r as isClientToolNameConflictError } from "./agent-tool-definition-adapter-1B3OaXGL.js";
import { a as extractImageContentFromSource, n as DEFAULT_INPUT_IMAGE_MIMES, o as normalizeMimeList, r as DEFAULT_INPUT_TIMEOUT_MS, t as DEFAULT_INPUT_IMAGE_MAX_BYTES } from "./input-files-CCNvAeZd.js";
import { t as createDefaultDeps } from "./deps-CUB-iOVy.js";
import { n as agentCommandFromGatewayIngress } from "./agent-command-CM1QNSFx.js";
import "./agent-DJnDeTgh.js";
import { _ as resolveOpenAiCompatibleHttpSenderIsOwner, g as resolveOpenAiCompatibleHttpOperatorScopes, o as authorizeOpenAiCompatibleHttpModelOverride } from "./http-auth-utils-BALcmMci.js";
import { h as writeDone, l as sendMissingScopeForbidden, m as watchClientDisconnect, o as sendInvalidRequest, p as setSseHeaders, r as parseGatewayJsonRequest, s as sendJson } from "./http-common-BnNJPGxP.js";
import { t as authorizeGatewaySessionCreation } from "./operator-role-policy-Blv6BRJs.js";
import { a as isGatewaySessionKeyOverrideError, c as isUnknownGatewayAgentError, d as resolveGatewayRequestContext, f as resolveOpenAiCompatModelOverride, i as isAgentSelectionRequiredError, o as isInvalidGatewayModelError, r as authorizeOpenAiCompatibleHttpSession } from "./http-utils-Buw61ytF.js";
import { t as handleGatewayPostJsonEndpoint } from "./http-endpoint-helpers-Dor2RylF.js";
import { a as validateOpenAiSamplingParams, c as IMAGE_ONLY_USER_MESSAGE, d as isReplaceableAssistantStreamEvent, f as resolveAssistantStreamDeltaText, i as resolveOpenAiCompatError, l as buildAgentMessageFromConversationEntries, n as resolveUnsatisfiedToolChoiceMessage, o as resolveAgentRunUsage, p as resolveAssistantStreamSnapshotText, r as toolChoiceConstraintPrompt, s as normalizeInputHostnameAllowlist, t as isToolChoiceConstraintSatisfied, u as renderConversationToolCall } from "./openai-tool-choice-BuoW0Ev8.js";
import { randomUUID } from "node:crypto";
//#region src/gateway/openai-http.ts
const OpenAiChatCompletionRequestSchema = object({
	model: string().optional(),
	stream: boolean().nullish(),
	stream_options: object({ include_usage: boolean().optional() }).passthrough().nullish(),
	tools: array(unknown()).optional(),
	tool_choice: unknown().optional(),
	messages: array(unknown()).optional(),
	user: string().optional(),
	max_tokens: number().int().positive().nullish(),
	max_completion_tokens: number().int().positive().nullish(),
	temperature: number().nullish(),
	top_p: number().nullish(),
	response_format: unknown().optional(),
	frequency_penalty: number().nullish(),
	presence_penalty: number().nullish(),
	seed: number().nullish(),
	stop: union([string(), array(string())]).nullish()
});
const DEFAULT_OPENAI_CHAT_COMPLETIONS_BODY_BYTES = 20 * 1024 * 1024;
const DEFAULT_OPENAI_MAX_IMAGE_PARTS = 8;
const DEFAULT_OPENAI_MAX_TOTAL_IMAGE_BYTES = 20 * 1024 * 1024;
const DEFAULT_OPENAI_IMAGE_LIMITS = {
	allowUrl: false,
	allowedMimes: new Set(DEFAULT_INPUT_IMAGE_MIMES),
	maxBytes: DEFAULT_INPUT_IMAGE_MAX_BYTES,
	maxRedirects: 3,
	timeoutMs: DEFAULT_INPUT_TIMEOUT_MS
};
function resolveOpenAiChatCompletionsLimits(config) {
	const imageConfig = config?.images;
	return {
		maxBodyBytes: DEFAULT_OPENAI_CHAT_COMPLETIONS_BODY_BYTES,
		maxImageParts: DEFAULT_OPENAI_MAX_IMAGE_PARTS,
		maxTotalImageBytes: DEFAULT_OPENAI_MAX_TOTAL_IMAGE_BYTES,
		images: {
			allowUrl: imageConfig?.allowUrl ?? DEFAULT_OPENAI_IMAGE_LIMITS.allowUrl,
			urlAllowlist: normalizeInputHostnameAllowlist(imageConfig?.urlAllowlist),
			allowedMimes: normalizeMimeList(imageConfig?.allowedMimes, DEFAULT_INPUT_IMAGE_MIMES),
			maxBytes: imageConfig?.maxBytes ?? 10485760,
			maxRedirects: imageConfig?.maxRedirects ?? 3,
			timeoutMs: imageConfig?.timeoutMs ?? 1e4
		}
	};
}
function writeSse(res, data) {
	res.write(`data: ${JSON.stringify(data)}\n\n`);
}
function buildAgentCommandInput(params) {
	return {
		message: params.prompt.message,
		extraSystemPrompt: params.prompt.extraSystemPrompt,
		images: params.prompt.images,
		clientTools: params.clientTools,
		model: params.modelOverride,
		sessionKey: params.sessionKey,
		runId: params.runId,
		deliver: false,
		messageChannel: params.messageChannel,
		senderIsOwner: params.senderIsOwner,
		bestEffortDeliver: false,
		allowModelOverride: params.modelOverride !== void 0,
		abortSignal: params.abortSignal,
		streamParams: params.streamParams
	};
}
function extractClientToolsFromChatRequest(tools) {
	if (tools == null) return [];
	if (!Array.isArray(tools)) throw new Error("tools must be an array");
	const clientTools = [];
	for (const tool of tools) {
		if (!tool || typeof tool !== "object" || Array.isArray(tool)) throw new Error("each tool must be an object");
		if (tool.type !== "function") throw new Error("only function tools are supported");
		const functionValue = tool.function;
		if (!functionValue || typeof functionValue !== "object" || Array.isArray(functionValue)) throw new Error("tool.function is required");
		const rawName = functionValue.name;
		const name = typeof rawName === "string" ? rawName.trim() : "";
		if (!name) throw new Error("tool.function.name is required");
		const description = functionValue.description;
		const parameters = functionValue.parameters;
		const strict = functionValue.strict;
		clientTools.push({
			type: "function",
			function: {
				name,
				...typeof description === "string" ? { description } : {},
				...parameters && typeof parameters === "object" && !Array.isArray(parameters) ? { parameters } : {},
				...typeof strict === "boolean" ? { strict } : {}
			}
		});
	}
	return clientTools;
}
function applyChatToolChoice(params) {
	const { tools, toolChoice } = params;
	if (toolChoice == null || toolChoice === "auto") return { tools };
	if (toolChoice === "none") return { tools: [] };
	if (toolChoice === "required") {
		if (tools.length === 0) throw new Error("tool_choice=required but no tools were provided");
		const constraint = { type: "required" };
		return {
			tools,
			extraSystemPrompt: toolChoiceConstraintPrompt(constraint),
			constraint
		};
	}
	if (typeof toolChoice !== "object" || Array.isArray(toolChoice)) throw new Error("tool_choice must be a string or object");
	const choiceType = toolChoice.type;
	if (choiceType === "function") {
		const targetName = normalizeOptionalString(toolChoice.function?.name);
		if (!targetName) throw new Error("tool_choice.function.name is required");
		const matched = tools.filter((tool) => tool.function?.name === targetName);
		if (matched.length === 0) throw new Error(`tool_choice requested unknown tool: ${targetName}`);
		const constraint = {
			type: "function",
			name: targetName
		};
		return {
			tools: matched,
			extraSystemPrompt: toolChoiceConstraintPrompt(constraint),
			constraint
		};
	}
	if (typeof choiceType !== "string") throw new Error("unsupported tool_choice type");
	throw new Error(`tool_choice ${choiceType} is not supported`);
}
function writeChatCompletionChunk(res, identity, chunk) {
	writeSse(res, {
		id: identity.runId,
		object: "chat.completion.chunk",
		created: identity.created,
		model: identity.model,
		...chunk
	});
}
function writeAssistantRoleChunk(res, params) {
	writeChatCompletionChunk(res, params, { choices: [{
		index: 0,
		delta: { role: "assistant" },
		finish_reason: null
	}] });
}
function writeAssistantContentChunk(res, params) {
	writeChatCompletionChunk(res, params, { choices: [{
		index: 0,
		delta: { content: params.content },
		finish_reason: null
	}] });
}
function writeAssistantFinishChunk(res, params) {
	writeChatCompletionChunk(res, params, { choices: [{
		index: 0,
		delta: {},
		finish_reason: params.finishReason
	}] });
}
function splitArgumentsForStreaming(argumentsValue) {
	if (!argumentsValue) return [""];
	const chunkSize = 256;
	const chunks = [];
	for (let start = 0; start < argumentsValue.length;) {
		const end = avoidTrailingHighSurrogateBreak(argumentsValue, start, Math.min(start + chunkSize, argumentsValue.length));
		chunks.push(argumentsValue.slice(start, end));
		start = end;
	}
	return chunks.length > 0 ? chunks : [""];
}
function writeAssistantToolCallsIncrementalChunks(res, params) {
	for (const [index, call] of params.toolCalls.entries()) {
		writeChatCompletionChunk(res, params, { choices: [{
			index: 0,
			delta: { tool_calls: [{
				index,
				id: call.id,
				type: "function",
				function: {
					name: call.name,
					arguments: ""
				}
			}] },
			finish_reason: null
		}] });
		for (const argsDelta of splitArgumentsForStreaming(call.arguments)) writeChatCompletionChunk(res, params, { choices: [{
			index: 0,
			delta: { tool_calls: [{
				index,
				function: { arguments: argsDelta }
			}] },
			finish_reason: null
		}] });
	}
}
function writeUsageChunk(res, params) {
	writeChatCompletionChunk(res, params, {
		choices: [],
		usage: params.usage
	});
}
function asMessages(val) {
	return Array.isArray(val) ? val : [];
}
function extractTextContent(content) {
	if (typeof content === "string") return content;
	if (Array.isArray(content)) return content.map((part) => {
		if (!part || typeof part !== "object") return "";
		const type = part.type;
		const text = part.text;
		const inputText = part.input_text;
		if (type === "text" && typeof text === "string") return text;
		if (type === "input_text" && typeof text === "string") return text;
		if (typeof inputText === "string") return inputText;
		return "";
	}).filter(Boolean).join("\n");
	return "";
}
function stringifyToolCallArguments(value) {
	if (typeof value === "string") return value;
	if (value == null) return "";
	try {
		const serialized = JSON.stringify(value);
		return typeof serialized === "string" ? serialized : "";
	} catch {
		return "";
	}
}
function extractAssistantToolCalls(value) {
	if (!Array.isArray(value)) return [];
	const calls = [];
	for (const rawCall of value) {
		if (!rawCall || typeof rawCall !== "object" || Array.isArray(rawCall)) continue;
		const id = normalizeOptionalString(rawCall.id) ?? "";
		const functionValue = rawCall.function;
		if (!functionValue || typeof functionValue !== "object" || Array.isArray(functionValue)) continue;
		const name = normalizeOptionalString(functionValue.name) ?? "";
		if (!id || !name) continue;
		const argumentsValue = stringifyToolCallArguments(functionValue.arguments);
		calls.push({
			id,
			name,
			arguments: argumentsValue
		});
	}
	return calls;
}
function resolveImageUrlPart(part) {
	if (!part || typeof part !== "object") return;
	const imageUrl = part.image_url;
	if (typeof imageUrl === "string") {
		const trimmed = imageUrl.trim();
		return trimmed.length > 0 ? trimmed : void 0;
	}
	if (!imageUrl || typeof imageUrl !== "object") return;
	const rawUrl = imageUrl.url;
	if (typeof rawUrl !== "string") return;
	const trimmed = rawUrl.trim();
	return trimmed.length > 0 ? trimmed : void 0;
}
function extractImageUrls(content) {
	const urls = [];
	if (!Array.isArray(content)) return {
		kind: "valid",
		urls
	};
	for (const part of content) {
		if (!part || typeof part !== "object") continue;
		if (part.type !== "image_url") continue;
		const url = resolveImageUrlPart(part);
		if (!url) return { kind: "invalid" };
		urls.push(url);
	}
	return {
		kind: "valid",
		urls
	};
}
function parseImageUrlToSource(url) {
	const dataUriMatch = /^data:([^,]*?),(.*)$/is.exec(url);
	if (dataUriMatch) {
		const metadata = normalizeOptionalString(dataUriMatch[1]) ?? "";
		const data = dataUriMatch[2] ?? "";
		const metadataParts = metadata.split(";").map((part) => normalizeOptionalString(part) ?? "").filter(Boolean);
		if (!metadataParts.some((part) => normalizeLowercaseStringOrEmpty(part) === "base64")) throw new Error("image_url data URI must be base64 encoded");
		if (!(normalizeOptionalString(data) ?? "")) throw new Error("image_url data URI is missing payload data");
		return {
			type: "base64",
			mediaType: metadataParts.find((part) => part.includes("/")),
			data
		};
	}
	return {
		type: "url",
		url
	};
}
function resolveActiveTurnContext(messagesUnknown) {
	const messages = asMessages(messagesUnknown);
	for (let i = messages.length - 1; i >= 0; i -= 1) {
		const msg = messages[i];
		if (!msg || typeof msg !== "object") continue;
		const role = normalizeOptionalString(msg.role) ?? "";
		const normalizedRole = role === "function" ? "tool" : role;
		if (normalizedRole !== "user" && normalizedRole !== "tool") continue;
		const imageUrls = normalizedRole === "user" ? extractImageUrls(msg.content) : {
			kind: "valid",
			urls: []
		};
		return {
			activeTurnIndex: i,
			activeUserMessageIndex: normalizedRole === "user" ? i : -1,
			imageUrls
		};
	}
	return {
		activeTurnIndex: -1,
		activeUserMessageIndex: -1,
		imageUrls: {
			kind: "valid",
			urls: []
		}
	};
}
async function resolveImagesForRequest(activeTurnContext, limits) {
	if (activeTurnContext.imageUrls.kind === "invalid") throw new Error("image_url part is missing a valid URL");
	const urls = activeTurnContext.imageUrls.urls;
	if (urls.length === 0) return [];
	if (urls.length > limits.maxImageParts) throw new Error(`Too many image_url parts (${urls.length}; limit ${limits.maxImageParts})`);
	const images = [];
	let totalBytes = 0;
	for (const url of urls) {
		const source = parseImageUrlToSource(url);
		if (source.type === "base64") {
			const sourceBytes = estimateBase64DecodedBytes(source.data);
			if (totalBytes + sourceBytes > limits.maxTotalImageBytes) throw new Error(`Total image payload too large (${totalBytes + sourceBytes}; limit ${limits.maxTotalImageBytes})`);
		}
		const image = await extractImageContentFromSource(source, limits.images);
		totalBytes += estimateBase64DecodedBytes(image.data);
		if (totalBytes > limits.maxTotalImageBytes) throw new Error(`Total image payload too large (${totalBytes}; limit ${limits.maxTotalImageBytes})`);
		images.push(image);
	}
	return images;
}
function buildAgentPrompt(messagesUnknown, activeTurnContext) {
	const messages = asMessages(messagesUnknown);
	const hasActiveTurnImage = activeTurnContext.imageUrls.kind === "valid" && activeTurnContext.imageUrls.urls.length > 0;
	const systemParts = [];
	const conversationEntries = [];
	for (const [i, msg] of messages.entries()) {
		if (!msg || typeof msg !== "object") continue;
		const role = normalizeOptionalString(msg.role) ?? "";
		const content = extractTextContent(msg.content).trim();
		if (!role) continue;
		if (role === "system" || role === "developer") {
			if (content) systemParts.push(content);
			continue;
		}
		const normalizedRole = role === "function" ? "tool" : role;
		if (normalizedRole !== "user" && normalizedRole !== "assistant" && normalizedRole !== "tool") continue;
		const assistantToolCallsSummary = (normalizedRole === "assistant" ? extractAssistantToolCalls(msg.tool_calls) : []).map(renderConversationToolCall).join("\n");
		const messageContent = [normalizedRole === "user" && !content && hasActiveTurnImage && i === activeTurnContext.activeUserMessageIndex ? IMAGE_ONLY_USER_MESSAGE : content, assistantToolCallsSummary].filter((part) => Boolean(part)).join("\n");
		if (!messageContent) continue;
		const name = normalizeOptionalString(msg.name) ?? "";
		const toolCallId = normalizeOptionalString(msg.tool_call_id) ?? "";
		const sender = normalizedRole === "assistant" ? "Assistant" : normalizedRole === "user" ? "User" : toolCallId ? `Tool:${toolCallId}` : name ? `Tool:${name}` : "Tool";
		conversationEntries.push({
			role: normalizedRole,
			entry: {
				sender,
				body: messageContent
			},
			internalStreamError: normalizedRole === "assistant" && normalizeOptionalString(msg.stopReason) === "error" && messageContent.trim() === "[assistant turn failed before producing content]"
		});
	}
	return {
		message: buildAgentMessageFromConversationEntries(conversationEntries),
		extraSystemPrompt: systemParts.length > 0 ? systemParts.join("\n\n") : void 0
	};
}
function resolveAgentResponseText(result) {
	const payloads = result?.payloads;
	if (!Array.isArray(payloads) || payloads.length === 0) return "No response from OpenClaw.";
	return payloads.map((p) => typeof p.text === "string" ? p.text : "").filter(Boolean).join("\n\n") || "No response from OpenClaw.";
}
function resolveAgentResponseCommentary(result) {
	const payloads = result?.payloads;
	if (!Array.isArray(payloads) || payloads.length === 0) return "";
	return payloads.map((p) => typeof p.text === "string" ? p.text : "").filter(Boolean).join("\n\n");
}
function resolveStopReasonAndPendingToolCalls(meta) {
	if (!meta || typeof meta !== "object" || Array.isArray(meta)) return {
		stopReason: void 0,
		pendingToolCalls: void 0
	};
	const stopReasonRaw = meta.stopReason;
	const stopReason = typeof stopReasonRaw === "string" ? stopReasonRaw : void 0;
	const pendingRaw = meta.pendingToolCalls;
	if (!Array.isArray(pendingRaw)) return {
		stopReason,
		pendingToolCalls: void 0
	};
	const pendingToolCalls = [];
	for (const call of pendingRaw) {
		const id = typeof call?.id === "string" ? call.id.trim() : "";
		const name = typeof call?.name === "string" ? call.name.trim() : "";
		const argsValue = call?.arguments;
		const argumentsValue = typeof argsValue === "string" ? argsValue : argsValue == null ? "" : JSON.stringify(argsValue);
		if (!id || !name) continue;
		pendingToolCalls.push({
			id,
			name,
			arguments: argumentsValue
		});
	}
	return {
		stopReason,
		pendingToolCalls
	};
}
function resolveChatCompletionUsage(result) {
	return toOpenAiChatCompletionsUsage(resolveAgentRunUsage(result));
}
function resolveIncludeUsageForStreaming(payload) {
	const streamOptions = payload.stream_options;
	if (!streamOptions || typeof streamOptions !== "object" || Array.isArray(streamOptions)) return false;
	return streamOptions.include_usage === true;
}
function resolveResponseFormat(value) {
	if (value == null) return;
	if (typeof value !== "object" || Array.isArray(value)) throw new Error("response_format must be an object");
	const obj = value;
	const type = obj.type;
	if (type !== "text" && type !== "json_object" && type !== "json_schema") throw new Error("response_format.type must be text, json_object, or json_schema");
	return obj;
}
function resolveStopSequences(value) {
	if (value == null) return;
	const list = typeof value === "string" ? [value] : value;
	if (!Array.isArray(list)) throw new Error("stop must be a string or array of strings");
	if (list.length > 4) throw new Error("stop supports at most 4 sequences");
	const sequences = [];
	for (const item of list) {
		if (typeof item !== "string" || item.length === 0) throw new Error("stop entries must be non-empty strings");
		sequences.push(item);
	}
	return sequences.length > 0 ? sequences : void 0;
}
function resolveChatCompletionTokenCap(value, field) {
	if (value == null) return;
	const maxTokens = asPositiveSafeInteger(value);
	if (maxTokens === void 0) throw new Error(`${field} must be a positive safe integer`);
	return maxTokens;
}
async function handleOpenAiHttpRequest(req, res, opts) {
	const limits = resolveOpenAiChatCompletionsLimits(opts.config);
	const handled = await handleGatewayPostJsonEndpoint(req, res, {
		pathname: "/v1/chat/completions",
		requiredOperatorMethod: "chat.send",
		resolveOperatorScopes: resolveOpenAiCompatibleHttpOperatorScopes,
		auth: opts.auth,
		trustedProxies: opts.trustedProxies,
		allowRealIpFallback: opts.allowRealIpFallback,
		rateLimiter: opts.rateLimiter,
		maxBodyBytes: opts.maxBodyBytes ?? limits.maxBodyBytes
	});
	if (handled === false) return false;
	if (!handled) return true;
	const modelOverrideAuth = authorizeOpenAiCompatibleHttpModelOverride(req, handled.requestAuth);
	if (!modelOverrideAuth.allowed) {
		sendMissingScopeForbidden(res, modelOverrideAuth.missingScope);
		return true;
	}
	const senderIsOwner = resolveOpenAiCompatibleHttpSenderIsOwner(req, handled.requestAuth);
	const payload = parseGatewayJsonRequest(res, handled.body, OpenAiChatCompletionRequestSchema);
	if (!payload) return true;
	const stream = payload.stream === true;
	const streamIncludeUsage = stream && resolveIncludeUsageForStreaming(payload);
	const model = typeof payload.model === "string" ? payload.model : "openclaw";
	const user = typeof payload.user === "string" ? payload.user : void 0;
	let maxTokens;
	try {
		const maxCompletionTokens = resolveChatCompletionTokenCap(payload.max_completion_tokens, "max_completion_tokens");
		const legacyMaxTokens = resolveChatCompletionTokenCap(payload.max_tokens, "max_tokens");
		maxTokens = maxCompletionTokens ?? legacyMaxTokens;
	} catch (err) {
		sendInvalidRequest(res, formatErrorMessage(err).trim());
		return true;
	}
	const temperature = typeof payload.temperature === "number" ? payload.temperature : void 0;
	const topP = typeof payload.top_p === "number" ? payload.top_p : void 0;
	const frequencyPenalty = typeof payload.frequency_penalty === "number" ? payload.frequency_penalty : void 0;
	const presencePenalty = typeof payload.presence_penalty === "number" ? payload.presence_penalty : void 0;
	const seed = typeof payload.seed === "number" ? payload.seed : void 0;
	let responseFormat;
	try {
		responseFormat = resolveResponseFormat(payload.response_format);
	} catch (err) {
		sendInvalidRequest(res, `Invalid response_format: ${formatErrorMessage(err).trim()}`);
		return true;
	}
	let stop;
	try {
		stop = resolveStopSequences(payload.stop);
	} catch (err) {
		sendInvalidRequest(res, `Invalid stop: ${formatErrorMessage(err).trim()}`);
		return true;
	}
	const samplingError = validateOpenAiSamplingParams({
		temperature: payload.temperature,
		topP: payload.top_p,
		frequencyPenalty: payload.frequency_penalty,
		presencePenalty: payload.presence_penalty,
		seed: payload.seed
	});
	if (samplingError) {
		sendInvalidRequest(res, samplingError);
		return true;
	}
	const streamParams = maxTokens !== void 0 || temperature !== void 0 || topP !== void 0 || responseFormat !== void 0 || frequencyPenalty !== void 0 || presencePenalty !== void 0 || seed !== void 0 || stop !== void 0 ? {
		...maxTokens !== void 0 ? { maxTokens } : {},
		...temperature !== void 0 ? { temperature } : {},
		...topP !== void 0 ? { topP } : {},
		...responseFormat !== void 0 ? { responseFormat } : {},
		...frequencyPenalty !== void 0 ? { frequencyPenalty } : {},
		...presencePenalty !== void 0 ? { presencePenalty } : {},
		...seed !== void 0 ? { seed } : {},
		...stop !== void 0 ? { stop } : {}
	} : void 0;
	let agentId;
	let sessionKey;
	let messageChannel;
	try {
		({agentId, sessionKey, messageChannel} = resolveGatewayRequestContext({
			req,
			model,
			user,
			sessionPrefix: "openai",
			defaultMessageChannel: "webchat",
			useMessageChannelHeader: true
		}));
	} catch (err) {
		if (isAgentSelectionRequiredError(err) || isUnknownGatewayAgentError(err) || isInvalidGatewayModelError(err) || isGatewaySessionKeyOverrideError(err)) {
			sendInvalidRequest(res, err.message);
			return true;
		}
		throw err;
	}
	const creationAuth = authorizeGatewaySessionCreation({
		cfg: getRuntimeConfig(),
		...senderIsOwner && !handled.requestAuth.authenticatedUserProfile ? { actor: { kind: "system" } } : { profileId: handled.requestAuth.authenticatedUserProfile?.profileId },
		agentId
	});
	if (creationAuth) {
		sendJson(res, 403, { error: {
			message: creationAuth.message,
			type: "forbidden"
		} });
		return true;
	}
	const sessionAuth = authorizeOpenAiCompatibleHttpSession({
		agentId,
		sessionKey,
		requestAuth: handled.requestAuth,
		senderIsOwner
	});
	if (!sessionAuth.allowed) {
		sendJson(res, 403, { error: {
			message: sessionAuth.message,
			type: "forbidden"
		} });
		return true;
	}
	const { modelOverride, errorMessage: modelError } = await resolveOpenAiCompatModelOverride({
		req,
		agentId,
		model
	});
	if (modelError) {
		sendInvalidRequest(res, modelError);
		return true;
	}
	const activeTurnContext = resolveActiveTurnContext(payload.messages);
	const prompt = buildAgentPrompt(payload.messages, activeTurnContext);
	let resolvedClientTools;
	let toolChoicePrompt;
	let toolChoiceConstraint;
	try {
		const toolChoiceResult = applyChatToolChoice({
			tools: extractClientToolsFromChatRequest(payload.tools),
			toolChoice: payload.tool_choice
		});
		resolvedClientTools = toolChoiceResult.tools;
		toolChoicePrompt = toolChoiceResult.extraSystemPrompt;
		toolChoiceConstraint = toolChoiceResult.constraint;
	} catch (err) {
		sendInvalidRequest(res, `Invalid tools/tool_choice: ${formatErrorMessage(err).trim()}`);
		return true;
	}
	let images;
	try {
		images = await resolveImagesForRequest(activeTurnContext, limits);
	} catch (err) {
		logWarn(`openai-compat: invalid image_url content: ${String(err)}`);
		sendInvalidRequest(res, "Invalid image_url content in `messages`.");
		return true;
	}
	if (!prompt.message && images.length === 0) {
		sendInvalidRequest(res, "Missing user message in `messages`.");
		return true;
	}
	const runId = `chatcmpl_${randomUUID()}`;
	const created = Math.floor(Date.now() / 1e3);
	const streamIdentity = {
		runId,
		model,
		created
	};
	const deps = createDefaultDeps();
	const abortController = new AbortController();
	const mergedExtraSystemPrompt = [prompt.extraSystemPrompt, toolChoicePrompt].filter((part) => Boolean(part)).join("\n\n");
	const commandInput = buildAgentCommandInput({
		prompt: {
			message: prompt.message,
			extraSystemPrompt: mergedExtraSystemPrompt || void 0,
			images: images.length > 0 ? images : void 0
		},
		clientTools: resolvedClientTools.length > 0 ? resolvedClientTools : void 0,
		modelOverride,
		sessionKey,
		runId,
		messageChannel,
		senderIsOwner,
		abortSignal: abortController.signal,
		streamParams
	});
	const gatewayCommandInput = opts.resolveGatewayContext ? {
		...commandInput,
		onAdmittedRunContext: (context) => bindGatewayContextResolver(context, opts.resolveGatewayContext)
	} : commandInput;
	if (!stream) {
		const stopWatchingDisconnect = watchClientDisconnect(req, res, abortController);
		try {
			const result = await agentCommandFromGatewayIngress(gatewayCommandInput, defaultRuntime, deps, {});
			if (abortController.signal.aborted) return true;
			const meta = result?.meta;
			if (readAgentRunTerminalOutcome(result) === "failed") throw new Error("agent run failed");
			const usage = resolveChatCompletionUsage(result);
			const { stopReason, pendingToolCalls } = resolveStopReasonAndPendingToolCalls(meta);
			if (toolChoiceConstraint && !isToolChoiceConstraintSatisfied({
				constraint: toolChoiceConstraint,
				pendingToolCalls
			})) {
				sendJson(res, 502, { error: {
					message: resolveUnsatisfiedToolChoiceMessage(toolChoiceConstraint),
					type: "api_error"
				} });
				return true;
			}
			if (stopReason === "tool_calls" && pendingToolCalls && pendingToolCalls.length > 0) {
				sendJson(res, 200, {
					id: runId,
					object: "chat.completion",
					created,
					model,
					choices: [{
						index: 0,
						message: {
							role: "assistant",
							content: resolveAgentResponseCommentary(result),
							tool_calls: pendingToolCalls.map((call) => ({
								id: call.id,
								type: "function",
								function: {
									name: call.name,
									arguments: call.arguments
								}
							}))
						},
						finish_reason: "tool_calls"
					}],
					usage
				});
				return true;
			}
			sendJson(res, 200, {
				id: runId,
				object: "chat.completion",
				created,
				model,
				choices: [{
					index: 0,
					message: {
						role: "assistant",
						content: resolveAgentResponseText(result)
					},
					finish_reason: "stop"
				}],
				usage
			});
		} catch (err) {
			if (abortController.signal.aborted) return true;
			logWarn(`openai-compat: chat completion failed: ${String(err)}`);
			if (isClientToolNameConflictError(err)) {
				sendInvalidRequest(res, "invalid tool configuration");
				return true;
			}
			const mapped = resolveOpenAiCompatError(err);
			if (mapped) {
				sendJson(res, mapped.status, { error: mapped.error });
				return true;
			}
			sendJson(res, 500, { error: {
				message: "internal error",
				type: "api_error"
			} });
		} finally {
			stopWatchingDisconnect();
		}
		return true;
	}
	setSseHeaders(res);
	let wroteStopChunk = false;
	let sawAssistantDelta = false;
	let streamedAssistantText = "";
	let bufferedReplaceableAssistantContent = "";
	let finalUsage;
	let finalizeRequested = false;
	let finalizeScheduled = false;
	let finalizeFinishReason = "stop";
	let resultResolved = false;
	let closed = false;
	let observedTerminalLifecycle = false;
	let terminalStreamError;
	let terminalLifecyclePhase = "end";
	let stopWatchingDisconnect = () => {};
	const maybeFinalize = () => {
		if (closed || finalizeScheduled || !finalizeRequested) return;
		if (!resultResolved) return;
		if (streamIncludeUsage && !finalUsage) return;
		finalizeScheduled = true;
		queueMicrotask(() => {
			if (closed) return;
			if (terminalStreamError) {
				finishStreamWithError(terminalStreamError);
				return;
			}
			closed = true;
			stopWatchingDisconnect();
			unsubscribe();
			if (!wroteStopChunk) {
				writeAssistantFinishChunk(res, {
					...streamIdentity,
					finishReason: finalizeFinishReason
				});
				wroteStopChunk = true;
			}
			if (streamIncludeUsage && finalUsage) writeUsageChunk(res, {
				...streamIdentity,
				usage: finalUsage
			});
			writeDone(res);
			res.end();
		});
	};
	const requestFinalize = (finishReason = "stop") => {
		if (!finalizeRequested || finishReason === "tool_calls") finalizeFinishReason = finishReason;
		finalizeRequested = true;
		maybeFinalize();
	};
	const unsubscribe = onAgentEventForRun(runId, (evt) => {
		if (evt.runId !== runId) return;
		if (closed) return;
		if (evt.stream === "assistant") {
			const text = evt.data?.text;
			const replace = evt.data?.replace === true;
			if (replace && typeof text === "string") bufferedReplaceableAssistantContent = text;
			if (isReplaceableAssistantStreamEvent(evt)) {
				const snapshot = resolveAssistantStreamSnapshotText(evt);
				if (snapshot) bufferedReplaceableAssistantContent = snapshot;
				return;
			}
			if (replace && typeof text === "string" && !toolChoiceConstraint && !text.startsWith(streamedAssistantText)) {
				terminalStreamError ??= {
					message: "Assistant output cannot be represented as an append-only response stream.",
					type: "api_error"
				};
				return;
			}
			const content = typeof text === "string" && text.startsWith(streamedAssistantText) ? text.slice(streamedAssistantText.length) : resolveAssistantStreamDeltaText(evt);
			if (!content) return;
			streamedAssistantText += content;
			if (toolChoiceConstraint) return;
			sawAssistantDelta = true;
			writeAssistantContentChunk(res, {
				...streamIdentity,
				content
			});
			return;
		}
		if (evt.stream === "lifecycle") {
			const phase = evt.data?.phase;
			if (phase === "start") observedTerminalLifecycle = false;
			if (phase === "end" || phase === "error") {
				observedTerminalLifecycle = true;
				if (phase === "error" && terminalLifecyclePhase !== "error") terminalStreamError ??= {
					message: normalizeOptionalString(evt.data?.error) ?? "Agent run failed",
					type: "api_error"
				};
				requestFinalize();
			}
		}
	});
	const finishStreamWithError = (error) => {
		if (closed) return;
		closed = true;
		stopWatchingDisconnect();
		unsubscribe();
		writeSse(res, { error });
		writeDone(res);
		res.end();
	};
	const releaseAgentRootWork = retainGatewayRootWorkAdmissionContinuation();
	const releaseResponseRootWork = retainGatewayRootWorkAdmissionContinuation();
	const releaseStreamRootWork = () => {
		res.off("finish", releaseStreamRootWork);
		res.off("close", releaseStreamRootWork);
		releaseResponseRootWork?.();
	};
	res.once("finish", releaseStreamRootWork);
	res.once("close", releaseStreamRootWork);
	stopWatchingDisconnect = watchClientDisconnect(req, res, abortController, () => {
		closed = true;
		unsubscribe();
		releaseStreamRootWork();
	});
	writeAssistantRoleChunk(res, streamIdentity);
	(async () => {
		try {
			const result = await agentCommandFromGatewayIngress(gatewayCommandInput, defaultRuntime, deps, {});
			resultResolved = true;
			if (closed) return;
			if (readAgentRunTerminalOutcome(result) === "failed") {
				terminalLifecyclePhase = "error";
				finishStreamWithError({
					message: "internal error",
					type: "api_error"
				});
				return;
			}
			if (terminalStreamError) {
				finishStreamWithError(terminalStreamError);
				return;
			}
			finalUsage = resolveChatCompletionUsage(result);
			const meta = result?.meta;
			const { stopReason, pendingToolCalls } = resolveStopReasonAndPendingToolCalls(meta);
			if (toolChoiceConstraint && !isToolChoiceConstraintSatisfied({
				constraint: toolChoiceConstraint,
				pendingToolCalls
			})) {
				finishStreamWithError({
					message: resolveUnsatisfiedToolChoiceMessage(toolChoiceConstraint),
					type: "api_error"
				});
				return;
			}
			if (stopReason === "tool_calls" && pendingToolCalls && pendingToolCalls.length > 0) {
				if (!sawAssistantDelta) {
					const commentary = resolveAgentResponseCommentary(result) || streamedAssistantText || bufferedReplaceableAssistantContent;
					if (commentary) {
						sawAssistantDelta = true;
						writeAssistantContentChunk(res, {
							...streamIdentity,
							content: commentary
						});
					}
				}
				writeAssistantToolCallsIncrementalChunks(res, {
					...streamIdentity,
					toolCalls: pendingToolCalls
				});
				requestFinalize("tool_calls");
				return;
			}
			if (!sawAssistantDelta) {
				const content = resolveAgentResponseCommentary(result) || bufferedReplaceableAssistantContent || resolveAgentResponseText(result) || "No response from OpenClaw.";
				sawAssistantDelta = true;
				writeAssistantContentChunk(res, {
					...streamIdentity,
					content
				});
			}
			requestFinalize();
		} catch (err) {
			resultResolved = true;
			if (closed || abortController.signal.aborted) return;
			terminalLifecyclePhase = "error";
			logWarn(`openai-compat: streaming chat completion failed: ${String(err)}`);
			if (isClientToolNameConflictError(err)) {
				finishStreamWithError({
					message: "invalid tool configuration",
					type: "invalid_request_error"
				});
				return;
			}
			const mapped = resolveOpenAiCompatError(err);
			if (mapped) {
				finishStreamWithError(mapped.error);
				return;
			}
			if (terminalStreamError) {
				finishStreamWithError(terminalStreamError);
				return;
			}
			finishStreamWithError({
				message: "internal error",
				type: "api_error"
			});
		} finally {
			releaseAgentRootWork?.();
			if (!observedTerminalLifecycle && (terminalLifecyclePhase === "error" || !closed)) emitAgentEvent({
				runId,
				stream: "lifecycle",
				data: { phase: terminalLifecyclePhase }
			});
		}
	})();
	return true;
}
//#endregion
export { handleOpenAiHttpRequest };
