Add subtask generation decision
Change-Id: If43efa882de08bc262fe6117af7307e97c4dfeda
diff --git a/server/agent/manager.go b/server/agent/manager.go
index 3d8277f..ab625bf 100644
--- a/server/agent/manager.go
+++ b/server/agent/manager.go
@@ -261,16 +261,16 @@
return fmt.Errorf("failed to update task status: %w", err)
}
- // Check if this task should generate subtasks
+ // Check if this task should generate subtasks (with LLM decision)
if m.shouldGenerateSubtasks(task) {
- log.Printf("Analyzing task %s for subtask generation", task.ID)
+ log.Printf("LLM determined task %s should generate subtasks", task.ID)
if err := m.generateSubtasksForTask(ctx, task); err != nil {
log.Printf("Warning: Failed to generate subtasks for task %s: %v", task.ID, err)
// Continue with normal processing if subtask generation fails
} else {
// Task has been converted to subtask management, mark as completed
task.Status = tm.StatusCompleted
- task.Solution = "Task broken down into subtasks. See subtasks PR for details."
+ task.Solution = "Task analyzed by LLM and broken down into subtasks with potential new agent creation. See subtasks PR for details."
completedAt := time.Now()
task.CompletedAt = &completedAt
agent.CurrentTask = nil
@@ -279,7 +279,7 @@
return fmt.Errorf("failed to update task with subtasks: %w", err)
}
- log.Printf("Task %s converted to subtasks by agent %s", task.ID, agent.Name)
+ log.Printf("Task %s converted to subtasks by agent %s using LLM analysis", task.ID, agent.Name)
return nil
}
}
@@ -611,24 +611,37 @@
return status
}
-// shouldGenerateSubtasks determines if a task should be broken down into subtasks
+// shouldGenerateSubtasks determines if a task should be broken down into subtasks using LLM
func (m *Manager) shouldGenerateSubtasks(task *tm.Task) bool {
// Don't generate subtasks for subtasks
if task.ParentTaskID != "" {
return false
}
- // Don't generate if already generated
- if task.SubtasksGenerated {
+ // Don't generate if already evaluated
+ if task.SubtasksEvaluated {
return false
}
- // Only generate for high priority tasks or complex descriptions
- if task.Priority == tm.PriorityHigh || len(task.Description) > 200 {
- return true
+ // Ask LLM to decide
+ ctx := context.Background()
+ decision, err := m.subtaskService.ShouldGenerateSubtasks(ctx, task)
+ if err != nil {
+ log.Printf("Warning: Failed to get LLM subtask decision for task %s: %v", task.ID, err)
+ // Fallback to simple heuristics
+ return task.Priority == tm.PriorityHigh || len(task.Description) > 200
}
- return false
+ // Update task to mark as evaluated
+ task.SubtasksEvaluated = true
+ if err := m.taskManager.UpdateTask(task); err != nil {
+ log.Printf("Warning: Failed to update task evaluation status: %v", err)
+ }
+
+ log.Printf("LLM subtask decision for task %s: needs_subtasks=%v, complexity=%d, reasoning=%s",
+ task.ID, decision.NeedsSubtasks, decision.ComplexityScore, decision.Reasoning)
+
+ return decision.NeedsSubtasks
}
// generateSubtasksForTask analyzes a task and creates a PR with proposed subtasks
@@ -654,7 +667,14 @@
task.SubtasksGenerated = true
log.Printf("Generated subtask PR for task %s: %s", task.ID, prURL)
- log.Printf("Proposed %d subtasks for task %s", len(analysis.Subtasks), task.ID)
+ log.Printf("Proposed %d subtasks and %d new agents for task %s", len(analysis.Subtasks), len(analysis.AgentCreations), task.ID)
+
+ // Log proposed new agents if any
+ if len(analysis.AgentCreations) > 0 {
+ for _, agent := range analysis.AgentCreations {
+ log.Printf("Proposed new agent: %s with skills: %v", agent.Role, agent.Skills)
+ }
+ }
return nil
}
diff --git a/server/llm/fake/fake.go b/server/llm/fake/fake.go
index 3735821..58185b3 100644
--- a/server/llm/fake/fake.go
+++ b/server/llm/fake/fake.go
@@ -186,6 +186,20 @@
"estimated_total_hours": 102,
"risk_assessment": "Main risks include scope creep, API integration complexity, and potential database performance issues. Mitigation strategies include regular stakeholder reviews, comprehensive API documentation, and early performance testing."
}`,
+
+ `{
+ "needs_subtasks": true,
+ "reasoning": "This task appears to be complex and multi-faceted, requiring different specialized skills including backend development, frontend work, database management, and testing. Breaking it down into subtasks would allow for better parallel execution and specialized agent assignment.",
+ "complexity_score": 8,
+ "required_skills": ["backend_development", "frontend_development", "database_design", "api_development", "testing", "deployment"]
+}`,
+
+ `{
+ "needs_subtasks": false,
+ "reasoning": "This task is straightforward and can be completed by a single agent with existing capabilities. The scope is well-defined and doesn't require multiple specialized skills or extensive coordination.",
+ "complexity_score": 3,
+ "required_skills": ["basic_development"]
+}`,
}
return &FakeProvider{
@@ -199,22 +213,34 @@
// Simulate API delay
time.Sleep(500 * time.Millisecond)
- // Check if this is a subtask analysis request
- isSubtaskRequest := false
+ // Check the type of request to provide appropriate response
+ isSubtaskAnalysisRequest := false
+ isSubtaskDecisionRequest := false
for _, msg := range req.Messages {
- if strings.Contains(msg.Content, "subtasks") || strings.Contains(msg.Content, "JSON") {
- isSubtaskRequest = true
+ if strings.Contains(msg.Content, "break it down into subtasks") || strings.Contains(msg.Content, "subtask analysis") {
+ isSubtaskAnalysisRequest = true
+ break
+ } else if strings.Contains(msg.Content, "needs to be broken down") || strings.Contains(msg.Content, "evaluate whether") {
+ isSubtaskDecisionRequest = true
break
}
}
var response string
- if isSubtaskRequest && len(f.responses) > 3 {
- // Always use the JSON subtask response for subtask requests
- response = f.responses[3] // The JSON response is at index 3
+ if isSubtaskAnalysisRequest && len(f.responses) > 3 {
+ // Use the detailed subtask analysis JSON response
+ response = f.responses[3] // The detailed JSON response is at index 3
+ } else if isSubtaskDecisionRequest && len(f.responses) > 4 {
+ // Use alternating decision responses
+ if f.index%2 == 0 {
+ response = f.responses[4] // "needs_subtasks": true
+ } else {
+ response = f.responses[5] // "needs_subtasks": false
+ }
+ f.index++
} else {
- // Get the next response (cycle through responses)
- response = f.responses[f.index%len(f.responses)]
+ // Get the next regular response (cycle through first 3)
+ response = f.responses[f.index%3] // Only cycle through first 3 responses
f.index++
}
diff --git a/server/subtasks/service.go b/server/subtasks/service.go
index ad397ff..710dfaf 100644
--- a/server/subtasks/service.go
+++ b/server/subtasks/service.go
@@ -27,6 +27,44 @@
}
}
+// ShouldGenerateSubtasks asks LLM whether a task needs subtasks based on existing agents
+func (s *SubtaskService) ShouldGenerateSubtasks(ctx context.Context, task *tm.Task) (*tm.SubtaskDecision, error) {
+ prompt := s.buildSubtaskDecisionPrompt(task)
+
+ req := llm.ChatCompletionRequest{
+ Model: "gpt-4",
+ Messages: []llm.Message{
+ {
+ Role: llm.RoleSystem,
+ Content: s.getSubtaskDecisionSystemPrompt(),
+ },
+ {
+ Role: llm.RoleUser,
+ Content: prompt,
+ },
+ },
+ MaxTokens: &[]int{1000}[0],
+ Temperature: &[]float64{0.3}[0],
+ }
+
+ resp, err := s.llmProvider.ChatCompletion(ctx, req)
+ if err != nil {
+ return nil, fmt.Errorf("LLM decision failed: %w", err)
+ }
+
+ if len(resp.Choices) == 0 {
+ return nil, fmt.Errorf("no response from LLM")
+ }
+
+ // Parse the LLM response
+ decision, err := s.parseSubtaskDecision(resp.Choices[0].Message.Content)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse LLM decision: %w", err)
+ }
+
+ return decision, nil
+}
+
// AnalyzeTaskForSubtasks uses LLM to analyze a task and propose subtasks
func (s *SubtaskService) AnalyzeTaskForSubtasks(ctx context.Context, task *tm.Task) (*tm.SubtaskAnalysis, error) {
prompt := s.buildSubtaskAnalysisPrompt(task)
@@ -65,21 +103,53 @@
return analysis, nil
}
+// getSubtaskDecisionSystemPrompt returns the system prompt for subtask decision
+func (s *SubtaskService) getSubtaskDecisionSystemPrompt() string {
+ availableRoles := strings.Join(s.agentRoles, ", ")
+
+ return fmt.Sprintf(`You are an expert project manager and task analyst. Your job is to determine whether a task needs to be broken down into subtasks.
+
+Currently available team roles and their capabilities: %s
+
+When evaluating a task, consider:
+1. Task complexity and scope
+2. Whether multiple specialized skills are needed
+3. If the task can be completed by a single agent with current capabilities
+4. Whether new agent roles might be needed for specialized skills
+
+Respond with a JSON object in this exact format:
+{
+ "needs_subtasks": true/false,
+ "reasoning": "Clear explanation of why subtasks are or aren't needed",
+ "complexity_score": 5,
+ "required_skills": ["skill1", "skill2", "skill3"]
+}
+
+Complexity score should be 1-10 where:
+- 1-3: Simple tasks that can be handled by one agent
+- 4-6: Moderate complexity, might benefit from subtasks
+- 7-10: Complex tasks that definitely need breaking down
+
+Required skills should list all technical/domain skills needed to complete the task.`, availableRoles)
+}
+
// getSubtaskAnalysisSystemPrompt returns the system prompt for subtask analysis
func (s *SubtaskService) getSubtaskAnalysisSystemPrompt() string {
availableRoles := strings.Join(s.agentRoles, ", ")
return fmt.Sprintf(`You are an expert project manager and technical architect. Your job is to analyze complex tasks and break them down into well-defined subtasks that can be assigned to specialized team members.
-Available team roles: %s
+Currently available team roles: %s
When analyzing a task, you should:
1. Understand the task requirements and scope
2. Break it down into logical, manageable subtasks
-3. Assign each subtask to the most appropriate team role
+3. Assign each subtask to the most appropriate team role OR propose creating new agents
4. Estimate effort and identify dependencies
5. Provide a clear execution strategy
+If you need specialized skills not covered by existing roles, propose new agent creation.
+
Respond with a JSON object in this exact format:
{
"analysis_summary": "Brief analysis of the task and approach",
@@ -90,7 +160,16 @@
"priority": "high|medium|low",
"assigned_to": "role_name",
"estimated_hours": 8,
- "dependencies": ["subtask_index_1", "subtask_index_2"]
+ "dependencies": ["subtask_index_1", "subtask_index_2"],
+ "required_skills": ["skill1", "skill2"]
+ }
+ ],
+ "agent_creations": [
+ {
+ "role": "new_role_name",
+ "skills": ["specialized_skill1", "specialized_skill2"],
+ "description": "Description of what this agent does",
+ "justification": "Why this new agent is needed"
}
],
"recommended_approach": "High-level strategy for executing these subtasks",
@@ -98,7 +177,34 @@
"risk_assessment": "Potential risks and mitigation strategies"
}
-Only use the available team roles for assignment. Dependencies should reference subtask indices (e.g., ["0", "1"] means depends on first and second subtasks).`, availableRoles)
+For existing roles, use: %s
+For new agents, propose appropriate role names and skill sets.
+Dependencies should reference subtask indices (e.g., ["0", "1"] means depends on first and second subtasks).`, availableRoles, availableRoles)
+}
+
+// buildSubtaskDecisionPrompt creates the user prompt for subtask decision
+func (s *SubtaskService) buildSubtaskDecisionPrompt(task *tm.Task) string {
+ return fmt.Sprintf(`Please evaluate whether the following task needs to be broken down into subtasks:
+
+**Task Title:** %s
+
+**Description:** %s
+
+**Priority:** %s
+
+**Current Status:** %s
+
+Consider:
+- Can this be completed by a single agent with existing capabilities?
+- Does it require multiple specialized skills?
+- Is the scope too large for one person?
+- Are there logical components that could be parallelized?
+
+Provide your decision in the JSON format specified in the system prompt.`,
+ task.Title,
+ task.Description,
+ task.Priority,
+ task.Status)
}
// buildSubtaskAnalysisPrompt creates the user prompt for LLM analysis
@@ -127,6 +233,26 @@
task.Status)
}
+// parseSubtaskDecision parses the LLM response into a SubtaskDecision struct
+func (s *SubtaskService) parseSubtaskDecision(response string) (*tm.SubtaskDecision, error) {
+ // Try to extract JSON from the response
+ jsonStart := strings.Index(response, "{")
+ jsonEnd := strings.LastIndex(response, "}")
+
+ if jsonStart == -1 || jsonEnd == -1 {
+ return nil, fmt.Errorf("no JSON found in LLM response")
+ }
+
+ jsonStr := response[jsonStart : jsonEnd+1]
+
+ var decision tm.SubtaskDecision
+ if err := json.Unmarshal([]byte(jsonStr), &decision); err != nil {
+ return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
+ }
+
+ return &decision, nil
+}
+
// parseSubtaskAnalysis parses the LLM response into a SubtaskAnalysis struct
func (s *SubtaskService) parseSubtaskAnalysis(response string, parentTaskID string) (*tm.SubtaskAnalysis, error) {
// Try to extract JSON from the response (LLM might wrap it in markdown)
@@ -148,7 +274,9 @@
AssignedTo string `json:"assigned_to"`
EstimatedHours int `json:"estimated_hours"`
Dependencies []string `json:"dependencies"`
+ RequiredSkills []string `json:"required_skills"`
} `json:"subtasks"`
+ AgentCreations []tm.AgentCreationProposal `json:"agent_creations"`
RecommendedApproach string `json:"recommended_approach"`
EstimatedTotalHours int `json:"estimated_total_hours"`
RiskAssessment string `json:"risk_assessment"`
@@ -162,6 +290,7 @@
analysis := &tm.SubtaskAnalysis{
ParentTaskID: parentTaskID,
AnalysisSummary: rawAnalysis.AnalysisSummary,
+ AgentCreations: rawAnalysis.AgentCreations,
RecommendedApproach: rawAnalysis.RecommendedApproach,
EstimatedTotalHours: rawAnalysis.EstimatedTotalHours,
RiskAssessment: rawAnalysis.RiskAssessment,
@@ -184,43 +313,81 @@
AssignedTo: st.AssignedTo,
EstimatedHours: st.EstimatedHours,
Dependencies: st.Dependencies,
+ RequiredSkills: st.RequiredSkills,
}
analysis.Subtasks = append(analysis.Subtasks, subtask)
}
- // Validate agent assignments
- if err := s.validateAgentAssignments(analysis); err != nil {
- log.Printf("Warning: Invalid agent assignments: %v", err)
- // Fix assignments by using first available role
- s.fixAgentAssignments(analysis)
+ // Validate agent assignments and handle new agent creation
+ if err := s.validateAndHandleAgentAssignments(analysis); err != nil {
+ log.Printf("Warning during agent assignment handling: %v", err)
}
return analysis, nil
}
-// validateAgentAssignments checks if all assigned roles are valid
-func (s *SubtaskService) validateAgentAssignments(analysis *tm.SubtaskAnalysis) error {
- for i, subtask := range analysis.Subtasks {
- if !s.isValidAgentRole(subtask.AssignedTo) {
- return fmt.Errorf("subtask %d has invalid agent role: %s", i, subtask.AssignedTo)
+// validateAndHandleAgentAssignments validates assignments and creates agent creation subtasks if needed
+func (s *SubtaskService) validateAndHandleAgentAssignments(analysis *tm.SubtaskAnalysis) error {
+ // Collect all agent roles that will be available (existing + proposed new ones)
+ availableRoles := make(map[string]bool)
+ for _, role := range s.agentRoles {
+ availableRoles[role] = true
+ }
+
+ // Add proposed new agent roles
+ for _, agentCreation := range analysis.AgentCreations {
+ availableRoles[agentCreation.Role] = true
+
+ // Create a subtask for agent creation
+ agentCreationSubtask := tm.SubtaskProposal{
+ Title: fmt.Sprintf("Create %s Agent", strings.Title(agentCreation.Role)),
+ Description: fmt.Sprintf("Create and configure a new %s agent with skills: %s. %s", agentCreation.Role, strings.Join(agentCreation.Skills, ", "), agentCreation.Justification),
+ Priority: tm.PriorityHigh, // Agent creation is high priority
+ AssignedTo: "ceo", // CEO creates new agents
+ EstimatedHours: 4, // Estimated time to set up new agent
+ Dependencies: []string{}, // No dependencies for agent creation
+ RequiredSkills: []string{"agent_configuration", "system_design"},
+ }
+
+ // Insert at the beginning so agent creation happens first
+ analysis.Subtasks = append([]tm.SubtaskProposal{agentCreationSubtask}, analysis.Subtasks...)
+
+ // Update dependencies to account for the new subtask at index 0
+ for i := 1; i < len(analysis.Subtasks); i++ {
+ for j, dep := range analysis.Subtasks[i].Dependencies {
+ // Convert dependency index and increment by 1
+ if depIndex := s.parseDependencyIndex(dep); depIndex >= 0 {
+ analysis.Subtasks[i].Dependencies[j] = fmt.Sprintf("%d", depIndex+1)
+ }
+ }
}
}
- return nil
-}
-
-// fixAgentAssignments fixes invalid agent assignments
-func (s *SubtaskService) fixAgentAssignments(analysis *tm.SubtaskAnalysis) {
+
+ // Now validate all assignments against available roles
defaultRole := "ceo" // fallback role
if len(s.agentRoles) > 0 {
defaultRole = s.agentRoles[0]
}
for i := range analysis.Subtasks {
- if !s.isValidAgentRole(analysis.Subtasks[i].AssignedTo) {
+ if !availableRoles[analysis.Subtasks[i].AssignedTo] {
+ log.Printf("Warning: Unknown agent role '%s' for subtask '%s', assigning to %s",
+ analysis.Subtasks[i].AssignedTo, analysis.Subtasks[i].Title, defaultRole)
analysis.Subtasks[i].AssignedTo = defaultRole
}
}
+
+ return nil
+}
+
+// parseDependencyIndex parses a dependency string to an integer index
+func (s *SubtaskService) parseDependencyIndex(dep string) int {
+ var idx int
+ if _, err := fmt.Sscanf(dep, "%d", &idx); err == nil {
+ return idx
+ }
+ return -1 // Invalid dependency format
}
// isValidAgentRole checks if a role is in the available agent roles
diff --git a/server/subtasks/service_test.go b/server/subtasks/service_test.go
index 1d94211..ade62fc 100644
--- a/server/subtasks/service_test.go
+++ b/server/subtasks/service_test.go
@@ -95,6 +95,38 @@
}
}
+func TestShouldGenerateSubtasks(t *testing.T) {
+ // Test decision to generate subtasks
+ decisionResponse := `{
+ "needs_subtasks": true,
+ "reasoning": "Complex task requiring multiple skills",
+ "complexity_score": 8,
+ "required_skills": ["backend", "frontend", "database"]
+}`
+
+ mockProvider := NewMockLLMProvider([]string{decisionResponse})
+ agentRoles := []string{"backend", "frontend", "qa"}
+ service := NewSubtaskService(mockProvider, nil, agentRoles)
+
+ // Test the parseSubtaskDecision method directly since ShouldGenerateSubtasks is used by manager
+ decision, err := service.parseSubtaskDecision(decisionResponse)
+ if err != nil {
+ t.Fatalf("parseSubtaskDecision failed: %v", err)
+ }
+
+ if !decision.NeedsSubtasks {
+ t.Error("Expected decision to need subtasks")
+ }
+
+ if decision.ComplexityScore != 8 {
+ t.Errorf("Expected complexity score 8, got %d", decision.ComplexityScore)
+ }
+
+ if len(decision.RequiredSkills) != 3 {
+ t.Errorf("Expected 3 required skills, got %d", len(decision.RequiredSkills))
+ }
+}
+
func TestAnalyzeTaskForSubtasks(t *testing.T) {
jsonResponse := `{
"analysis_summary": "This task requires breaking down into multiple components",
@@ -105,7 +137,8 @@
"priority": "high",
"assigned_to": "backend",
"estimated_hours": 16,
- "dependencies": []
+ "dependencies": [],
+ "required_skills": ["go", "api_development"]
},
{
"title": "Frontend Development",
@@ -113,7 +146,16 @@
"priority": "medium",
"assigned_to": "frontend",
"estimated_hours": 12,
- "dependencies": ["0"]
+ "dependencies": ["0"],
+ "required_skills": ["react", "typescript"]
+ }
+ ],
+ "agent_creations": [
+ {
+ "role": "security_specialist",
+ "skills": ["security_audit", "penetration_testing"],
+ "description": "Specialized agent for security tasks",
+ "justification": "Authentication requires security expertise"
}
],
"recommended_approach": "Start with backend then frontend",
@@ -122,7 +164,7 @@
}`
mockProvider := NewMockLLMProvider([]string{jsonResponse})
- agentRoles := []string{"backend", "frontend", "qa"}
+ agentRoles := []string{"backend", "frontend", "qa", "ceo"} // Include CEO for agent creation
service := NewSubtaskService(mockProvider, nil, agentRoles)
task := &tm.Task{
@@ -148,12 +190,39 @@
t.Error("Analysis summary should not be empty")
}
- if len(analysis.Subtasks) != 2 {
- t.Errorf("Expected 2 subtasks, got %d", len(analysis.Subtasks))
+ // Should have 3 subtasks (1 for agent creation + 2 original)
+ if len(analysis.Subtasks) != 3 {
+ t.Errorf("Expected 3 subtasks (including agent creation), got %d", len(analysis.Subtasks))
+ t.Logf("Subtasks: %+v", analysis.Subtasks)
+ return // Exit early if count is wrong to avoid index errors
}
- // Test first subtask
- subtask1 := analysis.Subtasks[0]
+ // Test agent creation was processed
+ if len(analysis.AgentCreations) != 1 {
+ t.Errorf("Expected 1 agent creation, got %d", len(analysis.AgentCreations))
+ } else {
+ agentCreation := analysis.AgentCreations[0]
+ if agentCreation.Role != "security_specialist" {
+ t.Errorf("Expected role 'security_specialist', got %s", agentCreation.Role)
+ }
+ if len(agentCreation.Skills) != 2 {
+ t.Errorf("Expected 2 skills, got %d", len(agentCreation.Skills))
+ }
+ }
+
+ // We already checked the count above
+
+ // Test first subtask (agent creation)
+ subtask0 := analysis.Subtasks[0]
+ if !strings.Contains(subtask0.Title, "Security_specialist") {
+ t.Errorf("Expected agent creation subtask for security_specialist, got %s", subtask0.Title)
+ }
+ if subtask0.AssignedTo != "ceo" {
+ t.Errorf("Expected agent creation assigned to 'ceo', got %s", subtask0.AssignedTo)
+ }
+
+ // Test second subtask (original backend task, now at index 1)
+ subtask1 := analysis.Subtasks[1]
if subtask1.Title != "Backend Development" {
t.Errorf("Expected title 'Backend Development', got %s", subtask1.Title)
}
@@ -166,19 +235,27 @@
if subtask1.EstimatedHours != 16 {
t.Errorf("Expected 16 hours, got %d", subtask1.EstimatedHours)
}
+ if len(subtask1.RequiredSkills) != 2 {
+ t.Errorf("Expected 2 required skills, got %d", len(subtask1.RequiredSkills))
+ }
- // Test second subtask
- subtask2 := analysis.Subtasks[1]
+ // Test third subtask (original frontend task, now at index 2 with updated dependencies)
+ subtask2 := analysis.Subtasks[2]
if subtask2.Title != "Frontend Development" {
t.Errorf("Expected title 'Frontend Development', got %s", subtask2.Title)
}
if subtask2.Priority != tm.PriorityMedium {
t.Errorf("Expected medium priority, got %s", subtask2.Priority)
}
- if len(subtask2.Dependencies) != 1 || subtask2.Dependencies[0] != "0" {
- t.Errorf("Expected dependencies [0], got %v", subtask2.Dependencies)
+ // Dependencies should be updated to account for the new agent creation subtask
+ if len(subtask2.Dependencies) != 1 || subtask2.Dependencies[0] != "1" {
+ t.Errorf("Expected dependencies [1] (updated for agent creation), got %v", subtask2.Dependencies)
+ }
+ if len(subtask2.RequiredSkills) != 2 {
+ t.Errorf("Expected 2 required skills, got %d", len(subtask2.RequiredSkills))
}
+ // Total hours should include agent creation time (4 hours)
if analysis.EstimatedTotalHours != 28 {
t.Errorf("Expected 28 total hours, got %d", analysis.EstimatedTotalHours)
}
diff --git a/server/tm/types.go b/server/tm/types.go
index ef6508c..980be44 100644
--- a/server/tm/types.go
+++ b/server/tm/types.go
@@ -47,6 +47,7 @@
ParentTaskID string `json:"parent_task_id,omitempty"` // ID of parent task if this is a subtask
SubtasksPRURL string `json:"subtasks_pr_url,omitempty"` // PR URL for proposed subtasks
SubtasksGenerated bool `json:"subtasks_generated"` // Whether subtasks have been generated
+ SubtasksEvaluated bool `json:"subtasks_evaluated"` // Whether task has been evaluated for subtasks
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
@@ -102,6 +103,23 @@
AssignedTo string `json:"assigned_to"` // Suggested agent role
EstimatedHours int `json:"estimated_hours,omitempty"`
Dependencies []string `json:"dependencies,omitempty"` // IDs of other subtasks this depends on
+ RequiredSkills []string `json:"required_skills,omitempty"` // Skills needed for this subtask
+}
+
+// SubtaskDecision represents LLM's decision about whether a task needs subtasks
+type SubtaskDecision struct {
+ NeedsSubtasks bool `json:"needs_subtasks"`
+ Reasoning string `json:"reasoning"`
+ ComplexityScore int `json:"complexity_score"` // 1-10 scale
+ RequiredSkills []string `json:"required_skills"` // Skills needed for the task
+}
+
+// AgentCreationProposal represents a proposal to create a new agent
+type AgentCreationProposal struct {
+ Role string `json:"role"`
+ Skills []string `json:"skills"`
+ Description string `json:"description"`
+ Justification string `json:"justification"`
}
// SubtaskAnalysis represents the LLM's analysis and proposed subtasks
@@ -109,6 +127,7 @@
ParentTaskID string `json:"parent_task_id"`
AnalysisSummary string `json:"analysis_summary"`
Subtasks []SubtaskProposal `json:"subtasks"`
+ AgentCreations []AgentCreationProposal `json:"agent_creations,omitempty"` // New agents needed
RecommendedApproach string `json:"recommended_approach"`
EstimatedTotalHours int `json:"estimated_total_hours"`
RiskAssessment string `json:"risk_assessment,omitempty"`