import runDbClient_default from "../../../data/db/runDbClient.js";
import { getLogger as getLogger$1 } from "../../../logger.js";
import { contextValidationMiddleware } from "../context/validation.js";
import { handleContextResolution } from "../context/context.js";
import "../context/index.js";
import { ImageContentItemSchema } from "../types/chat.js";
import { extractTextFromParts, getMessagePartsFromOpenAIContent } from "../utils/message-parts.js";
import { buildPersistedMessageContent } from "../services/blob-storage/file-upload-helpers.js";
import { agentExecutionWorkflow, toolApprovalHook } from "../workflow/functions/agentExecution.js";
import { OpenAPIHono, z } from "@hono/zod-openapi";
import { PartSchema, createApiError, createMessage, createOrGetConversation, generateId, getConversationId, getWorkflowExecution } from "@inkeep/agents-core";
import { createProtectedRoute, inheritedRunApiKeyAuth } from "@inkeep/agents-core/middleware";
import { getRun, start } from "workflow/api";
import { stream } from "hono/streaming";

//#region src/domains/run/routes/executions.ts
const app = new OpenAPIHono();
const logger = getLogger$1("executionsHandler");
const createExecutionRoute = createProtectedRoute({
	method: "post",
	path: "/executions",
	tags: ["Executions"],
	summary: "Create durable agent execution",
	description: "Starts a durable workflow-backed agent execution. Returns an SSE stream with an x-workflow-run-id header for reconnection.",
	security: [{ bearerAuth: [] }],
	permission: inheritedRunApiKeyAuth(),
	request: { body: { content: { "application/json": { schema: z.object({
		messages: z.array(z.object({
			role: z.enum([
				"system",
				"user",
				"assistant",
				"function",
				"tool"
			]),
			content: z.union([z.string(), z.array(z.discriminatedUnion("type", [z.object({
				type: z.literal("text"),
				text: z.string()
			}), ImageContentItemSchema]))]).describe("The message content")
		})).describe("The conversation messages"),
		conversationId: z.string().optional(),
		forwardedHeaders: z.record(z.string(), z.string()).optional()
	}) } } } },
	responses: {
		200: {
			description: "Streaming SSE response. The x-workflow-run-id header contains the run ID for reconnection.",
			headers: z.object({
				"Content-Type": z.string().default("text/event-stream"),
				"x-workflow-run-id": z.string()
			}),
			content: { "text/event-stream": { schema: z.string() } }
		},
		400: {
			description: "Invalid request",
			content: { "application/json": { schema: z.object({ error: z.string() }) } }
		},
		404: {
			description: "Agent not found",
			content: { "application/json": { schema: z.object({ error: z.string() }) } }
		}
	}
});
const getExecutionRoute = createProtectedRoute({
	method: "get",
	path: "/executions/:executionId",
	tags: ["Executions"],
	summary: "Get execution status",
	description: "Returns the status of a durable execution.",
	security: [{ bearerAuth: [] }],
	permission: inheritedRunApiKeyAuth(),
	request: { params: z.object({ executionId: z.string() }) },
	responses: {
		200: {
			description: "Execution status",
			content: { "application/json": { schema: z.object({
				id: z.string(),
				status: z.enum([
					"running",
					"suspended",
					"completed",
					"failed"
				]),
				conversationId: z.string(),
				agentId: z.string(),
				createdAt: z.string(),
				updatedAt: z.string()
			}) } }
		},
		404: {
			description: "Execution not found",
			content: { "application/json": { schema: z.object({ error: z.string() }) } }
		}
	}
});
const reconnectExecutionStreamRoute = createProtectedRoute({
	method: "get",
	path: "/executions/:executionId/stream",
	tags: ["Executions"],
	summary: "Reconnect to execution stream",
	description: "Reconnects to the SSE stream of an existing durable execution.",
	security: [{ bearerAuth: [] }],
	permission: inheritedRunApiKeyAuth(),
	request: { params: z.object({ executionId: z.string() }) },
	responses: {
		200: {
			description: "SSE stream",
			content: { "text/event-stream": { schema: z.string() } }
		},
		404: {
			description: "Execution not found",
			content: { "application/json": { schema: z.object({ error: z.string() }) } }
		}
	}
});
const approveToolCallRoute = createProtectedRoute({
	method: "post",
	path: "/executions/:executionId/approvals/:toolCallId",
	tags: ["Executions"],
	summary: "Approve or deny a tool call",
	description: "Resumes a suspended durable execution by approving or denying a pending tool call.",
	security: [{ bearerAuth: [] }],
	permission: inheritedRunApiKeyAuth(),
	request: {
		params: z.object({
			executionId: z.string(),
			toolCallId: z.string()
		}),
		body: { content: { "application/json": { schema: z.object({
			approved: z.boolean(),
			reason: z.string().optional()
		}) } } }
	},
	responses: {
		200: {
			description: "Tool call approval submitted",
			content: { "application/json": { schema: z.object({ success: z.boolean() }) } }
		},
		404: {
			description: "Execution not found",
			content: { "application/json": { schema: z.object({ error: z.string() }) } }
		}
	}
});
app.use("/executions", contextValidationMiddleware);
app.use("/executions/*", contextValidationMiddleware);
app.openapi(createExecutionRoute, async (c) => {
	const executionContext = c.get("executionContext");
	const { tenantId, projectId, agentId, resolvedRef } = executionContext;
	const body = c.req.valid("json");
	const conversationId = body.conversationId || getConversationId();
	await handleContextResolution({
		executionContext,
		conversationId,
		headers: {},
		credentialStores: c.get("credentialStores")
	});
	const lastUserMessage = body.messages.filter((msg) => msg.role === "user").slice(-1)[0];
	const messageParts = z.array(PartSchema).parse(lastUserMessage ? getMessagePartsFromOpenAIContent(lastUserMessage.content) : []);
	const userMessage = extractTextFromParts(messageParts);
	const messageId = generateId();
	const messageContent = await buildPersistedMessageContent(userMessage, messageParts, {
		tenantId,
		projectId,
		conversationId,
		messageId
	});
	const fullAgent = executionContext.project.agents[agentId];
	const agentKeys = Object.keys(fullAgent?.subAgents || {});
	const activeSubAgentId = fullAgent?.defaultSubAgentId || agentKeys[0] || "";
	await createOrGetConversation(runDbClient_default)({
		tenantId,
		projectId,
		id: conversationId,
		agentId,
		activeSubAgentId,
		ref: resolvedRef
	});
	await createMessage(runDbClient_default)({
		scopes: {
			tenantId,
			projectId
		},
		data: {
			id: messageId,
			conversationId,
			role: "user",
			content: messageContent,
			visibility: "user-facing",
			messageType: "chat"
		}
	});
	const requestId = `exec-${generateId()}`;
	const emitOperations = c.req.header("x-emit-operations") === "true";
	const run = await start(agentExecutionWorkflow, [{
		tenantId,
		projectId,
		agentId,
		conversationId,
		userMessage,
		messageParts: messageParts.length > 0 ? messageParts : void 0,
		requestId,
		resolvedRef,
		forwardedHeaders: body.forwardedHeaders,
		emitOperations: emitOperations || void 0
	}]);
	logger.info({
		runId: run.runId,
		conversationId,
		agentId
	}, "Durable execution started");
	c.header("Content-Type", "text/event-stream");
	c.header("Cache-Control", "no-cache");
	c.header("Connection", "keep-alive");
	c.header("x-workflow-run-id", run.runId);
	return stream(c, async (s) => {
		try {
			const reader = run.readable.getReader();
			while (true) {
				const { done, value } = await reader.read();
				if (done) break;
				await s.write(value);
			}
		} catch (error) {
			logger.error({
				error,
				runId: run.runId
			}, "Error streaming durable execution");
			await s.write(`event: error\ndata: ${JSON.stringify({ error: "Stream error" })}\n\n`);
		}
	});
});
app.openapi(getExecutionRoute, async (c) => {
	const { tenantId, projectId } = c.get("executionContext");
	const { executionId } = c.req.valid("param");
	const execution = await getWorkflowExecution(runDbClient_default)({
		tenantId,
		projectId,
		id: executionId
	});
	if (!execution) throw createApiError({
		code: "not_found",
		message: "Execution not found"
	});
	return c.json({
		id: execution.id,
		status: execution.status,
		conversationId: execution.conversationId,
		agentId: execution.agentId,
		createdAt: execution.createdAt,
		updatedAt: execution.updatedAt
	}, 200);
});
app.openapi(reconnectExecutionStreamRoute, async (c) => {
	const { tenantId, projectId } = c.get("executionContext");
	const { executionId } = c.req.valid("param");
	const startIndexHeader = c.req.header("x-stream-start-index");
	const startIndex = startIndexHeader ? Number.parseInt(startIndexHeader, 10) : 0;
	if (!await getWorkflowExecution(runDbClient_default)({
		tenantId,
		projectId,
		id: executionId
	})) throw createApiError({
		code: "not_found",
		message: "Execution not found"
	});
	const run = getRun(executionId);
	c.header("Content-Type", "text/event-stream");
	c.header("Cache-Control", "no-cache");
	c.header("Connection", "keep-alive");
	return stream(c, async (s) => {
		try {
			const reader = run.getReadable({ startIndex }).getReader();
			while (true) {
				const { done, value } = await reader.read();
				if (done) break;
				await s.write(value);
			}
		} catch (error) {
			logger.error({
				error,
				executionId
			}, "Error reconnecting to execution stream");
			await s.write(`event: error\ndata: ${JSON.stringify({ error: "Stream error" })}\n\n`);
		}
	});
});
app.openapi(approveToolCallRoute, async (c) => {
	const { tenantId, projectId } = c.get("executionContext");
	const { executionId, toolCallId } = c.req.valid("param");
	const { approved, reason } = c.req.valid("json");
	const execution = await getWorkflowExecution(runDbClient_default)({
		tenantId,
		projectId,
		id: executionId
	});
	if (!execution) throw createApiError({
		code: "not_found",
		message: "Execution not found"
	});
	const token = `tool-approval:${execution.conversationId}:${executionId}:${toolCallId}`;
	try {
		await toolApprovalHook.resume(token, {
			approved,
			reason: approved ? void 0 : reason
		});
	} catch (error) {
		const message = error instanceof Error ? error.message : String(error);
		if (!message.includes("not found") && !message.includes("already")) throw error;
		logger.warn({
			executionId,
			toolCallId,
			message
		}, "Tool approval already processed (idempotent)");
	}
	return c.json({ success: true }, 200);
});
var executions_default = app;

//#endregion
export { executions_default as default };