import { STREAM_BUFFER_MAX_SIZE_BYTES, STREAM_MAX_LIFETIME_MS, STREAM_TEXT_GAP_THRESHOLD_MS } from "../constants/execution-limits/index.js";
import { setSpanWithError as setSpanWithError$1, tracer } from "../utils/tracer.js";
import { SPAN_KEYS } from "@inkeep/agents-core";
import { parsePartialJson } from "ai";

//#region src/domains/run/stream/stream-helpers.ts
var SSEStreamHelper = class {
	isTextStreaming = false;
	queuedEvents = [];
	toolCallIndexById = /* @__PURE__ */ new Map();
	constructor(stream, requestId, timestamp) {
		this.stream = stream;
		this.requestId = requestId;
		this.timestamp = timestamp;
	}
	/**
	* Write the initial role message
	*/
	async writeRole(role = "assistant") {
		await this.stream.writeSSE({ data: JSON.stringify({
			id: this.requestId,
			object: "chat.completion.chunk",
			created: this.timestamp,
			choices: [{
				index: 0,
				delta: { role },
				finish_reason: null
			}]
		}) });
	}
	/**
	* Write content chunk
	*/
	async writeContent(content) {
		await this.stream.writeSSE({ data: JSON.stringify({
			id: this.requestId,
			object: "chat.completion.chunk",
			created: this.timestamp,
			choices: [{
				index: 0,
				delta: { content },
				finish_reason: null
			}]
		}) });
	}
	getToolIndex(toolCallId) {
		const existing = this.toolCallIndexById.get(toolCallId);
		if (existing !== void 0) return existing;
		const next = this.toolCallIndexById.size;
		this.toolCallIndexById.set(toolCallId, next);
		return next;
	}
	async writeToolCallsDelta(toolCalls) {
		await this.stream.writeSSE({ data: JSON.stringify({
			id: this.requestId,
			object: "chat.completion.chunk",
			created: this.timestamp,
			choices: [{
				index: 0,
				delta: { tool_calls: toolCalls },
				finish_reason: null
			}]
		}) });
	}
	/**
	* Stream text word by word with optional delay
	*/
	async streamText(text, delayMs = 100) {
		const words = text.split(" ");
		this.isTextStreaming = true;
		try {
			for (let i = 0; i < words.length; i++) {
				await this.stream.sleep(delayMs);
				const content = i === 0 ? words[i] : ` ${words[i]}`;
				await this.writeContent(content);
			}
		} finally {
			this.isTextStreaming = false;
			await this.flushQueuedOperations();
		}
	}
	async streamData(data) {
		await this.writeContent(JSON.stringify(data));
	}
	/**
	* Write error message or error event
	*/
	async writeError(error) {
		const errorMessage = typeof error === "string" ? error : error.message;
		await this.writeContent(`\n\n${errorMessage}`);
	}
	/**
	* Write the final completion message
	*/
	async writeCompletion(finishReason = "stop") {
		await this.stream.writeSSE({ data: JSON.stringify({
			id: this.requestId,
			object: "chat.completion.chunk",
			created: this.timestamp,
			choices: [{
				index: 0,
				delta: {},
				finish_reason: finishReason
			}]
		}) });
	}
	async writeData(type, data) {
		await this.stream.writeSSE({ data: JSON.stringify({
			id: this.requestId,
			object: "chat.completion.chunk",
			created: this.timestamp,
			choices: [{
				index: 0,
				delta: { content: JSON.stringify({
					type,
					data
				}) },
				finish_reason: null
			}]
		}) });
	}
	async writeToolInputStart(params) {
		const index = this.getToolIndex(params.toolCallId);
		await this.writeToolCallsDelta([{
			index,
			id: params.toolCallId,
			type: "function",
			function: {
				name: params.toolName,
				arguments: ""
			}
		}]);
	}
	async writeToolInputDelta(params) {
		const index = this.getToolIndex(params.toolCallId);
		await this.writeToolCallsDelta([{
			index,
			id: null,
			type: null,
			function: {
				name: null,
				arguments: params.inputTextDelta
			}
		}]);
	}
	async writeToolInputAvailable(params) {
		const fullArgs = JSON.stringify(params.input ?? {});
		if (fullArgs) await this.writeToolInputDelta({
			toolCallId: params.toolCallId,
			inputTextDelta: fullArgs
		});
	}
	async writeToolOutputAvailable(params) {
		await this.writeContent(JSON.stringify({
			type: "tool-output-available",
			toolCallId: params.toolCallId,
			output: params.output
		}));
	}
	async writeToolOutputError(params) {
		await this.writeContent(JSON.stringify({
			type: "tool-output-error",
			toolCallId: params.toolCallId,
			errorText: params.errorText,
			output: params.output ?? null
		}));
	}
	async writeToolApprovalRequest(params) {
		await this.writeContent(JSON.stringify({
			type: "tool-approval-request",
			approvalId: params.approvalId,
			toolCallId: params.toolCallId,
			...params.toolName && { toolName: params.toolName },
			...params.input !== void 0 && { input: params.input }
		}));
	}
	async writeToolOutputDenied(params) {
		await this.writeContent(JSON.stringify({
			type: "tool-output-denied",
			toolCallId: params.toolCallId
		}));
	}
	async writeSummary(summary) {
		if (this.isTextStreaming) {
			this.queuedEvents.push({
				type: "data-summary",
				event: summary
			});
			return;
		}
		await this.flushQueuedOperations();
		await this.writeData("data-summary", summary);
	}
	async writeOperation(operation) {
		if (this.isTextStreaming) {
			this.queuedEvents.push({
				type: "data-operation",
				event: operation
			});
			return;
		}
		await this.flushQueuedOperations();
		await this.writeData("data-operation", operation);
	}
	/**
	* Flush all queued operations in order after text streaming completes
	*/
	async flushQueuedOperations() {
		if (this.queuedEvents.length === 0) return;
		const eventsToFlush = [...this.queuedEvents];
		this.queuedEvents = [];
		for (const event of eventsToFlush) await this.writeData(event.type, event.event);
	}
	/**
	* Complete the stream with finish reason
	*/
	async complete(finishReason = "stop") {
		await this.flushQueuedOperations();
		await this.writeCompletion(finishReason);
	}
};
/**
* Factory function to create SSE stream helper
*/
function createSSEStreamHelper(stream, requestId, timestamp) {
	return new SSEStreamHelper(stream, requestId, timestamp);
}
var VercelDataStreamHelper = class VercelDataStreamHelper {
	textId = null;
	jsonBuffer = "";
	sentItems = /* @__PURE__ */ new Map();
	completedItems = /* @__PURE__ */ new Set();
	static MAX_BUFFER_SIZE = STREAM_BUFFER_MAX_SIZE_BYTES;
	isCompleted = false;
	isTextStreaming = false;
	queuedEvents = [];
	lastTextEndTimestamp = 0;
	connectionDropTimer;
	constructor(writer) {
		this.writer = writer;
		this.connectionDropTimer = setTimeout(() => {
			this.forceCleanup("Connection lifetime exceeded");
		}, STREAM_MAX_LIFETIME_MS);
	}
	setSessionId(_sessionId) {}
	async writeRole(_ = "assistant") {}
	async writeContent(content) {
		if (this.isCompleted) {
			console.warn("Attempted to write content to completed stream");
			return;
		}
		if (!this.textId) this.textId = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
		if (this.jsonBuffer.length + content.length > VercelDataStreamHelper.MAX_BUFFER_SIZE) {
			const newBuffer = this.truncateJsonBufferSafely(this.jsonBuffer);
			if (newBuffer.length === this.jsonBuffer.length) {
				console.warn("VercelDataStreamHelper: Could not find safe JSON truncation point, clearing buffer");
				this.jsonBuffer = "";
				this.sentItems.clear();
			} else {
				this.jsonBuffer = newBuffer;
				this.reindexSentItems();
			}
		}
		this.jsonBuffer += content;
		const { value, state } = await parsePartialJson(this.jsonBuffer);
		if (!["repaired-parse", "successful-parse"].includes(state)) return;
		if (!Array.isArray(value)) return;
		for (let i = 0; i < value.length; i++) {
			const { type, ...data } = value[i];
			const currentContent = JSON.stringify(data);
			if (currentContent !== this.sentItems.get(i)) {
				const chunk = {
					type: "data-component",
					id: `${this.textId}-${i}`,
					data: {
						type,
						...data
					}
				};
				this.writer.write(chunk);
				this.sentItems.set(i, currentContent);
			}
		}
	}
	async streamText(text, delayMs = 100) {
		if (this.isCompleted) {
			console.warn("Attempted to stream text to completed stream");
			return;
		}
		if (!this.textId) this.textId = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
		const id = this.textId;
		const startTime = Date.now();
		if ((this.lastTextEndTimestamp > 0 ? startTime - this.lastTextEndTimestamp : Number.MAX_SAFE_INTEGER) >= STREAM_TEXT_GAP_THRESHOLD_MS) await this.flushQueuedOperations();
		this.isTextStreaming = true;
		try {
			this.writer.write({
				type: "text-start",
				id
			});
			if (delayMs > 0) await new Promise((r) => setTimeout(r, delayMs));
			this.writer.write({
				type: "text-delta",
				id,
				delta: text
			});
			this.writer.write({
				type: "text-end",
				id
			});
			this.lastTextEndTimestamp = Date.now();
		} finally {
			this.isTextStreaming = false;
		}
	}
	async writeData(type, data) {
		if (this.isCompleted) {
			console.warn("Attempted to write data to completed stream");
			return;
		}
		if (type === "data-artifact") {
			const now = Date.now();
			const gapFromLastTextEnd = this.lastTextEndTimestamp > 0 ? now - this.lastTextEndTimestamp : Number.MAX_SAFE_INTEGER;
			if (this.isTextStreaming || gapFromLastTextEnd < STREAM_TEXT_GAP_THRESHOLD_MS) {
				this.writer.write({
					type: `${type}`,
					data
				});
				return;
			}
		}
		this.writer.write({
			type: `${type}`,
			data
		});
	}
	async writeError(error) {
		if (this.isCompleted) {
			console.warn("Attempted to write error to completed stream");
			return;
		}
		if (typeof error === "string") this.writer.write({
			type: "error",
			message: error,
			severity: "error",
			timestamp: Date.now()
		});
		else this.writer.write({
			...error,
			type: "error"
		});
	}
	async writeToolInputStart(params) {
		if (this.isCompleted) return;
		this.writer.write({
			type: "tool-input-start",
			toolCallId: params.toolCallId,
			toolName: params.toolName
		});
	}
	async writeToolInputDelta(params) {
		if (this.isCompleted) return;
		this.writer.write({
			type: "tool-input-delta",
			toolCallId: params.toolCallId,
			inputTextDelta: params.inputTextDelta
		});
	}
	async writeToolInputAvailable(params) {
		if (this.isCompleted) return;
		this.writer.write({
			type: "tool-input-available",
			toolCallId: params.toolCallId,
			toolName: params.toolName,
			input: params.input,
			...params.providerMetadata ? { providerMetadata: params.providerMetadata } : {}
		});
	}
	async writeToolOutputAvailable(params) {
		if (this.isCompleted) return;
		this.writer.write({
			type: "tool-output-available",
			toolCallId: params.toolCallId,
			output: params.output
		});
	}
	async writeToolOutputError(params) {
		if (this.isCompleted) return;
		this.writer.write({
			type: "tool-output-error",
			toolCallId: params.toolCallId,
			errorText: params.errorText
		});
	}
	async writeToolApprovalRequest(params) {
		if (this.isCompleted) return;
		this.writer.write({
			type: "tool-approval-request",
			approvalId: params.approvalId,
			toolCallId: params.toolCallId
		});
	}
	async writeToolOutputDenied(params) {
		if (this.isCompleted) return;
		this.writer.write({
			type: "tool-output-denied",
			toolCallId: params.toolCallId
		});
	}
	async streamData(data) {
		await this.writeContent(JSON.stringify(data));
	}
	async mergeStream(stream) {
		if (this.isCompleted) {
			console.warn("Attempted to merge stream to completed stream");
			return;
		}
		this.writer.merge(stream);
	}
	/**
	* Clean up all memory allocations
	* Should be called when the stream helper is no longer needed
	*/
	cleanup() {
		if (this.connectionDropTimer) {
			clearTimeout(this.connectionDropTimer);
			this.connectionDropTimer = void 0;
		}
		this.jsonBuffer = "";
		this.sentItems.clear();
		this.completedItems.clear();
		this.textId = null;
		this.queuedEvents = [];
		this.isTextStreaming = false;
	}
	/**
	* JSON-aware buffer truncation that preserves complete JSON structures
	*/
	truncateJsonBufferSafely(buffer) {
		const keepSize = Math.floor(VercelDataStreamHelper.MAX_BUFFER_SIZE * .6);
		if (buffer.length <= keepSize) return buffer;
		let depth = 0;
		let inString = false;
		let escaping = false;
		let lastCompleteStructureEnd = -1;
		for (let i = Math.min(keepSize + 1e3, buffer.length - 1); i >= keepSize; i--) {
			const char = buffer[i];
			if (escaping) {
				escaping = false;
				continue;
			}
			if (char === "\\") {
				escaping = true;
				continue;
			}
			if (char === "\"") {
				inString = !inString;
				continue;
			}
			if (inString) continue;
			if (char === "}" || char === "]") depth++;
			else if (char === "{" || char === "[") {
				depth--;
				if (depth === 0) {
					lastCompleteStructureEnd = i - 1;
					break;
				}
			}
		}
		if (lastCompleteStructureEnd > 0) return buffer.slice(lastCompleteStructureEnd + 1);
		for (let i = keepSize; i < Math.min(keepSize + 500, buffer.length); i++) if (buffer[i] === "\n" && buffer[i + 1] && buffer[i + 1].match(/[{[]]/)) return buffer.slice(i + 1);
		return buffer;
	}
	/**
	* Reindex sent items after buffer truncation
	*/
	reindexSentItems() {
		this.sentItems.clear();
		this.completedItems.clear();
	}
	/**
	* Force cleanup on connection drop or timeout
	*/
	forceCleanup(reason) {
		tracer.startActiveSpan("stream.force_cleanup", (span) => {
			span.setAttributes({
				[SPAN_KEYS.STREAM_CLEANUP_REASON]: reason,
				[SPAN_KEYS.STREAM_MAX_LIFETIME_MS]: STREAM_MAX_LIFETIME_MS,
				[SPAN_KEYS.STREAM_BUFFER_SIZE_BYTES]: this.jsonBuffer.length,
				[SPAN_KEYS.STREAM_SENT_ITEMS_COUNT]: this.sentItems.size,
				[SPAN_KEYS.STREAM_COMPLETED_ITEMS_COUNT]: this.completedItems.size
			});
			setSpanWithError$1(span, /* @__PURE__ */ new Error(`Stream force cleanup: ${reason}`));
			span.end();
		});
		console.warn(`VercelDataStreamHelper: Forcing cleanup - ${reason}`);
		this.isCompleted = true;
		this.cleanup();
		try {
			if (this.writer && !this.isCompleted) this.writer.write({
				type: "error",
				message: `Stream terminated: ${reason}`,
				severity: "error",
				timestamp: Date.now()
			});
		} catch (_e) {}
	}
	/**
	* Check if the stream has been completed and cleaned up
	*/
	isStreamCompleted() {
		return this.isCompleted;
	}
	/**
	* Get current memory usage stats (for debugging/monitoring)
	*/
	getMemoryStats() {
		return {
			bufferSize: this.jsonBuffer.length,
			sentItemsCount: this.sentItems.size,
			completedItemsCount: this.completedItems.size,
			isCompleted: this.isCompleted
		};
	}
	async writeSummary(summary) {
		if (this.isCompleted) {
			console.warn("Attempted to write summary to completed stream");
			return;
		}
		const now = Date.now();
		const gapFromLastTextEnd = this.lastTextEndTimestamp > 0 ? now - this.lastTextEndTimestamp : Number.MAX_SAFE_INTEGER;
		if (this.isTextStreaming || gapFromLastTextEnd < STREAM_TEXT_GAP_THRESHOLD_MS) {
			this.queuedEvents.push({
				type: "data-summary",
				event: summary
			});
			return;
		}
		await this.flushQueuedOperations();
		await this.writer.write({
			id: "id" in summary ? summary.id : void 0,
			type: "data-summary",
			data: summary
		});
	}
	async writeOperation(operation) {
		if (this.isCompleted) {
			console.warn("Attempted to write operation to completed stream");
			return;
		}
		const now = Date.now();
		const gapFromLastTextEnd = this.lastTextEndTimestamp > 0 ? now - this.lastTextEndTimestamp : Number.MAX_SAFE_INTEGER;
		if (operation.type !== "tool_call" && operation.type !== "tool_result" && (this.isTextStreaming || gapFromLastTextEnd < STREAM_TEXT_GAP_THRESHOLD_MS)) {
			this.queuedEvents.push({
				type: "data-operation",
				event: operation
			});
			return;
		}
		await this.flushQueuedOperations();
		this.writer.write({
			id: "id" in operation ? operation.id : void 0,
			type: "data-operation",
			data: operation
		});
	}
	/**
	* Flush all queued operations in order after text streaming completes
	*/
	async flushQueuedOperations() {
		if (this.queuedEvents.length === 0) return;
		const eventsToFlush = [...this.queuedEvents];
		this.queuedEvents = [];
		for (const event of eventsToFlush) this.writer.write({
			id: "id" in event.event ? event.event.id : void 0,
			type: event.type,
			data: event.event
		});
	}
	async writeCompletion(_finishReason = "stop") {}
	async writeDone() {}
	/**
	* Complete the stream and clean up all memory
	* This is the primary cleanup point to prevent memory leaks between requests
	*/
	async complete() {
		if (this.isCompleted) return;
		await this.flushQueuedOperations();
		this.isCompleted = true;
		this.cleanup();
	}
};
function createVercelStreamHelper(writer) {
	return new VercelDataStreamHelper(writer);
}
/**
* Buffering Stream Helper that captures content instead of streaming
* Used for MCP tool responses and non-streaming API responses that require a single complete message
*/
var BufferingStreamHelper = class {
	capturedText = "";
	capturedData = [];
	capturedOperations = [];
	capturedSummaries = [];
	hasError = false;
	errorMessage = "";
	setSessionId(_sessionId) {}
	async writeRole(_role) {}
	async writeContent(content) {
		this.capturedText += content;
	}
	async streamText(text, _delayMs) {
		this.capturedText += text;
	}
	async streamData(data) {
		this.capturedData.push(data);
	}
	async streamSummary(summary) {
		this.capturedSummaries.push(summary);
	}
	async streamOperation(operation) {
		this.capturedOperations.push(operation);
	}
	async writeData(_type, data) {
		this.capturedData.push(data);
	}
	async writeSummary(summary) {
		this.capturedSummaries.push(summary);
	}
	async writeOperation(operation) {
		this.capturedOperations.push(operation);
	}
	async writeError(error) {
		this.hasError = true;
		this.errorMessage = typeof error === "string" ? error : error.message;
	}
	async writeToolInputStart(params) {
		this.capturedData.push({
			type: "tool-input-start",
			...params
		});
	}
	async writeToolInputDelta(params) {
		this.capturedData.push({
			type: "tool-input-delta",
			...params
		});
	}
	async writeToolInputAvailable(params) {
		this.capturedData.push({
			type: "tool-input-available",
			...params
		});
	}
	async writeToolOutputAvailable(params) {
		this.capturedData.push({
			type: "tool-output-available",
			...params
		});
	}
	async writeToolOutputError(params) {
		this.capturedData.push({
			type: "tool-output-error",
			...params,
			output: params.output ?? null
		});
	}
	async writeToolApprovalRequest(params) {
		this.capturedData.push({
			type: "tool-approval-request",
			...params
		});
	}
	async writeToolOutputDenied(params) {
		this.capturedData.push({
			type: "tool-output-denied",
			...params
		});
	}
	async complete() {}
	/**
	* Get the captured response for non-streaming output
	*/
	getCapturedResponse() {
		return {
			text: this.capturedText,
			data: this.capturedData,
			operations: this.capturedOperations,
			hasError: this.hasError,
			errorMessage: this.errorMessage
		};
	}
};
function createBufferingStreamHelper() {
	return new BufferingStreamHelper();
}
const createMCPStreamHelper = createBufferingStreamHelper;

//#endregion
export { BufferingStreamHelper, SSEStreamHelper, VercelDataStreamHelper, createBufferingStreamHelper, createMCPStreamHelper, createSSEStreamHelper, createVercelStreamHelper };