spb/chat-spboucher-ai
Public
1import { z } from "zod";2import { guarded, body, sse } from "@/lib/api";3import { getMessage, updateMessage, deactivateFrom } from "@/lib/chat/store";4import { runAssistant } from "@/lib/chat/engine";5import type { Part } from "@/lib/ai/types";67export const maxDuration = 900;89/** Approve or deny tool calls left pending on an assistant message, then continue the turn. */10export const POST = guarded(async (req) => {11 const b = await body(req, z.object({ messageId: z.string(), approve: z.array(z.string()), deny: z.array(z.string()).optional() }));12 const m = await getMessage(b.messageId);13 if (!m || m.role !== "assistant" || !m.parent_id) return Response.json({ error: "Message introuvable" }, { status: 404 });14 const parts = (m.parts as Part[]).map(p => p.type === "tool_call" && p.status === "pending_approval" && b.deny?.includes(p.id) ? { ...p, status: "denied" as const, error: "Refusé par le propriétaire" } : p);15 await updateMessage(m.id, { parts, status: "complete" });16 // The continuation is a fresh assistant turn that sees the previous partial answer + tool decisions.17 await deactivateFrom(m.conversation_id, m.id, false);18 return sse(async (emit, signal) => {19 emit("conversation", { id: m.conversation_id, userMessageId: m.parent_id, continued: m.id });20 await runAssistant({ conversationId: m.conversation_id, modelId: m.model_id ?? undefined, routingMode: "manual", parentMessageId: m.id, signal, emit, approvedToolCallIds: b.approve });21 }, req.signal);22});23