Checkpoint: Phase 7 complete: Orchestrator Agent добавлен в /agents с меткой CROWN/SYSTEM, кнопками Configure и Open Chat. /chat читает конфиг оркестратора из БД (модель, промпт, инструменты). AgentDetailModal поддерживает isOrchestrator. 24 теста пройдены.
This commit is contained in:
@@ -40,7 +40,7 @@ async function main() {
|
||||
CONSTRAINT \`agentMetrics_requestId_unique\` UNIQUE(\`requestId\`)
|
||||
)`,
|
||||
|
||||
// agents
|
||||
// agents — full schema with isSystem and isOrchestrator
|
||||
`CREATE TABLE IF NOT EXISTS \`agents\` (
|
||||
\`id\` int AUTO_INCREMENT NOT NULL,
|
||||
\`userId\` int NOT NULL,
|
||||
@@ -60,6 +60,8 @@ async function main() {
|
||||
\`maxRequestsPerHour\` int DEFAULT 100,
|
||||
\`isActive\` boolean DEFAULT true,
|
||||
\`isPublic\` boolean DEFAULT false,
|
||||
\`isSystem\` boolean DEFAULT false,
|
||||
\`isOrchestrator\` boolean DEFAULT false,
|
||||
\`tags\` json,
|
||||
\`metadata\` json,
|
||||
\`createdAt\` timestamp NOT NULL DEFAULT (now()),
|
||||
@@ -101,6 +103,19 @@ async function main() {
|
||||
CONSTRAINT \`browserSessions_sessionId_unique\` UNIQUE(\`sessionId\`)
|
||||
)`,
|
||||
|
||||
// agentHistory — conversation history per agent
|
||||
`CREATE TABLE IF NOT EXISTS \`agentHistory\` (
|
||||
\`id\` int AUTO_INCREMENT NOT NULL,
|
||||
\`agentId\` int NOT NULL,
|
||||
\`sessionId\` varchar(64),
|
||||
\`role\` enum('user','assistant','system','tool') NOT NULL,
|
||||
\`content\` text NOT NULL,
|
||||
\`toolCalls\` json,
|
||||
\`metadata\` json,
|
||||
\`createdAt\` timestamp NOT NULL DEFAULT (now()),
|
||||
CONSTRAINT \`agentHistory_id\` PRIMARY KEY(\`id\`)
|
||||
)`,
|
||||
|
||||
// Indexes
|
||||
`CREATE INDEX IF NOT EXISTS \`agentAccessControl_agentId_tool_idx\` ON \`agentAccessControl\` (\`agentId\`,\`tool\`)`,
|
||||
`CREATE INDEX IF NOT EXISTS \`agentMetrics_agentId_idx\` ON \`agentMetrics\` (\`agentId\`)`,
|
||||
@@ -108,6 +123,7 @@ async function main() {
|
||||
`CREATE INDEX IF NOT EXISTS \`agents_userId_idx\` ON \`agents\` (\`userId\`)`,
|
||||
`CREATE INDEX IF NOT EXISTS \`agents_model_idx\` ON \`agents\` (\`model\`)`,
|
||||
`CREATE INDEX IF NOT EXISTS \`browserSessions_agentId_idx\` ON \`browserSessions\` (\`agentId\`)`,
|
||||
`CREATE INDEX IF NOT EXISTS \`agentHistory_agentId_idx\` ON \`agentHistory\` (\`agentId\`)`,
|
||||
];
|
||||
|
||||
for (const stmt of statements) {
|
||||
@@ -118,12 +134,36 @@ async function main() {
|
||||
} catch (e) {
|
||||
if (e.code === 'ER_DUP_KEYNAME' || e.message.includes('Duplicate key name')) {
|
||||
console.log('⚠ Index already exists (ok)');
|
||||
} else if (e.message.includes('already exists')) {
|
||||
console.log('⚠ Already exists (ok)');
|
||||
} else {
|
||||
console.error('✗ Error:', e.message.slice(0, 120));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ALTER TABLE to add missing columns to existing tables
|
||||
const alterStatements = [
|
||||
`ALTER TABLE \`agents\` ADD COLUMN IF NOT EXISTS \`isSystem\` boolean DEFAULT false`,
|
||||
`ALTER TABLE \`agents\` ADD COLUMN IF NOT EXISTS \`isOrchestrator\` boolean DEFAULT false`,
|
||||
];
|
||||
|
||||
console.log('\n--- Applying ALTER TABLE migrations ---');
|
||||
for (const stmt of alterStatements) {
|
||||
try {
|
||||
await conn.query(stmt);
|
||||
const col = stmt.match(/ADD COLUMN.*?`(\w+)`/)?.[1] || 'column';
|
||||
console.log('✓ Added column:', col);
|
||||
} catch (e) {
|
||||
if (e.message.includes('Duplicate column name') || e.message.includes('already exists')) {
|
||||
const col = stmt.match(/ADD COLUMN.*?`(\w+)`/)?.[1] || 'column';
|
||||
console.log('⚠ Column already exists:', col, '(ok)');
|
||||
} else {
|
||||
console.error('✗ ALTER error:', e.message.slice(0, 120));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const [tables] = await conn.query('SHOW TABLES');
|
||||
console.log('\n✅ All tables:', tables.map(t => Object.values(t)[0]).join(', '));
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/**
|
||||
* Seed script — creates 3 specialized agents in the database
|
||||
* Seed script — creates Orchestrator + 3 specialized agents in the database
|
||||
* Run: node scripts/seed-agents.mjs
|
||||
* Run with --force to re-seed existing agents
|
||||
*/
|
||||
import mysql from "mysql2/promise";
|
||||
import * as dotenv from "dotenv";
|
||||
@@ -13,7 +14,6 @@ if (!DATABASE_URL) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Parse mysql2 connection string
|
||||
function parseDbUrl(url) {
|
||||
const u = new URL(url);
|
||||
return {
|
||||
@@ -26,7 +26,78 @@ function parseDbUrl(url) {
|
||||
};
|
||||
}
|
||||
|
||||
const AGENTS = [
|
||||
const ORCHESTRATOR = {
|
||||
name: "Orchestrator",
|
||||
description:
|
||||
"Main system agent that powers the /chat interface. Has full access to all system resources: shell commands, file system, Docker management, HTTP requests, and can delegate tasks to any other agent. Configure its model, system prompt, and tools here.",
|
||||
role: "orchestrator",
|
||||
model: "qwen2.5:7b",
|
||||
provider: "ollama",
|
||||
temperature: "0.5",
|
||||
maxTokens: 8192,
|
||||
topP: "0.9",
|
||||
frequencyPenalty: "0.0",
|
||||
presencePenalty: "0.0",
|
||||
systemPrompt: `You are the GoClaw Orchestrator — the central AI agent of the GoClaw Control Center system.
|
||||
|
||||
You have FULL access to the system and can:
|
||||
- Execute shell commands on the host system
|
||||
- Read and write files anywhere on the filesystem
|
||||
- Manage Docker containers and services
|
||||
- Make HTTP requests to any URL
|
||||
- Delegate tasks to specialized agents (Browser Agent, Tool Builder, Agent Compiler)
|
||||
- List and manage all agents in the system
|
||||
- Install and manage skills
|
||||
- Access and modify the GoClaw codebase
|
||||
|
||||
When given a task:
|
||||
1. Analyze what needs to be done
|
||||
2. Choose the right approach: direct execution or delegation to a specialist agent
|
||||
3. Use tools step by step, showing your reasoning
|
||||
4. Report results clearly
|
||||
|
||||
Available specialized agents you can delegate to:
|
||||
- Browser Agent: web browsing, scraping, research
|
||||
- Tool Builder: create new tools from descriptions
|
||||
- Agent Compiler: compile new agents from specifications (ТЗ)
|
||||
|
||||
System access tools: shell_exec, file_read, file_write, http_request, docker_ps, docker_restart
|
||||
Agent management: delegate_to_agent, list_agents, list_skills, install_skill
|
||||
|
||||
Always be transparent about what you're doing and why.
|
||||
Respond in the same language as the user's message.`,
|
||||
allowedTools: JSON.stringify([
|
||||
"shell_exec",
|
||||
"file_read",
|
||||
"file_write",
|
||||
"http_request",
|
||||
"docker_ps",
|
||||
"docker_restart",
|
||||
"delegate_to_agent",
|
||||
"list_agents",
|
||||
"list_skills",
|
||||
"install_skill",
|
||||
"read_logs",
|
||||
"manage_agents",
|
||||
]),
|
||||
allowedDomains: JSON.stringify(["*"]),
|
||||
maxRequestsPerHour: 1000,
|
||||
isActive: 1,
|
||||
isPublic: 0,
|
||||
isSystem: 1,
|
||||
isOrchestrator: 1,
|
||||
tags: JSON.stringify(["orchestrator", "system", "core", "privileged"]),
|
||||
metadata: JSON.stringify({
|
||||
agentType: "orchestrator",
|
||||
icon: "Crown",
|
||||
color: "#FFD700",
|
||||
seeded: true,
|
||||
systemAgent: true,
|
||||
privileged: true,
|
||||
}),
|
||||
};
|
||||
|
||||
const SPECIALIZED_AGENTS = [
|
||||
{
|
||||
name: "Browser Agent",
|
||||
description:
|
||||
@@ -62,6 +133,8 @@ Respond in the same language as the user's message.`,
|
||||
maxRequestsPerHour: 100,
|
||||
isActive: 1,
|
||||
isPublic: 1,
|
||||
isSystem: 1,
|
||||
isOrchestrator: 0,
|
||||
tags: JSON.stringify(["browser", "web", "scraping", "research"]),
|
||||
metadata: JSON.stringify({
|
||||
agentType: "browser",
|
||||
@@ -111,6 +184,8 @@ Respond in the same language as the user's message.`,
|
||||
maxRequestsPerHour: 50,
|
||||
isActive: 1,
|
||||
isPublic: 1,
|
||||
isSystem: 1,
|
||||
isOrchestrator: 0,
|
||||
tags: JSON.stringify(["tools", "code", "builder", "automation"]),
|
||||
metadata: JSON.stringify({
|
||||
agentType: "tool_builder",
|
||||
@@ -164,6 +239,8 @@ Respond in the same language as the user's message.`,
|
||||
maxRequestsPerHour: 30,
|
||||
isActive: 1,
|
||||
isPublic: 1,
|
||||
isSystem: 1,
|
||||
isOrchestrator: 0,
|
||||
tags: JSON.stringify(["compiler", "meta", "agent-factory", "automation"]),
|
||||
metadata: JSON.stringify({
|
||||
agentType: "agent_compiler",
|
||||
@@ -174,41 +251,42 @@ Respond in the same language as the user's message.`,
|
||||
},
|
||||
];
|
||||
|
||||
const ALL_AGENTS = [ORCHESTRATOR, ...SPECIALIZED_AGENTS];
|
||||
|
||||
async function seed() {
|
||||
const conn = await mysql.createConnection(parseDbUrl(DATABASE_URL));
|
||||
console.log("Connected to DB");
|
||||
|
||||
try {
|
||||
// Check if agents already seeded
|
||||
// Check existing seeded agents
|
||||
const [existing] = await conn.execute(
|
||||
"SELECT id, name FROM agents WHERE JSON_CONTAINS(metadata, '\"seeded\"', '$.seeded') OR name IN (?, ?, ?)",
|
||||
["Browser Agent", "Tool Builder", "Agent Compiler"]
|
||||
"SELECT id, name FROM agents WHERE name IN (?, ?, ?, ?)",
|
||||
["Orchestrator", "Browser Agent", "Tool Builder", "Agent Compiler"]
|
||||
);
|
||||
|
||||
if (existing.length > 0) {
|
||||
console.log("Agents already seeded:");
|
||||
existing.forEach((a) => console.log(` - [${a.id}] ${a.name}`));
|
||||
console.log("Skipping seed. Use --force to re-seed.");
|
||||
if (!process.argv.includes("--force")) {
|
||||
console.log("Skipping seed. Use --force to re-seed.");
|
||||
await conn.end();
|
||||
return;
|
||||
}
|
||||
// Delete existing seeded agents
|
||||
const ids = existing.map((a) => a.id);
|
||||
await conn.execute(`DELETE FROM agents WHERE id IN (${ids.join(",")})`);
|
||||
console.log("Deleted existing seeded agents");
|
||||
}
|
||||
|
||||
// Insert agents
|
||||
for (const agent of AGENTS) {
|
||||
// Insert all agents
|
||||
for (const agent of ALL_AGENTS) {
|
||||
const [result] = await conn.execute(
|
||||
`INSERT INTO agents
|
||||
(userId, name, description, role, model, provider, temperature, maxTokens, topP,
|
||||
frequencyPenalty, presencePenalty, systemPrompt, allowedTools, allowedDomains,
|
||||
maxRequestsPerHour, isActive, isPublic, tags, metadata, createdAt, updatedAt)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())`,
|
||||
maxRequestsPerHour, isActive, isPublic, isSystem, isOrchestrator, tags, metadata, createdAt, updatedAt)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())`,
|
||||
[
|
||||
1, // SYSTEM_USER_ID
|
||||
1,
|
||||
agent.name,
|
||||
agent.description,
|
||||
agent.role,
|
||||
@@ -225,21 +303,25 @@ async function seed() {
|
||||
agent.maxRequestsPerHour,
|
||||
agent.isActive,
|
||||
agent.isPublic,
|
||||
agent.isSystem,
|
||||
agent.isOrchestrator,
|
||||
agent.tags,
|
||||
agent.metadata,
|
||||
]
|
||||
);
|
||||
console.log(`✓ Created agent: ${agent.name} (id: ${result.insertId})`);
|
||||
const badge = agent.isOrchestrator ? " [ORCHESTRATOR]" : agent.isSystem ? " [SYSTEM]" : "";
|
||||
console.log(`✓ Created agent: ${agent.name}${badge} (id: ${result.insertId})`);
|
||||
}
|
||||
|
||||
// Verify
|
||||
const [agents] = await conn.execute(
|
||||
"SELECT id, name, role, model, isActive FROM agents ORDER BY id"
|
||||
"SELECT id, name, role, model, isSystem, isOrchestrator FROM agents ORDER BY id"
|
||||
);
|
||||
console.log("\nAll agents in DB:");
|
||||
agents.forEach((a) =>
|
||||
console.log(` [${a.id}] ${a.name} | role: ${a.role} | model: ${a.model} | active: ${a.isActive}`)
|
||||
);
|
||||
agents.forEach((a) => {
|
||||
const flags = [a.isOrchestrator ? "ORCH" : "", a.isSystem ? "SYS" : ""].filter(Boolean).join(",");
|
||||
console.log(` [${a.id}] ${a.name} | role: ${a.role} | model: ${a.model}${flags ? ` | ${flags}` : ""}`);
|
||||
});
|
||||
} finally {
|
||||
await conn.end();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user