- Fixed module path from github.com/deepblackcloud/bzzz to github.com/anthonyrawlins/bzzz - Added dynamic Ollama model detection via /api/tags endpoint - Implemented intelligent model selection through N8N webhook integration - Added BZZZ_MODEL_SELECTION_WEBHOOK environment variable support - Fixed GitHub assignee issue by using valid username instead of peer ID - Added comprehensive model fallback mechanisms - Updated all import statements across the codebase - Removed duplicate systemd service file - Added sandbox execution environment and type definitions 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
254 lines
8.9 KiB
Go
254 lines
8.9 KiB
Go
package coordination
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/anthonyrawlins/bzzz/pubsub"
|
||
"github.com/libp2p/go-libp2p/core/peer"
|
||
)
|
||
|
||
// DependencyDetector analyzes tasks across repositories for relationships
|
||
type DependencyDetector struct {
|
||
pubsub *pubsub.PubSub
|
||
ctx context.Context
|
||
knownTasks map[string]*TaskContext // taskKey -> context
|
||
dependencyRules []DependencyRule
|
||
coordinationHops int
|
||
}
|
||
|
||
// TaskContext represents a task with its repository and project context
|
||
type TaskContext struct {
|
||
TaskID int `json:"task_id"`
|
||
ProjectID int `json:"project_id"`
|
||
Repository string `json:"repository"`
|
||
Title string `json:"title"`
|
||
Description string `json:"description"`
|
||
Keywords []string `json:"keywords"`
|
||
AgentID string `json:"agent_id"`
|
||
ClaimedAt time.Time `json:"claimed_at"`
|
||
}
|
||
|
||
// DependencyRule defines how to detect task relationships
|
||
type DependencyRule struct {
|
||
Name string
|
||
Description string
|
||
Keywords []string
|
||
Validator func(task1, task2 *TaskContext) (bool, string)
|
||
}
|
||
|
||
// TaskDependency represents a detected relationship between tasks
|
||
type TaskDependency struct {
|
||
Task1 *TaskContext `json:"task1"`
|
||
Task2 *TaskContext `json:"task2"`
|
||
Relationship string `json:"relationship"`
|
||
Confidence float64 `json:"confidence"`
|
||
Reason string `json:"reason"`
|
||
DetectedAt time.Time `json:"detected_at"`
|
||
}
|
||
|
||
// NewDependencyDetector creates a new cross-repository dependency detector
|
||
func NewDependencyDetector(ctx context.Context, ps *pubsub.PubSub) *DependencyDetector {
|
||
dd := &DependencyDetector{
|
||
pubsub: ps,
|
||
ctx: ctx,
|
||
knownTasks: make(map[string]*TaskContext),
|
||
coordinationHops: 3, // Limit meta discussion depth
|
||
}
|
||
|
||
// Initialize common dependency detection rules
|
||
dd.initializeDependencyRules()
|
||
|
||
// Subscribe to task announcements for dependency detection
|
||
go dd.listenForTaskAnnouncements()
|
||
|
||
return dd
|
||
}
|
||
|
||
// initializeDependencyRules sets up common patterns for task relationships
|
||
func (dd *DependencyDetector) initializeDependencyRules() {
|
||
dd.dependencyRules = []DependencyRule{
|
||
{
|
||
Name: "API_Contract",
|
||
Description: "Tasks involving API contracts and implementations",
|
||
Keywords: []string{"api", "endpoint", "contract", "interface", "schema"},
|
||
Validator: func(task1, task2 *TaskContext) (bool, string) {
|
||
// Check if one task defines API and another implements it
|
||
text1 := strings.ToLower(task1.Title + " " + task1.Description)
|
||
text2 := strings.ToLower(task2.Title + " " + task2.Description)
|
||
|
||
if (strings.Contains(text1, "api") && strings.Contains(text2, "implement")) ||
|
||
(strings.Contains(text2, "api") && strings.Contains(text1, "implement")) {
|
||
return true, "API definition and implementation dependency"
|
||
}
|
||
return false, ""
|
||
},
|
||
},
|
||
{
|
||
Name: "Database_Schema",
|
||
Description: "Database schema changes affecting multiple services",
|
||
Keywords: []string{"database", "schema", "migration", "table", "model"},
|
||
Validator: func(task1, task2 *TaskContext) (bool, string) {
|
||
text1 := strings.ToLower(task1.Title + " " + task1.Description)
|
||
text2 := strings.ToLower(task2.Title + " " + task2.Description)
|
||
|
||
dbKeywords := []string{"database", "schema", "migration", "table"}
|
||
hasDB1 := false
|
||
hasDB2 := false
|
||
|
||
for _, keyword := range dbKeywords {
|
||
if strings.Contains(text1, keyword) { hasDB1 = true }
|
||
if strings.Contains(text2, keyword) { hasDB2 = true }
|
||
}
|
||
|
||
if hasDB1 && hasDB2 {
|
||
return true, "Database schema dependency detected"
|
||
}
|
||
return false, ""
|
||
},
|
||
},
|
||
{
|
||
Name: "Configuration_Dependency",
|
||
Description: "Configuration changes affecting multiple components",
|
||
Keywords: []string{"config", "environment", "settings", "parameters"},
|
||
Validator: func(task1, task2 *TaskContext) (bool, string) {
|
||
text1 := strings.ToLower(task1.Title + " " + task1.Description)
|
||
text2 := strings.ToLower(task2.Title + " " + task2.Description)
|
||
|
||
if (strings.Contains(text1, "config") || strings.Contains(text1, "environment")) &&
|
||
(strings.Contains(text2, "config") || strings.Contains(text2, "environment")) {
|
||
return true, "Configuration dependency - coordinated changes needed"
|
||
}
|
||
return false, ""
|
||
},
|
||
},
|
||
{
|
||
Name: "Security_Compliance",
|
||
Description: "Security changes requiring coordinated implementation",
|
||
Keywords: []string{"security", "auth", "permission", "token", "encrypt"},
|
||
Validator: func(task1, task2 *TaskContext) (bool, string) {
|
||
text1 := strings.ToLower(task1.Title + " " + task1.Description)
|
||
text2 := strings.ToLower(task2.Title + " " + task2.Description)
|
||
|
||
secKeywords := []string{"security", "auth", "permission", "token"}
|
||
hasSecu1 := false
|
||
hasSecu2 := false
|
||
|
||
for _, keyword := range secKeywords {
|
||
if strings.Contains(text1, keyword) { hasSecu1 = true }
|
||
if strings.Contains(text2, keyword) { hasSecu2 = true }
|
||
}
|
||
|
||
if hasSecu1 && hasSecu2 {
|
||
return true, "Security implementation requires coordination"
|
||
}
|
||
return false, ""
|
||
},
|
||
},
|
||
}
|
||
}
|
||
|
||
// RegisterTask adds a task to the dependency tracking system
|
||
func (dd *DependencyDetector) RegisterTask(task *TaskContext) {
|
||
taskKey := fmt.Sprintf("%d:%d", task.ProjectID, task.TaskID)
|
||
dd.knownTasks[taskKey] = task
|
||
|
||
fmt.Printf("🔍 Registered task for dependency detection: %s/%s #%d\n",
|
||
task.Repository, task.Title, task.TaskID)
|
||
|
||
// Check for dependencies with existing tasks
|
||
dd.detectDependencies(task)
|
||
}
|
||
|
||
// detectDependencies analyzes a new task against existing tasks for relationships
|
||
func (dd *DependencyDetector) detectDependencies(newTask *TaskContext) {
|
||
for _, existingTask := range dd.knownTasks {
|
||
// Skip self-comparison
|
||
if existingTask.TaskID == newTask.TaskID && existingTask.ProjectID == newTask.ProjectID {
|
||
continue
|
||
}
|
||
|
||
// Skip if same repository (handled by single-repo coordination)
|
||
if existingTask.Repository == newTask.Repository {
|
||
continue
|
||
}
|
||
|
||
// Apply dependency detection rules
|
||
for _, rule := range dd.dependencyRules {
|
||
if matches, reason := rule.Validator(newTask, existingTask); matches {
|
||
dependency := &TaskDependency{
|
||
Task1: newTask,
|
||
Task2: existingTask,
|
||
Relationship: rule.Name,
|
||
Confidence: 0.8, // Could be improved with ML
|
||
Reason: reason,
|
||
DetectedAt: time.Now(),
|
||
}
|
||
|
||
dd.announceDependency(dependency)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// announceDependency broadcasts a detected dependency for agent coordination
|
||
func (dd *DependencyDetector) announceDependency(dep *TaskDependency) {
|
||
fmt.Printf("🔗 Dependency detected: %s/%s #%d ↔ %s/%s #%d (%s)\n",
|
||
dep.Task1.Repository, dep.Task1.Title, dep.Task1.TaskID,
|
||
dep.Task2.Repository, dep.Task2.Title, dep.Task2.TaskID,
|
||
dep.Relationship)
|
||
|
||
// Create coordination message for Antennae meta-discussion
|
||
coordMsg := map[string]interface{}{
|
||
"message_type": "dependency_detected",
|
||
"dependency": dep,
|
||
"coordination_request": fmt.Sprintf(
|
||
"Cross-repository dependency detected between tasks. "+
|
||
"Agent working on %s/%s #%d and agent working on %s/%s #%d should coordinate. "+
|
||
"Relationship: %s. Reason: %s",
|
||
dep.Task1.Repository, dep.Task1.Title, dep.Task1.TaskID,
|
||
dep.Task2.Repository, dep.Task2.Title, dep.Task2.TaskID,
|
||
dep.Relationship, dep.Reason,
|
||
),
|
||
"agents_involved": []string{dep.Task1.AgentID, dep.Task2.AgentID},
|
||
"repositories": []string{dep.Task1.Repository, dep.Task2.Repository},
|
||
"hop_count": 0,
|
||
"max_hops": dd.coordinationHops,
|
||
"detected_at": dep.DetectedAt.Unix(),
|
||
}
|
||
|
||
// Publish to Antennae meta-discussion channel
|
||
if err := dd.pubsub.PublishAntennaeMessage(pubsub.MetaDiscussion, coordMsg); err != nil {
|
||
fmt.Printf("❌ Failed to announce dependency: %v\n", err)
|
||
} else {
|
||
fmt.Printf("📡 Dependency coordination request sent to Antennae channel\n")
|
||
}
|
||
}
|
||
|
||
// listenForTaskAnnouncements monitors the P2P mesh for task claims
|
||
func (dd *DependencyDetector) listenForTaskAnnouncements() {
|
||
// This would integrate with the existing pubsub system
|
||
// to automatically detect when agents claim tasks
|
||
fmt.Printf("👂 Dependency detector listening for task announcements...\n")
|
||
|
||
// In a real implementation, this would subscribe to TaskClaim messages
|
||
// and extract task context for dependency analysis
|
||
}
|
||
|
||
// GetKnownTasks returns all tasks currently being tracked
|
||
func (dd *DependencyDetector) GetKnownTasks() map[string]*TaskContext {
|
||
return dd.knownTasks
|
||
}
|
||
|
||
// GetDependencyRules returns the configured dependency detection rules
|
||
func (dd *DependencyDetector) GetDependencyRules() []DependencyRule {
|
||
return dd.dependencyRules
|
||
}
|
||
|
||
// AddCustomRule allows adding project-specific dependency detection
|
||
func (dd *DependencyDetector) AddCustomRule(rule DependencyRule) {
|
||
dd.dependencyRules = append(dd.dependencyRules, rule)
|
||
fmt.Printf("➕ Added custom dependency rule: %s\n", rule.Name)
|
||
} |