agent-chat/route.ts: - Loads conversation history from chat_conversations before each turn - Passes history + knowledge context to agent runner - Saves returned history back to chat_conversations after each turn - Saves AI-generated memory updates to fs_knowledge_items knowledge/route.ts (new): - GET /api/projects/[id]/knowledge — list all knowledge items - POST /api/projects/[id]/knowledge — add/update item by key - DELETE /api/projects/[id]/knowledge?id=xxx — remove item OrchestratorChat.tsx: - Added "Saved to memory" label for save_memory tool calls Made-with: Cursor
104 lines
3.6 KiB
TypeScript
104 lines
3.6 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { getServerSession } from "next-auth/next";
|
|
import { authOptions } from "@/lib/auth/authOptions";
|
|
import { query } from "@/lib/db-postgres";
|
|
|
|
async function assertOwnership(projectId: string, email: string): Promise<boolean> {
|
|
const rows = await query(
|
|
`SELECT p.id FROM fs_projects p
|
|
JOIN fs_users u ON u.id = p.user_id
|
|
WHERE p.id = $1 AND u.data->>'email' = $2 LIMIT 1`,
|
|
[projectId, email]
|
|
);
|
|
return rows.length > 0;
|
|
}
|
|
|
|
// GET /api/projects/[projectId]/knowledge
|
|
export async function GET(
|
|
_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;
|
|
if (!(await assertOwnership(projectId, session.user.email))) {
|
|
return NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
}
|
|
|
|
const rows = await query<{ id: string; data: any; updated_at: string }>(
|
|
`SELECT id, data, updated_at FROM fs_knowledge_items WHERE project_id = $1 ORDER BY updated_at DESC`,
|
|
[projectId]
|
|
);
|
|
|
|
return NextResponse.json({
|
|
items: rows.map((r) => ({ id: r.id, ...r.data, updatedAt: r.updated_at })),
|
|
});
|
|
}
|
|
|
|
// POST /api/projects/[projectId]/knowledge — add or update an item
|
|
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;
|
|
if (!(await assertOwnership(projectId, session.user.email))) {
|
|
return NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
}
|
|
|
|
const { key, type, value } = await req.json();
|
|
if (!key || !value) {
|
|
return NextResponse.json({ error: "key and value are required" }, { status: 400 });
|
|
}
|
|
|
|
const itemType = type ?? "note";
|
|
const data = JSON.stringify({ key, type: itemType, value, source: "user" });
|
|
|
|
// Upsert by key
|
|
const existing = await query<{ id: string }>(
|
|
`SELECT id FROM fs_knowledge_items WHERE project_id = $1 AND data->>'key' = $2 LIMIT 1`,
|
|
[projectId, key]
|
|
);
|
|
|
|
if (existing.length > 0) {
|
|
await query(
|
|
`UPDATE fs_knowledge_items SET data = $1::jsonb, updated_at = NOW() WHERE id = $2`,
|
|
[data, existing[0].id]
|
|
);
|
|
return NextResponse.json({ id: existing[0].id, key, type: itemType, value, updated: true });
|
|
} else {
|
|
const rows = await query<{ id: string }>(
|
|
`INSERT INTO fs_knowledge_items (project_id, data) VALUES ($1, $2::jsonb) RETURNING id`,
|
|
[projectId, data]
|
|
);
|
|
return NextResponse.json({ id: rows[0].id, key, type: itemType, value, created: true });
|
|
}
|
|
}
|
|
|
|
// DELETE /api/projects/[projectId]/knowledge?id=xxx
|
|
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;
|
|
if (!(await assertOwnership(projectId, session.user.email))) {
|
|
return NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
}
|
|
|
|
const id = req.nextUrl.searchParams.get("id");
|
|
if (!id) return NextResponse.json({ error: "id is required" }, { status: 400 });
|
|
|
|
await query(
|
|
`DELETE FROM fs_knowledge_items WHERE id = $1 AND project_id = $2`,
|
|
[id, projectId]
|
|
);
|
|
|
|
return NextResponse.json({ deleted: true });
|
|
}
|