const require_helpers = require('./helpers.cjs');
const require_router = require('./router.cjs');
const require_sse_writer = require('./sse-writer.cjs');
const require_interruption = require('./interruption.cjs');
const require_chaos = require('./chaos.cjs');
const require_recorder = require('./recorder.cjs');

//#region src/messages.ts
/**
* Non-empty placeholder signature written into emitted `thinking` blocks.
*
* The real Anthropic signature is a cryptographic value aimock cannot
* reproduce, but extended-thinking invariant (b) requires a non-empty
* `signature` on the leading thinking block of a tool-loop continuation turn.
* Emitting "" would make a record→replay round-trip of an aimock thinking turn
* self-trip that invariant under strict mode. A non-empty placeholder keeps
* round-trips green; the invariant only checks for non-emptiness, not value.
*/
const PLACEHOLDER_SIGNATURE = "aimock-placeholder-signature";
function extractClaudeTextContent(content) {
	if (typeof content === "string") return content;
	if (!Array.isArray(content)) return "";
	return content.filter((b) => b != null && typeof b === "object" && b.type === "text").map((b) => b.text ?? "").join("");
}
function claudeToCompletionRequest(req) {
	const messages = [];
	if (req.system) {
		const systemText = typeof req.system === "string" ? req.system : Array.isArray(req.system) ? req.system.filter((b) => b != null && typeof b === "object" && b.type === "text").map((b) => b.text ?? "").join("") : "";
		if (systemText) messages.push({
			role: "system",
			content: systemText
		});
	}
	const reqMessages = Array.isArray(req.messages) ? req.messages : [];
	for (const msg of reqMessages) {
		if (!msg || typeof msg !== "object") continue;
		if (msg.role === "user") {
			if (typeof msg.content !== "string" && Array.isArray(msg.content)) {
				const blocks = msg.content.filter((b) => b != null && typeof b === "object");
				const toolResults = blocks.filter((b) => b.type === "tool_result");
				const textBlocks = blocks.filter((b) => b.type === "text");
				if (toolResults.length > 0) {
					for (const tr of toolResults) {
						const resultContent = typeof tr.content === "string" ? tr.content : Array.isArray(tr.content) ? tr.content.filter((b) => b != null && typeof b === "object" && b.type === "text").map((b) => b.text ?? "").join("") : "";
						messages.push({
							role: "tool",
							content: resultContent,
							tool_call_id: tr.tool_use_id
						});
					}
					if (textBlocks.length > 0) messages.push({
						role: "user",
						content: textBlocks.map((b) => b.text ?? "").join("")
					});
					continue;
				}
			}
			messages.push({
				role: "user",
				content: extractClaudeTextContent(msg.content)
			});
		} else if (msg.role === "assistant") if (typeof msg.content === "string") messages.push({
			role: "assistant",
			content: msg.content
		});
		else if (Array.isArray(msg.content)) {
			const toolUseBlocks = msg.content.filter((b) => b != null && typeof b === "object" && b.type === "tool_use");
			const textContent = extractClaudeTextContent(msg.content);
			if (toolUseBlocks.length > 0) messages.push({
				role: "assistant",
				content: textContent || null,
				tool_calls: toolUseBlocks.map((b) => ({
					id: b.id ?? require_helpers.generateToolUseId(),
					type: "function",
					function: {
						name: b.name ?? "",
						arguments: typeof b.input === "string" ? b.input : JSON.stringify(b.input ?? {})
					}
				}))
			});
			else messages.push({
				role: "assistant",
				content: textContent || null
			});
		} else messages.push({
			role: "assistant",
			content: null
		});
	}
	let tools;
	if (req.tools && req.tools.length > 0) tools = req.tools.map((t) => ({
		type: "function",
		function: {
			name: t.name,
			description: t.description,
			parameters: t.input_schema
		}
	}));
	return {
		model: req.model,
		messages,
		stream: req.stream,
		temperature: req.temperature,
		max_tokens: req.max_tokens,
		tools,
		_endpointType: "chat"
	};
}
/** True iff `req.thinking` is an object with `type === "enabled"`. */
function isThinkingEnabled(req) {
	const t = req.thinking;
	return typeof t === "object" && t !== null && t.type === "enabled";
}
/**
* True iff the user turn at `req.messages[userIndex]` carries a `tool_result`
* referencing one of `toolUseIds` — i.e. it answers the preceding assistant
* turn's `tool_use` block(s), making that assistant turn a tool-loop
* continuation point.
*/
function userTurnAnswersToolUse(msg, toolUseIds) {
	if (!msg || msg.role !== "user" || !Array.isArray(msg.content)) return false;
	return msg.content.some((b) => b != null && typeof b === "object" && b.type === "tool_result" && typeof b.tool_use_id === "string" && toolUseIds.has(b.tool_use_id));
}
/**
* Validate the Anthropic extended-thinking request invariants on tool-loop
* continuation turns. Pure: returns the first detected `ThinkingViolation` or
* `null` when thinking is disabled or every in-scope turn is well-formed.
*
* Scope: an assistant turn is in-scope only when it (a) is array content
* carrying at least one `tool_use` block and (b) is followed by a matching
* `tool_result` on the next user turn. Text-only / `end_turn` turns, string or
* empty turns, and trailing unanswered `tool_use` turns are all exempt.
*
* Scope detection assumes well-formed Anthropic transcripts: `tool_use` ids are
* unique, and a tool_use turn is answered by the immediately-following user
* turn's `tool_result` (adjacency). Malformed shapes — non-adjacent answers,
* idless `tool_use` blocks, or a `tool_result` separated from its `tool_use` by
* intervening turns — are intentionally treated as out-of-scope (and therefore
* not 400'd) rather than validated, since the real Anthropic API would never
* have produced them.
*/
function validateThinkingInvariants(req) {
	if (!isThinkingEnabled(req)) return null;
	const messages = Array.isArray(req.messages) ? req.messages : [];
	for (let i = 0; i < messages.length; i++) {
		const msg = messages[i];
		if (!msg || typeof msg !== "object") continue;
		if (msg.role !== "assistant" || !Array.isArray(msg.content) || msg.content.length === 0) continue;
		const toolUseIds = /* @__PURE__ */ new Set();
		for (const b of msg.content) {
			if (!b || typeof b !== "object") continue;
			if (b.type === "tool_use" && typeof b.id === "string") toolUseIds.add(b.id);
		}
		if (toolUseIds.size === 0) continue;
		if (!userTurnAnswersToolUse(messages[i + 1], toolUseIds)) continue;
		const first = msg.content[0];
		if (!first || typeof first !== "object") return {
			kind: "missing_thinking_first",
			messageIndex: i,
			observedFirstBlockType: void 0
		};
		if (first.type !== "thinking" && first.type !== "redacted_thinking") return {
			kind: "missing_thinking_first",
			messageIndex: i,
			observedFirstBlockType: first.type
		};
		if (first.type === "thinking") {
			if (typeof first.signature !== "string" || first.signature.length === 0) return {
				kind: "missing_signature",
				messageIndex: i
			};
		}
		if (first.type === "redacted_thinking") {
			if (typeof first.data !== "string" || first.data.length === 0) return {
				kind: "dropped_redacted_thinking",
				messageIndex: i
			};
		}
	}
	return null;
}
/** Render the Anthropic-shaped 400 error message for a thinking violation. */
function thinkingViolationMessage(v) {
	const prefix = `messages.${v.messageIndex}.content.0`;
	switch (v.kind) {
		case "missing_thinking_first": return `${prefix}: when \`thinking\` is enabled, a tool-loop continuation assistant turn must begin with a \`thinking\` block; got \`${v.observedFirstBlockType ?? "unknown"}\`.`;
		case "missing_signature": return `${prefix}: the leading \`thinking\` block is missing a non-empty \`signature\`.`;
		case "dropped_redacted_thinking": return `${prefix}: the leading \`redacted_thinking\` block must preserve its \`data\`.`;
	}
}
function claudeStopReason(finishReason, defaultReason) {
	if (!finishReason) return defaultReason;
	if (finishReason === "stop") return "end_turn";
	if (finishReason === "tool_calls") return "tool_use";
	if (finishReason === "length") return "max_tokens";
	return finishReason;
}
function claudeUsage(overrides) {
	if (!overrides?.usage) return {
		input_tokens: 0,
		output_tokens: 0
	};
	return {
		input_tokens: overrides.usage.input_tokens ?? overrides.usage.prompt_tokens ?? 0,
		output_tokens: overrides.usage.output_tokens ?? overrides.usage.completion_tokens ?? 0
	};
}
/**
* Emit faithful Anthropic `redacted_thinking` content-block events at the head
* of a streamed turn — one `content_block_start` (carrying the opaque `data`)
* and `content_block_stop` per recorded block, with NO deltas (real Anthropic
* delivers the encrypted reasoning entirely on the start event). Returns the
* next free block index. When `redactedThinking` is empty/absent this is a
* no-op, so no events are added.
*
* Leading placement keeps the turn round-trip-safe for the extended-thinking
* leading-block invariant (a thinking / redacted_thinking block must come
* first). It does NOT reproduce real Anthropic's stream order: aimock joins all
* plaintext reasoning into one block and emits all redacted blocks ahead of it,
* whereas real Anthropic interleaves `thinking` and `redacted_thinking` in the
* order they occurred. The interleaving between the two is not preserved.
*/
function pushRedactedThinkingStreamEvents(events, startIndex, redactedThinking) {
	let blockIndex = startIndex;
	for (const data of redactedThinking ?? []) {
		events.push({
			type: "content_block_start",
			index: blockIndex,
			content_block: {
				type: "redacted_thinking",
				data
			}
		});
		events.push({
			type: "content_block_stop",
			index: blockIndex
		});
		blockIndex++;
	}
	return blockIndex;
}
function buildClaudeTextStreamEvents(content, model, chunkSize, reasoning, overrides, reasoningSignature, redactedThinking) {
	const msgId = overrides?.id ?? require_helpers.generateMessageId();
	const effectiveModel = overrides?.model ?? model;
	const signature = reasoningSignature ?? PLACEHOLDER_SIGNATURE;
	const events = [];
	events.push({
		type: "message_start",
		message: {
			id: msgId,
			type: "message",
			role: overrides?.role ?? "assistant",
			content: [],
			model: effectiveModel,
			stop_reason: null,
			stop_sequence: null,
			usage: claudeUsage(overrides)
		}
	});
	let blockIndex = 0;
	blockIndex = pushRedactedThinkingStreamEvents(events, blockIndex, redactedThinking);
	if (reasoning) {
		events.push({
			type: "content_block_start",
			index: blockIndex,
			content_block: {
				type: "thinking",
				thinking: "",
				signature: ""
			}
		});
		for (let i = 0; i < reasoning.length; i += chunkSize) {
			const slice = reasoning.slice(i, i + chunkSize);
			events.push({
				type: "content_block_delta",
				index: blockIndex,
				delta: {
					type: "thinking_delta",
					thinking: slice
				}
			});
		}
		events.push({
			type: "content_block_delta",
			index: blockIndex,
			delta: {
				type: "signature_delta",
				signature
			}
		});
		events.push({
			type: "content_block_stop",
			index: blockIndex
		});
		blockIndex++;
	}
	events.push({
		type: "content_block_start",
		index: blockIndex,
		content_block: {
			type: "text",
			text: ""
		}
	});
	for (let i = 0; i < content.length; i += chunkSize) {
		const slice = content.slice(i, i + chunkSize);
		events.push({
			type: "content_block_delta",
			index: blockIndex,
			delta: {
				type: "text_delta",
				text: slice
			}
		});
	}
	events.push({
		type: "content_block_stop",
		index: blockIndex
	});
	events.push({
		type: "message_delta",
		delta: {
			stop_reason: claudeStopReason(overrides?.finishReason, "end_turn"),
			stop_sequence: null
		},
		usage: { output_tokens: claudeUsage(overrides).output_tokens }
	});
	events.push({ type: "message_stop" });
	return events;
}
function buildClaudeToolCallStreamEvents(toolCalls, model, chunkSize, logger, reasoning, overrides, reasoningSignature, redactedThinking) {
	const msgId = overrides?.id ?? require_helpers.generateMessageId();
	const effectiveModel = overrides?.model ?? model;
	const signature = reasoningSignature ?? PLACEHOLDER_SIGNATURE;
	const events = [];
	events.push({
		type: "message_start",
		message: {
			id: msgId,
			type: "message",
			role: overrides?.role ?? "assistant",
			content: [],
			model: effectiveModel,
			stop_reason: null,
			stop_sequence: null,
			usage: claudeUsage(overrides)
		}
	});
	let blockIndex = 0;
	blockIndex = pushRedactedThinkingStreamEvents(events, blockIndex, redactedThinking);
	if (reasoning) {
		events.push({
			type: "content_block_start",
			index: blockIndex,
			content_block: {
				type: "thinking",
				thinking: "",
				signature: ""
			}
		});
		for (let i = 0; i < reasoning.length; i += chunkSize) {
			const slice = reasoning.slice(i, i + chunkSize);
			events.push({
				type: "content_block_delta",
				index: blockIndex,
				delta: {
					type: "thinking_delta",
					thinking: slice
				}
			});
		}
		events.push({
			type: "content_block_delta",
			index: blockIndex,
			delta: {
				type: "signature_delta",
				signature
			}
		});
		events.push({
			type: "content_block_stop",
			index: blockIndex
		});
		blockIndex++;
	}
	for (const tc of toolCalls) {
		const toolUseId = tc.id || require_helpers.generateToolUseId();
		let argsObj;
		try {
			argsObj = JSON.parse(tc.arguments || "{}");
		} catch {
			logger.warn(`Malformed JSON in fixture tool call arguments for "${tc.name}": ${tc.arguments}`);
			argsObj = {};
		}
		const argsJson = JSON.stringify(argsObj);
		events.push({
			type: "content_block_start",
			index: blockIndex,
			content_block: {
				type: "tool_use",
				id: toolUseId,
				name: tc.name,
				input: {}
			}
		});
		for (let i = 0; i < argsJson.length; i += chunkSize) {
			const slice = argsJson.slice(i, i + chunkSize);
			events.push({
				type: "content_block_delta",
				index: blockIndex,
				delta: {
					type: "input_json_delta",
					partial_json: slice
				}
			});
		}
		events.push({
			type: "content_block_stop",
			index: blockIndex
		});
		blockIndex++;
	}
	events.push({
		type: "message_delta",
		delta: {
			stop_reason: claudeStopReason(overrides?.finishReason, "tool_use"),
			stop_sequence: null
		},
		usage: { output_tokens: claudeUsage(overrides).output_tokens }
	});
	events.push({ type: "message_stop" });
	return events;
}
/**
* Push faithful Anthropic `redacted_thinking` content blocks (each a
* `{ type: "redacted_thinking", data }`) onto a non-streaming content array, in
* recorded order. No-op when `redactedThinking` is empty/absent, so no blocks
* are added.
*
* Leading placement keeps the turn round-trip-safe for the extended-thinking
* leading-block invariant. It does NOT reproduce real Anthropic's block order:
* aimock joins all plaintext reasoning into one block and emits all redacted
* blocks ahead of it, whereas real Anthropic interleaves `thinking` and
* `redacted_thinking` in occurrence order. The interleaving is not preserved.
*/
function pushRedactedThinkingBlocks(contentBlocks, redactedThinking) {
	for (const data of redactedThinking ?? []) contentBlocks.push({
		type: "redacted_thinking",
		data
	});
}
function buildClaudeTextResponse(content, model, reasoning, overrides, reasoningSignature, redactedThinking) {
	const contentBlocks = [];
	pushRedactedThinkingBlocks(contentBlocks, redactedThinking);
	if (reasoning) contentBlocks.push({
		type: "thinking",
		thinking: reasoning,
		signature: reasoningSignature ?? PLACEHOLDER_SIGNATURE
	});
	contentBlocks.push({
		type: "text",
		text: content
	});
	return {
		id: overrides?.id ?? require_helpers.generateMessageId(),
		type: "message",
		role: overrides?.role ?? "assistant",
		content: contentBlocks,
		model: overrides?.model ?? model,
		stop_reason: claudeStopReason(overrides?.finishReason, "end_turn"),
		stop_sequence: null,
		usage: claudeUsage(overrides)
	};
}
function buildClaudeToolCallResponse(toolCalls, model, logger, reasoning, overrides, reasoningSignature, redactedThinking) {
	const contentBlocks = [];
	pushRedactedThinkingBlocks(contentBlocks, redactedThinking);
	if (reasoning) contentBlocks.push({
		type: "thinking",
		thinking: reasoning,
		signature: reasoningSignature ?? PLACEHOLDER_SIGNATURE
	});
	for (const tc of toolCalls) {
		let argsObj;
		try {
			argsObj = JSON.parse(tc.arguments || "{}");
		} catch {
			logger.warn(`Malformed JSON in fixture tool call arguments for "${tc.name}": ${tc.arguments}`);
			argsObj = {};
		}
		contentBlocks.push({
			type: "tool_use",
			id: tc.id || require_helpers.generateToolUseId(),
			name: tc.name,
			input: argsObj
		});
	}
	return {
		id: overrides?.id ?? require_helpers.generateMessageId(),
		type: "message",
		role: overrides?.role ?? "assistant",
		content: contentBlocks,
		model: overrides?.model ?? model,
		stop_reason: claudeStopReason(overrides?.finishReason, "tool_use"),
		stop_sequence: null,
		usage: claudeUsage(overrides)
	};
}
function buildClaudeContentWithToolCallsStreamEvents(content, toolCalls, model, chunkSize, logger, reasoning, overrides, reasoningSignature, redactedThinking, blocks) {
	const msgId = overrides?.id ?? require_helpers.generateMessageId();
	const effectiveModel = overrides?.model ?? model;
	const signature = reasoningSignature ?? PLACEHOLDER_SIGNATURE;
	const events = [];
	events.push({
		type: "message_start",
		message: {
			id: msgId,
			type: "message",
			role: overrides?.role ?? "assistant",
			content: [],
			model: effectiveModel,
			stop_reason: null,
			stop_sequence: null,
			usage: claudeUsage(overrides)
		}
	});
	let blockIndex = 0;
	blockIndex = pushRedactedThinkingStreamEvents(events, blockIndex, redactedThinking);
	if (reasoning) {
		events.push({
			type: "content_block_start",
			index: blockIndex,
			content_block: {
				type: "thinking",
				thinking: "",
				signature: ""
			}
		});
		for (let i = 0; i < reasoning.length; i += chunkSize) {
			const slice = reasoning.slice(i, i + chunkSize);
			events.push({
				type: "content_block_delta",
				index: blockIndex,
				delta: {
					type: "thinking_delta",
					thinking: slice
				}
			});
		}
		events.push({
			type: "content_block_delta",
			index: blockIndex,
			delta: {
				type: "signature_delta",
				signature
			}
		});
		events.push({
			type: "content_block_stop",
			index: blockIndex
		});
		blockIndex++;
	}
	if (blocks && blocks.length > 0) {
		const ordered = require_helpers.resolveFixtureBlocks(blocks);
		for (const block of ordered) if (block.type === "text") {
			events.push({
				type: "content_block_start",
				index: blockIndex,
				content_block: {
					type: "text",
					text: ""
				}
			});
			for (let i = 0; i < block.text.length; i += chunkSize) {
				const slice = block.text.slice(i, i + chunkSize);
				events.push({
					type: "content_block_delta",
					index: blockIndex,
					delta: {
						type: "text_delta",
						text: slice
					}
				});
			}
			events.push({
				type: "content_block_stop",
				index: blockIndex
			});
			blockIndex++;
		} else {
			const toolUseId = block.id || require_helpers.generateToolUseId();
			let argsObj;
			try {
				argsObj = JSON.parse(block.arguments || "{}");
			} catch {
				logger.warn(`Malformed JSON in fixture tool call arguments for "${block.name}": ${block.arguments}`);
				argsObj = {};
			}
			const argsJson = JSON.stringify(argsObj);
			events.push({
				type: "content_block_start",
				index: blockIndex,
				content_block: {
					type: "tool_use",
					id: toolUseId,
					name: block.name,
					input: {}
				}
			});
			for (let i = 0; i < argsJson.length; i += chunkSize) {
				const slice = argsJson.slice(i, i + chunkSize);
				events.push({
					type: "content_block_delta",
					index: blockIndex,
					delta: {
						type: "input_json_delta",
						partial_json: slice
					}
				});
			}
			events.push({
				type: "content_block_stop",
				index: blockIndex
			});
			blockIndex++;
		}
		events.push({
			type: "message_delta",
			delta: {
				stop_reason: claudeStopReason(overrides?.finishReason, "tool_use"),
				stop_sequence: null
			},
			usage: { output_tokens: claudeUsage(overrides).output_tokens }
		});
		events.push({ type: "message_stop" });
		return events;
	}
	events.push({
		type: "content_block_start",
		index: blockIndex,
		content_block: {
			type: "text",
			text: ""
		}
	});
	for (let i = 0; i < content.length; i += chunkSize) {
		const slice = content.slice(i, i + chunkSize);
		events.push({
			type: "content_block_delta",
			index: blockIndex,
			delta: {
				type: "text_delta",
				text: slice
			}
		});
	}
	events.push({
		type: "content_block_stop",
		index: blockIndex
	});
	blockIndex++;
	for (const tc of toolCalls) {
		const toolUseId = tc.id || require_helpers.generateToolUseId();
		let argsObj;
		try {
			argsObj = JSON.parse(tc.arguments || "{}");
		} catch {
			logger.warn(`Malformed JSON in fixture tool call arguments for "${tc.name}": ${tc.arguments}`);
			argsObj = {};
		}
		const argsJson = JSON.stringify(argsObj);
		events.push({
			type: "content_block_start",
			index: blockIndex,
			content_block: {
				type: "tool_use",
				id: toolUseId,
				name: tc.name,
				input: {}
			}
		});
		for (let i = 0; i < argsJson.length; i += chunkSize) {
			const slice = argsJson.slice(i, i + chunkSize);
			events.push({
				type: "content_block_delta",
				index: blockIndex,
				delta: {
					type: "input_json_delta",
					partial_json: slice
				}
			});
		}
		events.push({
			type: "content_block_stop",
			index: blockIndex
		});
		blockIndex++;
	}
	events.push({
		type: "message_delta",
		delta: {
			stop_reason: claudeStopReason(overrides?.finishReason, "tool_use"),
			stop_sequence: null
		},
		usage: { output_tokens: claudeUsage(overrides).output_tokens }
	});
	events.push({ type: "message_stop" });
	return events;
}
function buildClaudeContentWithToolCallsResponse(content, toolCalls, model, logger, reasoning, overrides, reasoningSignature, redactedThinking, blocks) {
	const contentBlocks = [];
	pushRedactedThinkingBlocks(contentBlocks, redactedThinking);
	if (reasoning) contentBlocks.push({
		type: "thinking",
		thinking: reasoning,
		signature: reasoningSignature ?? PLACEHOLDER_SIGNATURE
	});
	const toolUseBlock = (tc) => {
		let argsObj;
		try {
			argsObj = JSON.parse(tc.arguments || "{}");
		} catch {
			logger.warn(`Malformed JSON in fixture tool call arguments for "${tc.name}": ${tc.arguments}`);
			argsObj = {};
		}
		return {
			type: "tool_use",
			id: tc.id || require_helpers.generateToolUseId(),
			name: tc.name,
			input: argsObj
		};
	};
	if (blocks && blocks.length > 0) {
		const ordered = require_helpers.resolveFixtureBlocks(blocks);
		for (const block of ordered) if (block.type === "text") contentBlocks.push({
			type: "text",
			text: block.text
		});
		else contentBlocks.push(toolUseBlock({
			name: block.name,
			arguments: block.arguments,
			id: block.id
		}));
	} else {
		contentBlocks.push({
			type: "text",
			text: content
		});
		for (const tc of toolCalls) contentBlocks.push(toolUseBlock(tc));
	}
	return {
		id: overrides?.id ?? require_helpers.generateMessageId(),
		type: "message",
		role: overrides?.role ?? "assistant",
		content: contentBlocks,
		model: overrides?.model ?? model,
		stop_reason: claudeStopReason(overrides?.finishReason, "tool_use"),
		stop_sequence: null,
		usage: claudeUsage(overrides)
	};
}
async function writeClaudeSSEStream(res, events, optionsOrLatency) {
	const opts = typeof optionsOrLatency === "number" ? { latency: optionsOrLatency } : optionsOrLatency ?? {};
	const latency = opts.latency ?? 0;
	const profile = opts.streamingProfile;
	const { recordedTimings, replaySpeed } = opts;
	const signal = opts.signal;
	const onChunkSent = opts.onChunkSent;
	if (res.writableEnded) return true;
	res.setHeader("Content-Type", "text/event-stream");
	res.setHeader("Cache-Control", "no-cache");
	res.setHeader("Connection", "keep-alive");
	let chunkIndex = 0;
	for (const event of events) {
		const chunkDelay = require_sse_writer.calculateDelay(chunkIndex, profile, latency, recordedTimings, replaySpeed);
		if (chunkDelay > 0) await require_sse_writer.delay(chunkDelay, signal);
		if (signal?.aborted) return false;
		if (res.writableEnded) return true;
		res.write(`event: ${event.type}\ndata: ${JSON.stringify(event)}\n\n`);
		onChunkSent?.();
		if (signal?.aborted) return false;
		chunkIndex++;
	}
	if (!res.writableEnded) res.end();
	return true;
}
async function handleMessages(req, res, raw, fixtures, journal, defaults, setCorsHeaders) {
	const { logger } = defaults;
	setCorsHeaders(res);
	let claudeReq;
	try {
		claudeReq = JSON.parse(raw);
	} catch (parseErr) {
		const detail = parseErr instanceof Error ? parseErr.message : "unknown";
		journal.add({
			method: req.method ?? "POST",
			path: req.url ?? "/v1/messages",
			headers: require_helpers.flattenHeaders(req.headers),
			body: null,
			response: {
				status: 400,
				fixture: null
			}
		});
		require_sse_writer.writeErrorResponse(res, 400, JSON.stringify({ error: {
			message: `Malformed JSON: ${detail}`,
			type: "invalid_request_error"
		} }));
		return;
	}
	const thinkingViolation = validateThinkingInvariants(claudeReq);
	if (thinkingViolation) {
		const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, req.headers);
		const violationMessage = thinkingViolationMessage(thinkingViolation);
		if (effectiveStrict) {
			logger.error(`THINKING: ${violationMessage}`);
			journal.add({
				method: req.method ?? "POST",
				path: req.url ?? "/v1/messages",
				headers: require_helpers.flattenHeaders(req.headers),
				body: null,
				response: {
					status: 400,
					fixture: null,
					...require_helpers.strictOverrideField(defaults.strict, req.headers)
				}
			});
			require_sse_writer.writeErrorResponse(res, 400, JSON.stringify({
				type: "error",
				error: {
					type: "invalid_request_error",
					message: violationMessage
				}
			}));
			return;
		}
		logger.warn(`THINKING: ${violationMessage} (strict off — replaying anyway)`);
	}
	const completionReq = claudeToCompletionRequest(claudeReq);
	completionReq._context = require_helpers.getContext(req);
	const testId = require_helpers.getTestId(req);
	const { fixture, skippedBySequenceOrTurn } = require_router.matchFixtureDiagnostic(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform, require_router.recordMatchOptions(!!defaults.record, defaults.logger));
	if (fixture) {
		journal.incrementFixtureMatchCount(fixture, fixtures, testId);
		logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);
	} else {
		const lastUserMsg = completionReq.messages.filter((m) => m.role === "user").pop();
		const snippet = typeof lastUserMsg?.content === "string" ? lastUserMsg.content.slice(0, 80) : "";
		logger.debug(`No fixture matched for request (model=${completionReq.model ?? "?"}, msg="${snippet}")`);
	}
	if (require_chaos.applyChaos(res, fixture, defaults.chaos, req.headers, journal, {
		method: req.method ?? "POST",
		path: req.url ?? "/v1/messages",
		headers: require_helpers.flattenHeaders(req.headers),
		body: completionReq
	}, fixture ? "fixture" : "proxy", defaults.registry, defaults.logger)) return;
	if (!fixture) {
		if (require_helpers.resolveStrictMode(defaults.strict, req.headers)) {
			const strictStatus = 503;
			const strictMessage = require_helpers.strictNoMatchMessage(skippedBySequenceOrTurn);
			logger.error(require_helpers.strictNoMatchLogLine(req.method ?? "POST", req.url ?? "/v1/messages", skippedBySequenceOrTurn));
			journal.add({
				method: req.method ?? "POST",
				path: req.url ?? "/v1/messages",
				headers: require_helpers.flattenHeaders(req.headers),
				body: completionReq,
				response: {
					status: strictStatus,
					fixture: null,
					...require_helpers.strictOverrideField(defaults.strict, req.headers)
				}
			});
			require_sse_writer.writeErrorResponse(res, strictStatus, JSON.stringify({ error: {
				message: strictMessage,
				type: "invalid_request_error"
			} }));
			return;
		}
		if (defaults.record) {
			const outcome = await require_recorder.proxyAndRecord(req, res, completionReq, "anthropic", req.url ?? "/v1/messages", fixtures, defaults, raw);
			if (outcome === "handled_by_hook") return;
			if (outcome !== "not_configured") {
				journal.add({
					method: req.method ?? "POST",
					path: req.url ?? "/v1/messages",
					headers: require_helpers.flattenHeaders(req.headers),
					body: completionReq,
					response: {
						status: res.statusCode ?? 200,
						fixture: null,
						source: "proxy"
					}
				});
				return;
			}
		}
		journal.add({
			method: req.method ?? "POST",
			path: req.url ?? "/v1/messages",
			headers: require_helpers.flattenHeaders(req.headers),
			body: completionReq,
			response: {
				status: 404,
				fixture: null,
				...require_helpers.strictOverrideField(defaults.strict, req.headers)
			}
		});
		require_sse_writer.writeErrorResponse(res, 404, JSON.stringify({ error: {
			message: "No fixture matched",
			type: "invalid_request_error"
		} }));
		return;
	}
	const response = await require_helpers.resolveResponse(fixture, completionReq);
	const latency = fixture.latency ?? defaults.latency;
	const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);
	if (require_helpers.isErrorResponse(response)) {
		const status = response.status ?? 500;
		journal.add({
			method: req.method ?? "POST",
			path: req.url ?? "/v1/messages",
			headers: require_helpers.flattenHeaders(req.headers),
			body: completionReq,
			response: {
				status,
				fixture
			}
		});
		const anthropicError = {
			type: "error",
			error: {
				type: response.error.type ?? "api_error",
				message: response.error.message
			}
		};
		require_sse_writer.writeErrorResponse(res, status, JSON.stringify(anthropicError), { retryAfter: response.retryAfter });
		return;
	}
	if (require_helpers.isContentWithToolCallsResponse(response)) {
		if (response.webSearches?.length) logger.warn("webSearches in fixture response are not supported for Claude Messages API — ignoring");
		const overrides = require_helpers.extractOverrides(response);
		const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, req.headers);
		const effReasoning = require_helpers.resolveReasoningForModel(response.reasoning, completionReq.model, effectiveStrict, defaults.logger);
		const { reasoningSignature: effReasoningSignature, redactedThinking: effRedactedThinking } = require_helpers.resolveReasoningArtifactsForModel(response.reasoningSignature, response.redactedThinking, completionReq.model, effectiveStrict, defaults.logger);
		const journalEntry = journal.add({
			method: req.method ?? "POST",
			path: req.url ?? "/v1/messages",
			headers: require_helpers.flattenHeaders(req.headers),
			body: completionReq,
			response: {
				status: 200,
				fixture
			}
		});
		if (claudeReq.stream !== true) {
			const body = buildClaudeContentWithToolCallsResponse(response.content ?? "", response.toolCalls ?? [], completionReq.model, logger, effReasoning, overrides, effReasoningSignature, effRedactedThinking, response.blocks);
			res.writeHead(200, { "Content-Type": "application/json" });
			res.end(JSON.stringify(body));
		} else {
			const events = buildClaudeContentWithToolCallsStreamEvents(response.content ?? "", response.toolCalls ?? [], completionReq.model, chunkSize, logger, effReasoning, overrides, effReasoningSignature, effRedactedThinking, response.blocks);
			const interruption = require_interruption.createInterruptionSignal(fixture);
			if (!await writeClaudeSSEStream(res, events, {
				latency,
				streamingProfile: fixture.streamingProfile,
				recordedTimings: fixture.recordedTimings,
				replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
				signal: interruption?.signal,
				onChunkSent: interruption?.tick
			})) {
				if (!res.writableEnded) res.destroy();
				journalEntry.response.interrupted = true;
				journalEntry.response.interruptReason = interruption?.reason();
			}
			interruption?.cleanup();
		}
		return;
	}
	if (require_helpers.isTextResponse(response)) {
		if (response.webSearches?.length) logger.warn("webSearches in fixture response are not supported for Claude Messages API — ignoring");
		const overrides = require_helpers.extractOverrides(response);
		const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, req.headers);
		const effReasoning = require_helpers.resolveReasoningForModel(response.reasoning, completionReq.model, effectiveStrict, defaults.logger);
		const { reasoningSignature: effReasoningSignature, redactedThinking: effRedactedThinking } = require_helpers.resolveReasoningArtifactsForModel(response.reasoningSignature, response.redactedThinking, completionReq.model, effectiveStrict, defaults.logger);
		const journalEntry = journal.add({
			method: req.method ?? "POST",
			path: req.url ?? "/v1/messages",
			headers: require_helpers.flattenHeaders(req.headers),
			body: completionReq,
			response: {
				status: 200,
				fixture
			}
		});
		if (claudeReq.stream !== true) {
			const body = buildClaudeTextResponse(response.content, completionReq.model, effReasoning, overrides, effReasoningSignature, effRedactedThinking);
			res.writeHead(200, { "Content-Type": "application/json" });
			res.end(JSON.stringify(body));
		} else {
			const events = buildClaudeTextStreamEvents(response.content, completionReq.model, chunkSize, effReasoning, overrides, effReasoningSignature, effRedactedThinking);
			const interruption = require_interruption.createInterruptionSignal(fixture);
			if (!await writeClaudeSSEStream(res, events, {
				latency,
				streamingProfile: fixture.streamingProfile,
				recordedTimings: fixture.recordedTimings,
				replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
				signal: interruption?.signal,
				onChunkSent: interruption?.tick
			})) {
				if (!res.writableEnded) res.destroy();
				journalEntry.response.interrupted = true;
				journalEntry.response.interruptReason = interruption?.reason();
			}
			interruption?.cleanup();
		}
		return;
	}
	if (require_helpers.isToolCallResponse(response)) {
		if (response.webSearches?.length) logger.warn("webSearches in fixture response are not supported for Claude Messages API — ignoring");
		const overrides = require_helpers.extractOverrides(response);
		const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, req.headers);
		const effReasoning = require_helpers.resolveReasoningForModel(response.reasoning, completionReq.model, effectiveStrict, defaults.logger);
		const { reasoningSignature: effReasoningSignature, redactedThinking: effRedactedThinking } = require_helpers.resolveReasoningArtifactsForModel(response.reasoningSignature, response.redactedThinking, completionReq.model, effectiveStrict, defaults.logger);
		const journalEntry = journal.add({
			method: req.method ?? "POST",
			path: req.url ?? "/v1/messages",
			headers: require_helpers.flattenHeaders(req.headers),
			body: completionReq,
			response: {
				status: 200,
				fixture
			}
		});
		if (claudeReq.stream !== true) {
			const body = buildClaudeToolCallResponse(response.toolCalls, completionReq.model, logger, effReasoning, overrides, effReasoningSignature, effRedactedThinking);
			res.writeHead(200, { "Content-Type": "application/json" });
			res.end(JSON.stringify(body));
		} else {
			const events = buildClaudeToolCallStreamEvents(response.toolCalls, completionReq.model, chunkSize, logger, effReasoning, overrides, effReasoningSignature, effRedactedThinking);
			const interruption = require_interruption.createInterruptionSignal(fixture);
			if (!await writeClaudeSSEStream(res, events, {
				latency,
				streamingProfile: fixture.streamingProfile,
				recordedTimings: fixture.recordedTimings,
				replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
				signal: interruption?.signal,
				onChunkSent: interruption?.tick
			})) {
				if (!res.writableEnded) res.destroy();
				journalEntry.response.interrupted = true;
				journalEntry.response.interruptReason = interruption?.reason();
			}
			interruption?.cleanup();
		}
		return;
	}
	journal.add({
		method: req.method ?? "POST",
		path: req.url ?? "/v1/messages",
		headers: require_helpers.flattenHeaders(req.headers),
		body: completionReq,
		response: {
			status: 500,
			fixture
		}
	});
	require_sse_writer.writeErrorResponse(res, 500, JSON.stringify({ error: {
		message: "Fixture response did not match any known type",
		type: "server_error"
	} }));
}

//#endregion
exports.handleMessages = handleMessages;
//# sourceMappingURL=messages.cjs.map