arcadiasuite/server/blackboard/agents/ExecutorAgent.ts

179 lines
5.5 KiB
TypeScript

/**
* Arcadia Suite - Executor Agent
*
* Agente responsável por preparar código para staging (preview).
* NÃO aplica código diretamente - envia para área de staging
* onde o usuário pode revisar e aprovar antes de publicar.
*
* @author Arcadia Development Team
* @version 3.0.0 - Staging Flow
*/
import { BaseBlackboardAgent, type AgentConfig } from "../BaseBlackboardAgent";
import { blackboardService } from "../service";
import { type BlackboardTask } from "@shared/schema";
const SYSTEM_PROMPT = `Você é o Agente Executor do Arcadia Suite.
## Seu Papel
Você prepara código validado para staging (preview), onde o usuário pode revisar antes de publicar.
## Processo de Staging
1. Verificar se validação passou
2. Validar caminhos dos arquivos
3. Marcar tarefa como "staged" (pronta para preview)
4. Aguardar aprovação do usuário para publicar
## IMPORTANTE
- NÃO aplique código diretamente ao projeto
- O código vai para STAGING primeiro
- O usuário decide quando publicar`;
const PROTECTED_FILES = [
"server/routes.ts",
"server/index.ts",
"server/storage.ts",
"server/db.ts",
"shared/schema.ts",
"client/src/App.tsx",
"client/src/main.tsx",
"client/src/pages/Cockpit.tsx",
"client/src/pages/Agent.tsx",
"client/src/pages/ProcessCompass.tsx",
"client/src/pages/DevCenter.tsx",
"package.json",
"tsconfig.json",
"vite.config.ts",
"drizzle.config.ts",
];
export class ExecutorAgent extends BaseBlackboardAgent {
constructor() {
const config: AgentConfig = {
name: "executor",
displayName: "Agente Executor",
description: "Prepara código para staging e publicação",
systemPrompt: SYSTEM_PROMPT,
capabilities: [
"Preparar código para staging",
"Validar caminhos de arquivos",
"Bloquear arquivos protegidos",
"Preparar para publicação"
],
pollInterval: 2000
};
super(config);
}
canHandle(task: BlackboardTask): boolean {
const context = task.context as any;
return context?.phase === "deploy" || task.assignedAgent === "executor";
}
async process(task: BlackboardTask): Promise<void> {
await this.log(task.id, "thinking", "Preparando para staging...");
const codeArtifacts = await blackboardService.getArtifactsForTask(task.id, "code");
const allDocs = await blackboardService.getArtifactsForTask(task.id, "doc");
const validationArtifact = allDocs.find(a => a.name === "validation-report.json");
if (codeArtifacts.length === 0) {
await blackboardService.failTask(task.id, "executor", "Nenhum código para aplicar");
return;
}
let validation: any = { valid: false, score: 0 };
if (validationArtifact?.content) {
try {
validation = JSON.parse(validationArtifact.content);
} catch {}
}
if (!validation.valid || validation.score < 60) {
await this.log(task.id, "blocked", `Validação falhou (score: ${validation.score}, valid: ${validation.valid})`);
await blackboardService.failTask(task.id, "executor", `Código não aprovado (score: ${validation.score}) - staging bloqueado`);
return;
}
await this.log(task.id, "executing", `Preparando ${codeArtifacts.length} arquivos para staging...`);
const stagedFiles: string[] = [];
const blockedFiles: string[] = [];
const errors: string[] = [];
for (const artifact of codeArtifacts) {
const filePath = artifact.name;
const content = artifact.content;
if (!content) {
errors.push(`${filePath}: conteúdo vazio`);
continue;
}
if (PROTECTED_FILES.includes(filePath)) {
blockedFiles.push(filePath);
await this.log(task.id, "blocked", `Arquivo protegido: ${filePath} - não será incluído no staging`);
continue;
}
const isValidPath = filePath.startsWith("client/src") ||
filePath.startsWith("server/") ||
filePath.startsWith("shared/");
if (!isValidPath) {
await this.log(task.id, "skipped", `Arquivo fora dos diretórios permitidos: ${filePath}`);
continue;
}
stagedFiles.push(filePath);
}
if (stagedFiles.length === 0) {
await blackboardService.failTask(task.id, "executor", `Nenhum arquivo válido para staging. Bloqueados: ${blockedFiles.join(", ")}. Erros: ${errors.join("; ")}`);
return;
}
const mainTask = await blackboardService.getMainTask(task.id);
const stagingReport = {
action: "staging",
status: "awaiting_approval",
stagedFiles,
blockedFiles,
errors: errors.length > 0 ? errors : undefined,
validationScore: validation.score,
taskId: task.id,
mainTaskId: mainTask?.id,
title: mainTask?.title || task.title,
stagedAt: new Date().toISOString(),
};
await blackboardService.addArtifact(
task.id,
"doc",
"staging-report.json",
JSON.stringify(stagingReport, null, 2),
"executor"
);
await this.log(task.id, "completed", `Staging pronto: ${stagedFiles.length} arquivos aguardando aprovação no Preview`);
if (mainTask) {
await blackboardService.createSubtask(
mainTask.id,
"Registrar aprendizado",
"Armazenar padrões aprendidos para evolução",
"evolution",
[task.id],
{ phase: "evolution" }
);
}
await blackboardService.completeTask(task.id, "executor", stagingReport);
}
}
export { PROTECTED_FILES };
export const executorAgent = new ExecutorAgent();