arcadiasuite/server/agent-defs/routes.ts

243 lines
8.1 KiB
TypeScript

/**
* Agent Definitions Routes — Phase 6
* Dev Center: Fábrica de Agentes (Design → Assemble → Deploy)
*/
import { Router, Request, Response } from "express";
import { db } from "../../db/index";
import { arcadiaAgentDefs, insertAgentDefSchema } from "@shared/schema";
import { eq, desc, and } from "drizzle-orm";
import { blackboardService } from "../blackboard/service";
const router = Router();
// GET /api/agent-defs — listar todos (filtro por status opcional)
router.get("/", async (req: Request, res: Response) => {
try {
const { status } = req.query;
const tenantId = (req.user as any)?.tenantId?.toString() || null;
let query = db.select().from(arcadiaAgentDefs).orderBy(desc(arcadiaAgentDefs.updatedAt));
const defs = await query;
const filtered = defs.filter(d => {
const tenantMatch = !tenantId || !d.tenantId || d.tenantId === tenantId;
const statusMatch = !status || d.status === status;
return tenantMatch && statusMatch;
});
res.json({ success: true, data: filtered });
} catch (error: any) {
res.status(500).json({ success: false, error: error.message });
}
});
// GET /api/agent-defs/:id
router.get("/:id", async (req: Request, res: Response) => {
try {
const id = parseInt(req.params.id);
const [def] = await db.select().from(arcadiaAgentDefs).where(eq(arcadiaAgentDefs.id, id));
if (!def) return res.status(404).json({ success: false, error: "Não encontrado" });
res.json({ success: true, data: def });
} catch (error: any) {
res.status(500).json({ success: false, error: error.message });
}
});
// POST /api/agent-defs — criar
router.post("/", async (req: Request, res: Response) => {
try {
const userId = (req.user as any)?.id || "anonymous";
const tenantId = (req.user as any)?.tenantId?.toString() || null;
const parsed = insertAgentDefSchema.safeParse({ ...req.body, userId, tenantId });
if (!parsed.success) {
return res.status(400).json({ success: false, error: parsed.error.message });
}
const [created] = await db.insert(arcadiaAgentDefs).values(parsed.data).returning();
res.json({ success: true, data: created });
} catch (error: any) {
res.status(500).json({ success: false, error: error.message });
}
});
// PATCH /api/agent-defs/:id — atualizar
router.patch("/:id", async (req: Request, res: Response) => {
try {
const id = parseInt(req.params.id);
const { name, description, spec } = req.body;
const updates: any = { updatedAt: new Date() };
if (name !== undefined) updates.name = name;
if (description !== undefined) updates.description = description;
if (spec !== undefined) updates.spec = spec;
const [updated] = await db
.update(arcadiaAgentDefs)
.set(updates)
.where(eq(arcadiaAgentDefs.id, id))
.returning();
if (!updated) return res.status(404).json({ success: false, error: "Não encontrado" });
res.json({ success: true, data: updated });
} catch (error: any) {
res.status(500).json({ success: false, error: error.message });
}
});
// DELETE /api/agent-defs/:id
router.delete("/:id", async (req: Request, res: Response) => {
try {
const id = parseInt(req.params.id);
await db.delete(arcadiaAgentDefs).where(eq(arcadiaAgentDefs.id, id));
res.json({ success: true });
} catch (error: any) {
res.status(500).json({ success: false, error: error.message });
}
});
// POST /api/agent-defs/:id/assemble — montar via Blackboard
router.post("/:id/assemble", async (req: Request, res: Response) => {
try {
const id = parseInt(req.params.id);
const userId = (req.user as any)?.id || "anonymous";
const [def] = await db.select().from(arcadiaAgentDefs).where(eq(arcadiaAgentDefs.id, id));
if (!def) return res.status(404).json({ success: false, error: "Não encontrado" });
const spec = def.spec as any;
const specContent = spec?.content || JSON.stringify(spec);
const task = await blackboardService.createMainTask(
`Montar agente: ${def.name}`,
specContent,
userId,
{ agentDefId: id, spec, source: "agent-factory" }
);
await blackboardService.createSubtask(
task.id,
"Projetar agente",
`Analisar spec e criar estrutura do agente: ${def.name}\n\n${specContent}`,
"architect",
[],
{ phase: "design", agentDefId: id }
);
const [updated] = await db
.update(arcadiaAgentDefs)
.set({ status: "assembling", lastTaskId: task.id, updatedAt: new Date() })
.where(eq(arcadiaAgentDefs.id, id))
.returning();
res.json({ success: true, data: updated, taskId: task.id });
} catch (error: any) {
res.status(500).json({ success: false, error: error.message });
}
});
// POST /api/agent-defs/:id/deploy — marcar como deployed
router.post("/:id/deploy", async (req: Request, res: Response) => {
try {
const id = parseInt(req.params.id);
const [def] = await db.select().from(arcadiaAgentDefs).where(eq(arcadiaAgentDefs.id, id));
if (!def) return res.status(404).json({ success: false, error: "Não encontrado" });
if (def.status !== "ready") {
return res.status(400).json({ success: false, error: "Agente precisa estar 'ready' para deploy" });
}
const [updated] = await db
.update(arcadiaAgentDefs)
.set({ status: "deployed", version: (def.version || 1) + 1, updatedAt: new Date() })
.where(eq(arcadiaAgentDefs.id, id))
.returning();
res.json({ success: true, data: updated });
} catch (error: any) {
res.status(500).json({ success: false, error: error.message });
}
});
// POST /api/agent-defs/:id/redraft — volta para draft para remontar
router.post("/:id/redraft", async (req: Request, res: Response) => {
try {
const id = parseInt(req.params.id);
const [updated] = await db
.update(arcadiaAgentDefs)
.set({ status: "draft", updatedAt: new Date() })
.where(eq(arcadiaAgentDefs.id, id))
.returning();
if (!updated) return res.status(404).json({ success: false, error: "Não encontrado" });
res.json({ success: true, data: updated });
} catch (error: any) {
res.status(500).json({ success: false, error: error.message });
}
});
// POST /api/agent-defs/:id/run — executar agente via Blackboard
router.post("/:id/run", async (req: Request, res: Response) => {
try {
const id = parseInt(req.params.id);
const userId = (req.user as any)?.id || "anonymous";
const [def] = await db.select().from(arcadiaAgentDefs).where(eq(arcadiaAgentDefs.id, id));
if (!def) return res.status(404).json({ success: false, error: "Não encontrado" });
const spec = def.spec as any;
const specContent = spec?.content || JSON.stringify(spec);
const task = await blackboardService.createMainTask(
`Executar agente: ${def.name}`,
specContent,
userId,
{ agentDefId: id, spec, source: "agent-run" }
);
await blackboardService.createSubtask(
task.id,
"Executar agente",
`Executar o agente conforme spec:\n\n${specContent}`,
"run-executor",
[],
{ phase: "execution", agentDefId: id }
);
res.json({ success: true, taskId: task.id });
} catch (error: any) {
res.status(500).json({ success: false, error: error.message });
}
});
// POST /api/agent-defs/:id/fork — duplicar como novo draft
router.post("/:id/fork", async (req: Request, res: Response) => {
try {
const id = parseInt(req.params.id);
const userId = (req.user as any)?.id || "anonymous";
const tenantId = (req.user as any)?.tenantId?.toString() || null;
const [def] = await db.select().from(arcadiaAgentDefs).where(eq(arcadiaAgentDefs.id, id));
if (!def) return res.status(404).json({ success: false, error: "Não encontrado" });
const [forked] = await db.insert(arcadiaAgentDefs).values({
name: `${def.name} (cópia)`,
description: def.description,
spec: def.spec as any,
status: "draft",
version: 1,
tenantId,
userId,
}).returning();
res.json({ success: true, data: forked });
} catch (error: any) {
res.status(500).json({ success: false, error: error.message });
}
});
export default router;