import { NextRequest, NextResponse } from "next/server"; import { getServerSession } from "next-auth/next"; import { authOptions } from "@/lib/auth/authOptions"; import { query } from "@/lib/db-postgres"; const AGENT_RUNNER_URL = process.env.AGENT_RUNNER_URL ?? "http://localhost:3333"; export async function POST( req: NextRequest, { params }: { params: Promise<{ projectId: string }> } ) { const session = await getServerSession(authOptions); if (!session?.user?.email) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } const { projectId } = await params; const { message } = await req.json(); if (!message?.trim()) { return NextResponse.json({ error: '"message" is required' }, { status: 400 }); } // Load project context to inject into the orchestrator session let projectContext = ""; try { const rows = await query<{ data: any }>( `SELECT data FROM fs_projects WHERE id = $1 LIMIT 1`, [projectId] ); if (rows.length > 0) { const p = rows[0].data; const lines = [ `Project: ${p.productName ?? p.name ?? "Unnamed"}`, p.productVision ? `Vision: ${p.productVision}` : null, p.giteaRepo ? `Gitea repo: ${p.giteaRepo}` : null, p.coolifyAppUuid ? `Coolify app UUID: ${p.coolifyAppUuid}` : null, p.deploymentUrl ? `Live URL: ${p.deploymentUrl}` : null, p.theiaWorkspaceUrl ? `IDE: ${p.theiaWorkspaceUrl}` : null, ].filter(Boolean); projectContext = lines.join("\n"); } } catch { // Non-fatal — orchestrator still works without extra context } // Use projectId as the session ID so each project has its own conversation const sessionId = `project_${projectId}`; // First message in a new session? Prepend project context const enrichedMessage = projectContext ? `[Project context]\n${projectContext}\n\n[User message]\n${message}` : message; try { const res = await fetch(`${AGENT_RUNNER_URL}/orchestrator/chat`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ message: enrichedMessage, session_id: sessionId }), signal: AbortSignal.timeout(120_000), // 2 min — agents can take time }); if (!res.ok) { const errText = await res.text(); return NextResponse.json( { error: `Agent runner error: ${res.status} — ${errText.slice(0, 200)}` }, { status: 502 } ); } const data = await res.json(); return NextResponse.json({ reply: data.reply, reasoning: data.reasoning ?? null, toolCalls: data.toolCalls ?? [], turns: data.turns ?? 0, model: data.model || null, sessionId, }); } catch (err) { const msg = err instanceof Error ? err.message : String(err); return NextResponse.json( { error: msg.includes("fetch") ? "Agent runner is offline" : msg }, { status: 503 } ); } } // Clear session for this project export async function DELETE( _req: NextRequest, { params }: { params: Promise<{ projectId: string }> } ) { const session = await getServerSession(authOptions); if (!session?.user?.email) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } const { projectId } = await params; const sessionId = `project_${projectId}`; try { await fetch(`${AGENT_RUNNER_URL}/orchestrator/sessions/${sessionId}`, { method: "DELETE", }); } catch { // Best-effort } return NextResponse.json({ cleared: true }); }