Files
bzzz/agent/role_config.go
anthonyrawlins 5978a0b8f5 WIP: Save agent roles integration work before CHORUS rebrand
- Agent roles and coordination features
- Chat API integration testing
- New configuration and workspace management

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-01 02:21:11 +10:00

495 lines
14 KiB
Go

package agent
import (
"fmt"
"path/filepath"
"strings"
"sync"
)
// AgentRole represents different agent roles in the system
type AgentRole string
const (
BackendRole AgentRole = "backend"
FrontendRole AgentRole = "frontend"
DevOpsRole AgentRole = "devops"
QARole AgentRole = "qa"
TestingRole AgentRole = "testing"
GeneralRole AgentRole = "general"
)
// RoleCapability represents capabilities of an agent role
type RoleCapability struct {
Name string
Description string
Weight float64
}
// DirectoryScope represents directory patterns for context filtering
type DirectoryScope struct {
Patterns []string
Description string
}
// RoleConfig holds configuration for an agent role
type RoleConfig struct {
Role AgentRole
DisplayName string
Description string
Capabilities []RoleCapability
DirectoryScopes DirectoryScope
TaskTypes []string
Priority int
// Context filtering parameters
ContextWeight float64
FeedbackWeight float64
LearningRate float64
}
// RoleManager manages agent roles and their configurations
type RoleManager struct {
roles map[AgentRole]*RoleConfig
agentRoles map[string]AgentRole // Maps agent ID to role
mu sync.RWMutex
}
// NewRoleManager creates a new role manager with default configurations
func NewRoleManager() *RoleManager {
rm := &RoleManager{
roles: make(map[AgentRole]*RoleConfig),
agentRoles: make(map[string]AgentRole),
}
rm.initializeDefaultRoles()
return rm
}
// initializeDefaultRoles sets up default role configurations
func (rm *RoleManager) initializeDefaultRoles() {
// Backend role configuration
rm.roles[BackendRole] = &RoleConfig{
Role: BackendRole,
DisplayName: "Backend Developer",
Description: "Specializes in server-side development, APIs, databases, and backend services",
Capabilities: []RoleCapability{
{Name: "api_development", Description: "REST/GraphQL API development", Weight: 1.0},
{Name: "database_design", Description: "Database schema and query optimization", Weight: 0.9},
{Name: "server_architecture", Description: "Server architecture and microservices", Weight: 0.9},
{Name: "authentication", Description: "Authentication and authorization systems", Weight: 0.8},
{Name: "caching", Description: "Caching strategies and implementation", Weight: 0.8},
},
DirectoryScopes: DirectoryScope{
Patterns: []string{
"*/backend/*",
"*/api/*",
"*/services/*",
"*/server/*",
"*/core/*",
"*/models/*",
"*/controllers/*",
"*/middleware/*",
},
Description: "Backend-related directories and server-side code",
},
TaskTypes: []string{
"api_development",
"database_migration",
"backend_optimization",
"server_configuration",
"authentication_setup",
},
Priority: 5,
ContextWeight: 1.0,
FeedbackWeight: 0.3,
LearningRate: 0.1,
}
// Frontend role configuration
rm.roles[FrontendRole] = &RoleConfig{
Role: FrontendRole,
DisplayName: "Frontend Developer",
Description: "Specializes in user interfaces, client-side logic, and user experience",
Capabilities: []RoleCapability{
{Name: "ui_development", Description: "User interface development", Weight: 1.0},
{Name: "responsive_design", Description: "Responsive and mobile-first design", Weight: 0.9},
{Name: "state_management", Description: "Client-side state management", Weight: 0.8},
{Name: "component_architecture", Description: "Component-based architecture", Weight: 0.9},
{Name: "accessibility", Description: "Web accessibility implementation", Weight: 0.7},
},
DirectoryScopes: DirectoryScope{
Patterns: []string{
"*/frontend/*",
"*/ui/*",
"*/client/*",
"*/web/*",
"*/components/*",
"*/pages/*",
"*/styles/*",
"*/assets/*",
},
Description: "Frontend-related directories and client-side code",
},
TaskTypes: []string{
"ui_implementation",
"component_development",
"responsive_design",
"frontend_optimization",
"user_experience",
},
Priority: 4,
ContextWeight: 0.8,
FeedbackWeight: 0.3,
LearningRate: 0.1,
}
// DevOps role configuration
rm.roles[DevOpsRole] = &RoleConfig{
Role: DevOpsRole,
DisplayName: "DevOps Engineer",
Description: "Specializes in deployment, infrastructure, CI/CD, and system operations",
Capabilities: []RoleCapability{
{Name: "infrastructure", Description: "Infrastructure as Code", Weight: 1.0},
{Name: "containerization", Description: "Docker and container orchestration", Weight: 0.9},
{Name: "ci_cd", Description: "Continuous Integration/Deployment", Weight: 0.9},
{Name: "monitoring", Description: "System monitoring and alerting", Weight: 0.8},
{Name: "security", Description: "Security and compliance", Weight: 0.8},
},
DirectoryScopes: DirectoryScope{
Patterns: []string{
"*/deploy/*",
"*/config/*",
"*/docker/*",
"*/k8s/*",
"*/kubernetes/*",
"*/infrastructure/*",
"*/scripts/*",
"*/ci/*",
"*.yml",
"*.yaml",
"Dockerfile*",
"docker-compose*",
},
Description: "DevOps-related configuration and deployment files",
},
TaskTypes: []string{
"deployment",
"infrastructure_setup",
"ci_cd_pipeline",
"system_monitoring",
"security_configuration",
},
Priority: 5,
ContextWeight: 1.0,
FeedbackWeight: 0.4,
LearningRate: 0.1,
}
// QA role configuration
rm.roles[QARole] = &RoleConfig{
Role: QARole,
DisplayName: "Quality Assurance",
Description: "Specializes in quality assurance, code review, and process improvement",
Capabilities: []RoleCapability{
{Name: "code_review", Description: "Code review and quality assessment", Weight: 1.0},
{Name: "process_improvement", Description: "Development process improvement", Weight: 0.9},
{Name: "quality_metrics", Description: "Quality metrics and reporting", Weight: 0.8},
{Name: "best_practices", Description: "Best practices enforcement", Weight: 0.9},
{Name: "documentation", Description: "Documentation quality assurance", Weight: 0.7},
},
DirectoryScopes: DirectoryScope{
Patterns: []string{
"*/tests/*",
"*/quality/*",
"*/review/*",
"*/docs/*",
"*/documentation/*",
"*", // QA role gets broader access for review purposes
},
Description: "All directories for quality assurance and code review",
},
TaskTypes: []string{
"code_review",
"quality_assessment",
"process_improvement",
"documentation_review",
"compliance_check",
},
Priority: 4,
ContextWeight: 0.7,
FeedbackWeight: 0.5,
LearningRate: 0.2,
}
// Testing role configuration
rm.roles[TestingRole] = &RoleConfig{
Role: TestingRole,
DisplayName: "Test Engineer",
Description: "Specializes in automated testing, test frameworks, and test strategy",
Capabilities: []RoleCapability{
{Name: "unit_testing", Description: "Unit test development", Weight: 1.0},
{Name: "integration_testing", Description: "Integration test development", Weight: 0.9},
{Name: "e2e_testing", Description: "End-to-end test automation", Weight: 0.9},
{Name: "test_frameworks", Description: "Test framework setup and maintenance", Weight: 0.8},
{Name: "performance_testing", Description: "Performance and load testing", Weight: 0.7},
},
DirectoryScopes: DirectoryScope{
Patterns: []string{
"*/tests/*",
"*/spec/*",
"*/test/*",
"*/e2e/*",
"*/integration/*",
"*/__tests__/*",
"*.test.*",
"*.spec.*",
},
Description: "Test-related directories and files",
},
TaskTypes: []string{
"unit_testing",
"integration_testing",
"e2e_testing",
"test_automation",
"performance_testing",
},
Priority: 4,
ContextWeight: 0.6,
FeedbackWeight: 0.4,
LearningRate: 0.15,
}
// General role configuration
rm.roles[GeneralRole] = &RoleConfig{
Role: GeneralRole,
DisplayName: "General Developer",
Description: "General-purpose development with broad capabilities",
Capabilities: []RoleCapability{
{Name: "general_development", Description: "General software development", Weight: 0.7},
{Name: "problem_solving", Description: "General problem solving", Weight: 0.8},
{Name: "documentation", Description: "Documentation writing", Weight: 0.6},
{Name: "code_maintenance", Description: "Code maintenance and refactoring", Weight: 0.7},
{Name: "research", Description: "Technical research and analysis", Weight: 0.8},
},
DirectoryScopes: DirectoryScope{
Patterns: []string{
"*", // General role has access to all directories
},
Description: "All directories for general development tasks",
},
TaskTypes: []string{
"general_development",
"documentation",
"code_maintenance",
"research",
"bug_fixes",
},
Priority: 2,
ContextWeight: 0.5,
FeedbackWeight: 0.2,
LearningRate: 0.1,
}
}
// AssignRole assigns a role to an agent
func (rm *RoleManager) AssignRole(agentID string, role AgentRole) error {
rm.mu.Lock()
defer rm.mu.Unlock()
if _, exists := rm.roles[role]; !exists {
return fmt.Errorf("role %s does not exist", role)
}
rm.agentRoles[agentID] = role
return nil
}
// GetAgentRole returns the role assigned to an agent
func (rm *RoleManager) GetAgentRole(agentID string) (AgentRole, bool) {
rm.mu.RLock()
defer rm.mu.RUnlock()
role, exists := rm.agentRoles[agentID]
return role, exists
}
// GetRoleConfig returns the configuration for a specific role
func (rm *RoleManager) GetRoleConfig(role AgentRole) (*RoleConfig, bool) {
rm.mu.RLock()
defer rm.mu.RUnlock()
config, exists := rm.roles[role]
return config, exists
}
// GetAllRoles returns all available roles
func (rm *RoleManager) GetAllRoles() map[AgentRole]*RoleConfig {
rm.mu.RLock()
defer rm.mu.RUnlock()
result := make(map[AgentRole]*RoleConfig)
for role, config := range rm.roles {
// Create a copy to avoid race conditions
configCopy := *config
result[role] = &configCopy
}
return result
}
// MatchesDirectoryScope checks if a directory path matches the agent's scope
func (rm *RoleManager) MatchesDirectoryScope(agentID, directory string) bool {
role, exists := rm.GetAgentRole(agentID)
if !exists {
return false
}
config, exists := rm.GetRoleConfig(role)
if !exists {
return false
}
return rm.matchesPatterns(directory, config.DirectoryScopes.Patterns)
}
// GetRelevanceScore calculates context relevance score for an agent and directory
func (rm *RoleManager) GetRelevanceScore(agentID, directory string) float64 {
role, exists := rm.GetAgentRole(agentID)
if !exists {
return 0.1 // Low default score
}
config, exists := rm.GetRoleConfig(role)
if !exists {
return 0.1
}
if rm.matchesPatterns(directory, config.DirectoryScopes.Patterns) {
return config.ContextWeight
}
return 0.1 // Low score for non-matching directories
}
// matchesPatterns checks if a directory matches any of the given patterns
func (rm *RoleManager) matchesPatterns(directory string, patterns []string) bool {
if directory == "" {
return false
}
directory = strings.ToLower(directory)
for _, pattern := range patterns {
pattern = strings.ToLower(pattern)
// Handle wildcard patterns
if pattern == "*" {
return true
}
// Handle glob-style patterns
if matched, _ := filepath.Match(pattern, directory); matched {
return true
}
// Handle substring matching for directory paths
if strings.Contains(directory, strings.Trim(pattern, "*")) {
return true
}
}
return false
}
// UpdateRoleWeight updates the context weight for a role (for RL learning)
func (rm *RoleManager) UpdateRoleWeight(role AgentRole, newWeight float64) error {
rm.mu.Lock()
defer rm.mu.Unlock()
config, exists := rm.roles[role]
if !exists {
return fmt.Errorf("role %s does not exist", role)
}
// Clamp weight to reasonable bounds
if newWeight < 0.1 {
newWeight = 0.1
}
if newWeight > 2.0 {
newWeight = 2.0
}
config.ContextWeight = newWeight
return nil
}
// GetAgentsByRole returns all agents assigned to a specific role
func (rm *RoleManager) GetAgentsByRole(role AgentRole) []string {
rm.mu.RLock()
defer rm.mu.RUnlock()
var agents []string
for agentID, agentRole := range rm.agentRoles {
if agentRole == role {
agents = append(agents, agentID)
}
}
return agents
}
// GetCapabilitiesForRole returns capabilities for a specific role
func (rm *RoleManager) GetCapabilitiesForRole(role AgentRole) ([]RoleCapability, bool) {
config, exists := rm.GetRoleConfig(role)
if !exists {
return nil, false
}
return config.Capabilities, true
}
// CanHandleTaskType checks if a role can handle a specific task type
func (rm *RoleManager) CanHandleTaskType(role AgentRole, taskType string) bool {
config, exists := rm.GetRoleConfig(role)
if !exists {
return false
}
for _, supportedType := range config.TaskTypes {
if supportedType == taskType {
return true
}
}
return false
}
// GetBestRoleForDirectory returns the best role for a given directory
func (rm *RoleManager) GetBestRoleForDirectory(directory string) (AgentRole, float64) {
bestRole := GeneralRole
bestScore := 0.0
for role, config := range rm.roles {
if rm.matchesPatterns(directory, config.DirectoryScopes.Patterns) {
score := config.ContextWeight * float64(config.Priority) / 5.0
if score > bestScore {
bestScore = score
bestRole = role
}
}
}
return bestRole, bestScore
}
// String returns string representation of AgentRole
func (ar AgentRole) String() string {
return string(ar)
}
// IsValid checks if the agent role is valid
func (ar AgentRole) IsValid() bool {
switch ar {
case BackendRole, FrontendRole, DevOpsRole, QARole, TestingRole, GeneralRole:
return true
default:
return false
}
}