package action import ( "context" "fmt" "github.com/rockliang/poimen/workflows/action/llm" "github.com/rockliang/poimen/workflows/prompts" "github.com/rockliang/poimen/workflows/statemachine" ) // PlanningInput is input to PlanningActivity. type PlanningInput struct { Config statemachine.OrchestratorConfig BoardState string // JSON or markdown of task board Milestone string } // TaskDispatch represents a dispatched task. type TaskDispatch struct { TaskID string PromptSpec statemachine.PromptSpec BaseTimeout *int64 // optional override in milliseconds } // PlanningOutput is the output of PlanningActivity. type PlanningOutput struct { Tasks []TaskDispatch SubmilestoneComplete bool } // PlanningActivity calls the Planner LLM to decide which tasks to dispatch. func PlanningActivity(ctx context.Context, in PlanningInput) (PlanningOutput, error) { // Get LLM client client, err := llm.NewClient() if err != nil { return PlanningOutput{}, fmt.Errorf("failed to create LLM client: %w", err) } // Get planner spec plannerSpec, exists := in.Config.RolePrompts["planner"] if !exists { return PlanningOutput{}, fmt.Errorf("planner role prompt not configured") } // Render template var templateContent string if plannerSpec.RawTemplate != "" { templateContent = plannerSpec.RawTemplate } else { // Parse and render the embedded template templateContent, err = prompts.Render(plannerSpec.TemplateRef, map[string]any{ "SystemPrompt": in.Config.SystemPrompt, "BoardState": in.BoardState, "Milestone": in.Milestone, "Config": in.Config, }) if err != nil { return PlanningOutput{}, fmt.Errorf("failed to render planner template: %w", err) } } // Call LLM messages := []llm.MessageParam{ { Role: "user", Content: templateContent, }, } response, err := client.CreateMessage(ctx, llm.MessageInput{ Model: plannerSpec.Model, SystemPrompt: in.Config.SystemPrompt, Messages: messages, }) if err != nil { return PlanningOutput{}, fmt.Errorf("planner LLM call failed: %w", err) } // For now, return empty dispatch (will be parsed from LLM response in full implementation) // This is a stub that allows the test to verify the activity is called _ = response return PlanningOutput{ Tasks: []TaskDispatch{}, SubmilestoneComplete: false, }, nil }