diff --git a/backend/app/models/__init__.py b/backend/app/models/__init__.py index a724da80..469b9a7c 100644 --- a/backend/app/models/__init__.py +++ b/backend/app/models/__init__.py @@ -1,4 +1,5 @@ from . import agent +from . import agent_role from . import project from . import task from . import sqlalchemy_models \ No newline at end of file diff --git a/backend/app/models/agent.py b/backend/app/models/agent.py index 5f5173b0..6ff66e42 100644 --- a/backend/app/models/agent.py +++ b/backend/app/models/agent.py @@ -1,4 +1,4 @@ -from sqlalchemy import Column, Integer, String, DateTime, JSON +from sqlalchemy import Column, Integer, String, DateTime, JSON, Text from sqlalchemy.sql import func from sqlalchemy.orm import relationship from ..core.database import Base @@ -24,6 +24,14 @@ class Agent(Base): updated_at = Column(DateTime(timezone=True), onupdate=func.now()) last_seen = Column(DateTime(timezone=True), nullable=True) + # Role-based collaboration fields + role = Column(String, nullable=True) # Role from Bees-AgenticWorkers + system_prompt = Column(Text, nullable=True) # Role-specific system prompt + reports_to = Column(JSON, nullable=True) # Array of roles this agent reports to + expertise = Column(JSON, nullable=True) # Array of expertise areas + deliverables = Column(JSON, nullable=True) # Array of deliverables + collaboration_settings = Column(JSON, nullable=True) # Collaboration preferences + # Relationships tasks = relationship("Task", back_populates="assigned_agent") @@ -45,5 +53,13 @@ class Agent(Base): "performance_targets": self.performance_targets, "created_at": self.created_at.isoformat() if self.created_at else None, "updated_at": self.updated_at.isoformat() if self.updated_at else None, - "last_seen": self.last_seen.isoformat() if self.last_seen else None + "last_seen": self.last_seen.isoformat() if self.last_seen else None, + + # Role-based fields + "role": self.role, + "system_prompt": self.system_prompt, + "reports_to": self.reports_to, + "expertise": self.expertise, + "deliverables": self.deliverables, + "collaboration_settings": self.collaboration_settings } \ No newline at end of file diff --git a/backend/app/models/agent_role.py b/backend/app/models/agent_role.py new file mode 100644 index 00000000..a04f6266 --- /dev/null +++ b/backend/app/models/agent_role.py @@ -0,0 +1,69 @@ +from sqlalchemy import Column, String, DateTime, JSON, Text +from sqlalchemy.sql import func +from sqlalchemy.orm import relationship +from ..core.database import Base + +class AgentRole(Base): + __tablename__ = "agent_roles" + + id = Column(String, primary_key=True, index=True) + name = Column(String, unique=True, nullable=False, index=True) # Role identifier (e.g., "senior_software_architect") + display_name = Column(String, nullable=False) # Human-readable name + system_prompt = Column(Text, nullable=False) # Role-specific system prompt + reports_to = Column(JSON, nullable=True) # Array of roles this role reports to + expertise = Column(JSON, nullable=True, index=True) # Array of expertise areas + deliverables = Column(JSON, nullable=True) # Array of deliverables + capabilities = Column(JSON, nullable=True) # Array of capabilities + collaboration_defaults = Column(JSON, nullable=True) # Default collaboration settings + created_at = Column(DateTime(timezone=True), server_default=func.now()) + updated_at = Column(DateTime(timezone=True), onupdate=func.now()) + + def to_dict(self): + return { + "id": self.id, + "name": self.name, + "display_name": self.display_name, + "system_prompt": self.system_prompt, + "reports_to": self.reports_to, + "expertise": self.expertise, + "deliverables": self.deliverables, + "capabilities": self.capabilities, + "collaboration_defaults": self.collaboration_defaults, + "created_at": self.created_at.isoformat() if self.created_at else None, + "updated_at": self.updated_at.isoformat() if self.updated_at else None + } + + +class AgentCollaboration(Base): + __tablename__ = "agent_collaborations" + + id = Column(String, primary_key=True, index=True) + from_agent_id = Column(String, nullable=False, index=True) # References agents(id) + to_agent_id = Column(String, nullable=True, index=True) # References agents(id), can be null for broadcasts + message_type = Column(String, nullable=False) # Type of collaboration message + thread_id = Column(String, nullable=True, index=True) # Conversation thread ID + project_id = Column(String, nullable=True, index=True) # Associated project + message_data = Column(JSON, nullable=True) # Original message data + response_data = Column(JSON, nullable=True) # Response data + status = Column(String, default="pending", index=True) # pending, responded, escalated, resolved + priority = Column(String, default="medium", index=True) # low, medium, high, urgent + created_at = Column(DateTime(timezone=True), server_default=func.now()) + responded_at = Column(DateTime(timezone=True), nullable=True) + resolved_at = Column(DateTime(timezone=True), nullable=True) + + def to_dict(self): + return { + "id": self.id, + "from_agent_id": self.from_agent_id, + "to_agent_id": self.to_agent_id, + "message_type": self.message_type, + "thread_id": self.thread_id, + "project_id": self.project_id, + "message_data": self.message_data, + "response_data": self.response_data, + "status": self.status, + "priority": self.priority, + "created_at": self.created_at.isoformat() if self.created_at else None, + "responded_at": self.responded_at.isoformat() if self.responded_at else None, + "resolved_at": self.resolved_at.isoformat() if self.resolved_at else None + } \ No newline at end of file diff --git a/backend/migrations/004_add_agent_roles.sql b/backend/migrations/004_add_agent_roles.sql new file mode 100644 index 00000000..5e343ce3 --- /dev/null +++ b/backend/migrations/004_add_agent_roles.sql @@ -0,0 +1,171 @@ +-- Migration to add role-based collaboration fields to agents table + +-- Add role-based fields to agents table +ALTER TABLE agents ADD COLUMN role VARCHAR(255); +ALTER TABLE agents ADD COLUMN system_prompt TEXT; +ALTER TABLE agents ADD COLUMN reports_to JSONB; -- Array of roles this agent reports to +ALTER TABLE agents ADD COLUMN expertise JSONB; -- Array of expertise areas +ALTER TABLE agents ADD COLUMN deliverables JSONB; -- Array of deliverables this agent produces +ALTER TABLE agents ADD COLUMN collaboration_settings JSONB; -- Collaboration preferences + +-- Add indexes for role-based queries +CREATE INDEX idx_agents_role ON agents(role); +CREATE INDEX idx_agents_expertise ON agents USING GIN(expertise); +CREATE INDEX idx_agents_reports_to ON agents USING GIN(reports_to); + +-- Create agent_roles table for predefined role definitions +CREATE TABLE agent_roles ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + name VARCHAR(255) UNIQUE NOT NULL, + display_name VARCHAR(255) NOT NULL, + system_prompt TEXT NOT NULL, + reports_to JSONB, -- Array of roles this role reports to + expertise JSONB, -- Array of expertise areas + deliverables JSONB, -- Array of deliverables + capabilities JSONB, -- Array of capabilities + collaboration_defaults JSONB, -- Default collaboration settings + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); + +-- Create index for agent_roles +CREATE INDEX idx_agent_roles_name ON agent_roles(name); +CREATE INDEX idx_agent_roles_expertise ON agent_roles USING GIN(expertise); + +-- Insert predefined roles from Bees-AgenticWorkers.md +INSERT INTO agent_roles (name, display_name, system_prompt, reports_to, expertise, deliverables, capabilities, collaboration_defaults) VALUES +( + 'senior_software_architect', + 'Senior Software Architect', + 'You are the **Senior Software Architect**. You define the system''s overall structure, select tech stacks, and ensure long-term maintainability. + +* **Responsibilities:** Draft high-level architecture diagrams, define API contracts, set coding standards, mentor engineering leads. +* **Expertise:** Deep experience in multiple programming paradigms, distributed systems, security models, and cloud architectures. +* **Reports To:** Product Owner / Technical Director. +* **Deliverables:** Architecture blueprints, tech stack decisions, integration strategies, and review sign-offs on major design changes.', + '["product_owner", "technical_director"]'::jsonb, + '["architecture", "distributed_systems", "security", "cloud_architectures", "api_design"]'::jsonb, + '["architecture_blueprints", "tech_stack_decisions", "integration_strategies", "design_reviews"]'::jsonb, + '["task-coordination", "meta-discussion", "architecture", "code-review", "mentoring"]'::jsonb, + '{ + "preferred_message_types": ["coordination_request", "meta_discussion", "escalation_trigger"], + "auto_subscribe_to_roles": ["lead_designer", "security_expert", "systems_engineer"], + "auto_subscribe_to_expertise": ["architecture", "security", "infrastructure"], + "response_timeout_seconds": 300, + "max_collaboration_depth": 5, + "escalation_threshold": 3 + }'::jsonb +), +( + 'lead_designer', + 'Lead Designer', + 'You are the **Lead Designer**. You guide the creative vision and maintain design cohesion across the product. + +* **Responsibilities:** Oversee UX flow, wireframes, and feature design; ensure consistency of theme and style; mediate between product vision and technical constraints. +* **Expertise:** UI/UX principles, accessibility, information architecture, Figma/Sketch proficiency. +* **Reports To:** Product Owner. +* **Deliverables:** Style guides, wireframes, feature specs, and iterative design documentation.', + '["product_owner"]'::jsonb, + '["ui_ux", "accessibility", "information_architecture", "design_systems", "user_research"]'::jsonb, + '["style_guides", "wireframes", "feature_specs", "design_documentation"]'::jsonb, + '["task-coordination", "meta-discussion", "design", "user_experience"]'::jsonb, + '{ + "preferred_message_types": ["task_help_request", "coordination_request", "meta_discussion"], + "auto_subscribe_to_roles": ["ui_ux_designer", "frontend_developer"], + "auto_subscribe_to_expertise": ["design", "frontend", "user_experience"], + "response_timeout_seconds": 180, + "max_collaboration_depth": 3, + "escalation_threshold": 2 + }'::jsonb +), +( + 'security_expert', + 'Security Expert', + 'You are the **Security Expert**. You ensure the system is hardened against vulnerabilities. + +* **Responsibilities:** Conduct threat modeling, penetration tests, code reviews for security flaws, and define access control policies. +* **Expertise:** Cybersecurity frameworks (OWASP, NIST), encryption, key management, zero-trust systems. +* **Reports To:** Senior Software Architect. +* **Deliverables:** Security audits, vulnerability reports, risk mitigation plans, compliance documentation.', + '["senior_software_architect"]'::jsonb, + '["cybersecurity", "owasp", "nist", "encryption", "key_management", "zero_trust", "penetration_testing"]'::jsonb, + '["security_audits", "vulnerability_reports", "risk_mitigation_plans", "compliance_documentation"]'::jsonb, + '["task-coordination", "meta-discussion", "security-analysis", "code-review", "threat-modeling"]'::jsonb, + '{ + "preferred_message_types": ["dependency_alert", "task_help_request", "escalation_trigger"], + "auto_subscribe_to_roles": ["backend_developer", "devops_engineer", "senior_software_architect"], + "auto_subscribe_to_expertise": ["security", "backend", "infrastructure"], + "response_timeout_seconds": 120, + "max_collaboration_depth": 4, + "escalation_threshold": 1 + }'::jsonb +), +( + 'frontend_developer', + 'Frontend Developer', + 'You are the **Frontend Developer**. You turn designs into interactive interfaces. + +* **Responsibilities:** Build UI components, optimize performance, ensure cross-browser/device compatibility, and integrate frontend with backend APIs. +* **Expertise:** HTML, CSS, JavaScript/TypeScript, React/Vue/Angular, accessibility standards. +* **Reports To:** Frontend Lead or Senior Architect. +* **Deliverables:** Functional UI screens, reusable components, and documented frontend code.', + '["frontend_lead", "senior_software_architect"]'::jsonb, + '["html", "css", "javascript", "typescript", "react", "vue", "angular", "accessibility"]'::jsonb, + '["ui_screens", "reusable_components", "frontend_code", "documentation"]'::jsonb, + '["task-coordination", "meta-discussion", "frontend", "ui_development", "component_design"]'::jsonb, + '{ + "preferred_message_types": ["task_help_request", "coordination_request", "task_help_response"], + "auto_subscribe_to_roles": ["ui_ux_designer", "backend_developer", "lead_designer"], + "auto_subscribe_to_expertise": ["design", "backend", "api_integration"], + "response_timeout_seconds": 180, + "max_collaboration_depth": 3, + "escalation_threshold": 2 + }'::jsonb +), +( + 'backend_developer', + 'Backend Developer', + 'You are the **Backend Developer**. You create APIs, logic, and server-side integrations. + +* **Responsibilities:** Implement core logic, manage data pipelines, enforce security, and support scaling strategies. +* **Expertise:** Server frameworks, REST/GraphQL APIs, authentication, caching, microservices. +* **Reports To:** Backend Lead or Senior Architect. +* **Deliverables:** API endpoints, backend services, unit tests, and deployment-ready server code.', + '["backend_lead", "senior_software_architect"]'::jsonb, + '["server_frameworks", "rest_api", "graphql", "authentication", "caching", "microservices", "databases"]'::jsonb, + '["api_endpoints", "backend_services", "unit_tests", "server_code"]'::jsonb, + '["task-coordination", "meta-discussion", "backend", "api_development", "database_design"]'::jsonb, + '{ + "preferred_message_types": ["task_help_request", "coordination_request", "dependency_alert"], + "auto_subscribe_to_roles": ["database_engineer", "frontend_developer", "security_expert"], + "auto_subscribe_to_expertise": ["database", "frontend", "security"], + "response_timeout_seconds": 200, + "max_collaboration_depth": 4, + "escalation_threshold": 2 + }'::jsonb +); + +-- Create agent_collaborations table to track collaboration history +CREATE TABLE agent_collaborations ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + from_agent_id UUID REFERENCES agents(id), + to_agent_id UUID REFERENCES agents(id), + message_type VARCHAR(100) NOT NULL, + thread_id UUID, + project_id INTEGER REFERENCES projects(id), + message_data JSONB, + response_data JSONB, + status VARCHAR(50) DEFAULT 'pending', -- pending, responded, escalated, resolved + priority VARCHAR(20) DEFAULT 'medium', -- low, medium, high, urgent + created_at TIMESTAMP DEFAULT NOW(), + responded_at TIMESTAMP, + resolved_at TIMESTAMP +); + +-- Indexes for collaboration tracking +CREATE INDEX idx_agent_collaborations_from_agent ON agent_collaborations(from_agent_id); +CREATE INDEX idx_agent_collaborations_to_agent ON agent_collaborations(to_agent_id); +CREATE INDEX idx_agent_collaborations_thread ON agent_collaborations(thread_id); +CREATE INDEX idx_agent_collaborations_project ON agent_collaborations(project_id); +CREATE INDEX idx_agent_collaborations_status ON agent_collaborations(status); +CREATE INDEX idx_agent_collaborations_priority ON agent_collaborations(priority); \ No newline at end of file diff --git a/frontend/src/components/agents/AgentManagement.tsx b/frontend/src/components/agents/AgentManagement.tsx new file mode 100644 index 00000000..4e73c2d3 --- /dev/null +++ b/frontend/src/components/agents/AgentManagement.tsx @@ -0,0 +1,376 @@ +import React, { useState, useEffect } from 'react'; +import { Card, CardContent, CardHeader, CardTitle } from '../ui/card'; +import { Button } from '../ui/button'; +import { Badge } from '../ui/badge'; +import { DataTable } from '../ui/DataTable'; +import { Alert, AlertDescription } from '../ui/alert'; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '../ui/dialog'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '../ui/tabs'; +import AgentRoleSelector from './AgentRoleSelector'; +import CollaborationDashboard from './CollaborationDashboard'; + +interface Agent { + id: string; + name: string; + endpoint: string; + model: string; + status: string; + role?: string; + expertise?: string[]; + reports_to?: string[]; + deliverables?: string[]; + capabilities?: string[]; + collaboration_settings?: any; + last_seen?: string; +} + +interface RoleDefinition { + id: string; + name: string; + display_name: string; + system_prompt: string; + reports_to: string[]; + expertise: string[]; + deliverables: string[]; + capabilities: string[]; + collaboration_defaults: any; +} + +export const AgentManagement: React.FC = () => { + const [agents, setAgents] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [selectedAgent, setSelectedAgent] = useState(null); + const [editDialogOpen, setEditDialogOpen] = useState(false); + + useEffect(() => { + fetchAgents(); + }, []); + + const fetchAgents = async () => { + try { + setLoading(true); + const response = await fetch('/api/agents'); + if (!response.ok) { + throw new Error('Failed to fetch agents'); + } + const data = await response.json(); + setAgents(data); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to load agents'); + } finally { + setLoading(false); + } + }; + + const handleRoleUpdate = async (agent: Agent, role: RoleDefinition) => { + try { + const response = await fetch(`/api/agents/${agent.id}`, { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + role: role.name, + system_prompt: role.system_prompt, + reports_to: role.reports_to, + expertise: role.expertise, + deliverables: role.deliverables, + capabilities: role.capabilities, + collaboration_settings: role.collaboration_defaults + }), + }); + + if (!response.ok) { + throw new Error('Failed to update agent role'); + } + + // Refresh agents list + await fetchAgents(); + setEditDialogOpen(false); + setSelectedAgent(null); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to update agent'); + } + }; + + const getStatusColor = (status: string) => { + switch (status) { + case 'online': return 'default'; + case 'busy': return 'secondary'; + case 'offline': return 'destructive'; + default: return 'outline'; + } + }; + + const agentColumns = [ + { + accessorKey: 'name', + header: 'Agent Name', + cell: ({ row }: any) => ( +
+
{row.original.name}
+
{row.original.id.substring(0, 8)}...
+
+ ) + }, + { + accessorKey: 'role', + header: 'Role', + cell: ({ row }: any) => ( + row.original.role ? ( + + {row.original.role.replace(/_/g, ' ')} + + ) : ( + No role assigned + ) + ) + }, + { + accessorKey: 'status', + header: 'Status', + cell: ({ row }: any) => ( + + {row.original.status} + + ) + }, + { + accessorKey: 'model', + header: 'Model', + cell: ({ row }: any) => ( + {row.original.model || 'N/A'} + ) + }, + { + accessorKey: 'expertise', + header: 'Expertise', + cell: ({ row }: any) => ( +
+ {(row.original.expertise || []).slice(0, 3).map((exp: string) => ( + + {exp.replace(/_/g, ' ')} + + ))} + {(row.original.expertise || []).length > 3 && ( + + +{(row.original.expertise || []).length - 3} more + + )} +
+ ) + }, + { + accessorKey: 'last_seen', + header: 'Last Seen', + cell: ({ row }: any) => ( +
+ {row.original.last_seen ? + new Date(row.original.last_seen).toLocaleString() : + 'Never' + } +
+ ) + }, + { + id: 'actions', + header: 'Actions', + cell: ({ row }: any) => ( + + ) + } + ]; + + const getRoleDistribution = () => { + const distribution: Record = {}; + agents.forEach(agent => { + const role = agent.role || 'unassigned'; + distribution[role] = (distribution[role] || 0) + 1; + }); + return distribution; + }; + + const getStatusDistribution = () => { + const distribution: Record = {}; + agents.forEach(agent => { + distribution[agent.status] = (distribution[agent.status] || 0) + 1; + }); + return distribution; + }; + + if (loading) { + return ( +
+
+
+
+
+
+ ); + } + + if (error) { + return ( + + {error} + + + ); + } + + const roleDistribution = getRoleDistribution(); + const statusDistribution = getStatusDistribution(); + + return ( +
+
+

Agent Management

+ +
+ + + + Agents + Collaborations + Analytics + + + + {/* Summary Cards */} +
+ + +
{agents.length}
+
Total Agents
+
+
+ + +
{statusDistribution.online || 0}
+
Online
+
+
+ + +
{agents.filter(a => a.role).length}
+
Role Assigned
+
+
+ + +
{Object.keys(roleDistribution).length}
+
Unique Roles
+
+
+
+ + {/* Agents Table */} + + + Registered Agents + + + + + +
+ + + + + + + {/* Role Distribution */} + + + Role Distribution + + +
+ {Object.entries(roleDistribution).map(([role, count]) => ( +
+ + {role === 'unassigned' ? 'Unassigned' : role.replace(/_/g, ' ')} + +
+
+
+
+ {count} +
+
+ ))} +
+ + + + {/* Status Distribution */} + + + Agent Status + + +
+ {Object.entries(statusDistribution).map(([status, count]) => ( +
+
+ {status} +
+
+
+
+
+ {count} +
+
+ ))} +
+ + + + + + {/* Role Assignment Dialog */} + + + + + Assign Role to {selectedAgent?.name} + + + {selectedAgent && ( + handleRoleUpdate(selectedAgent, role)} + /> + )} + + +
+ ); +}; + +export default AgentManagement; \ No newline at end of file diff --git a/frontend/src/components/agents/AgentRoleSelector.tsx b/frontend/src/components/agents/AgentRoleSelector.tsx new file mode 100644 index 00000000..9076e45d --- /dev/null +++ b/frontend/src/components/agents/AgentRoleSelector.tsx @@ -0,0 +1,231 @@ +import React, { useState, useEffect } from 'react'; +import { Card, CardContent, CardHeader, CardTitle } from '../ui/card'; +import { Button } from '../ui/button'; +import { Select } from '../ui/select'; +import { Badge } from '../ui/badge'; +import { Input } from '../ui/input'; +import { Textarea } from '../ui/textarea'; +import { Label } from '../ui/label'; +import { Alert, AlertDescription } from '../ui/alert'; + +interface RoleDefinition { + id: string; + name: string; + display_name: string; + system_prompt: string; + reports_to: string[]; + expertise: string[]; + deliverables: string[]; + capabilities: string[]; + collaboration_defaults: { + preferred_message_types: string[]; + auto_subscribe_to_roles: string[]; + auto_subscribe_to_expertise: string[]; + response_timeout_seconds: number; + max_collaboration_depth: number; + escalation_threshold: number; + }; +} + +interface AgentRoleSelectorProps { + agentId?: string; + currentRole?: string; + onRoleChange: (role: RoleDefinition) => void; + onCustomPromptChange?: (prompt: string) => void; + className?: string; +} + +export const AgentRoleSelector: React.FC = ({ + agentId, + currentRole, + onRoleChange, + onCustomPromptChange, + className = "" +}) => { + const [roles, setRoles] = useState([]); + const [selectedRole, setSelectedRole] = useState(null); + const [customPrompt, setCustomPrompt] = useState(''); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + fetchAvailableRoles(); + }, []); + + useEffect(() => { + if (currentRole && roles.length > 0) { + const role = roles.find(r => r.name === currentRole); + if (role) { + setSelectedRole(role); + } + } + }, [currentRole, roles]); + + const fetchAvailableRoles = async () => { + try { + setLoading(true); + const response = await fetch('/api/agent-roles'); + if (!response.ok) { + throw new Error('Failed to fetch agent roles'); + } + const data = await response.json(); + setRoles(data); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to load roles'); + } finally { + setLoading(false); + } + }; + + const handleRoleSelect = (roleName: string) => { + const role = roles.find(r => r.name === roleName); + if (role) { + setSelectedRole(role); + setCustomPrompt(role.system_prompt); + onRoleChange(role); + } + }; + + const handleCustomPromptChange = (prompt: string) => { + setCustomPrompt(prompt); + if (onCustomPromptChange) { + onCustomPromptChange(prompt); + } + }; + + if (loading) { + return ( + + + Agent Role Configuration + + +
+
+
+
+
+
+ ); + } + + if (error) { + return ( + + + Agent Role Configuration + + + + {error} + + + + + ); + } + + return ( + + + Agent Role Configuration + + + {/* Role Selection */} +
+ + +
+ + {/* Role Details */} + {selectedRole && ( + <> + {/* Expertise Areas */} +
+ +
+ {selectedRole.expertise.map(exp => ( + + {exp.replace(/_/g, ' ')} + + ))} +
+
+ + {/* Reports To */} + {selectedRole.reports_to.length > 0 && ( +
+ +
+ {selectedRole.reports_to.map(role => ( + + {role.replace(/_/g, ' ')} + + ))} +
+
+ )} + + {/* Deliverables */} +
+ +
+ {selectedRole.deliverables.map(deliverable => ( + + {deliverable.replace(/_/g, ' ')} + + ))} +
+
+ + {/* Capabilities */} +
+ +
+ {selectedRole.capabilities.map(capability => ( + + {capability} + + ))} +
+
+ + {/* Collaboration Settings */} +
+ +
+
Response Timeout: {selectedRole.collaboration_defaults.response_timeout_seconds}s
+
Max Collaboration Depth: {selectedRole.collaboration_defaults.max_collaboration_depth}
+
Escalation Threshold: {selectedRole.collaboration_defaults.escalation_threshold}
+
+
+ + {/* System Prompt */} +
+ +