206 lines
6.4 KiB
Go
206 lines
6.4 KiB
Go
// Package controller provides the business logic coordination layer for the orchestrator.
|
|
package controller
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/kandev/kandev/internal/orchestrator"
|
|
"github.com/kandev/kandev/internal/orchestrator/dto"
|
|
)
|
|
|
|
// Controller coordinates orchestrator business logic
|
|
type Controller struct {
|
|
service *orchestrator.Service
|
|
}
|
|
|
|
// NewController creates a new orchestrator controller
|
|
func NewController(svc *orchestrator.Service) *Controller {
|
|
return &Controller{
|
|
service: svc,
|
|
}
|
|
}
|
|
|
|
// GetStatus returns the orchestrator status
|
|
func (c *Controller) GetStatus(ctx context.Context, req dto.GetStatusRequest) (dto.StatusResponse, error) {
|
|
status := c.service.GetStatus()
|
|
return dto.StatusResponse{
|
|
Running: status.Running,
|
|
ActiveAgents: status.ActiveAgents,
|
|
QueuedTasks: status.QueuedTasks,
|
|
}, nil
|
|
}
|
|
|
|
// GetQueue returns the queued tasks
|
|
func (c *Controller) GetQueue(ctx context.Context, req dto.GetQueueRequest) (dto.QueueResponse, error) {
|
|
queuedTasks := c.service.GetQueuedTasks()
|
|
|
|
tasks := make([]dto.QueuedTaskDTO, 0, len(queuedTasks))
|
|
for _, qt := range queuedTasks {
|
|
tasks = append(tasks, dto.QueuedTaskDTO{
|
|
TaskID: qt.TaskID,
|
|
Priority: qt.Priority,
|
|
QueuedAt: qt.QueuedAt.Format("2006-01-02T15:04:05Z"),
|
|
})
|
|
}
|
|
|
|
return dto.QueueResponse{
|
|
Tasks: tasks,
|
|
Total: len(tasks),
|
|
}, nil
|
|
}
|
|
|
|
// TriggerTask triggers a task for execution
|
|
func (c *Controller) TriggerTask(ctx context.Context, req dto.TriggerTaskRequest) (dto.TriggerTaskResponse, error) {
|
|
// For now, just return success - the service would need task details
|
|
return dto.TriggerTaskResponse{
|
|
Success: true,
|
|
Message: "Task triggered",
|
|
TaskID: req.TaskID,
|
|
}, nil
|
|
}
|
|
|
|
// StartTask starts task execution.
|
|
// If SessionID is provided, resumes the existing session and sends a prompt with workflow step config.
|
|
// If SessionID is not provided, creates a new session (requires AgentProfileID).
|
|
func (c *Controller) StartTask(ctx context.Context, req dto.StartTaskRequest) (dto.StartTaskResponse, error) {
|
|
// If session_id is provided, resume existing session with workflow step config
|
|
if req.SessionID != "" {
|
|
err := c.service.StartSessionForWorkflowStep(ctx, req.TaskID, req.SessionID, req.WorkflowStepID)
|
|
if err != nil {
|
|
return dto.StartTaskResponse{}, err
|
|
}
|
|
return dto.StartTaskResponse{
|
|
Success: true,
|
|
TaskID: req.TaskID,
|
|
TaskSessionID: req.SessionID,
|
|
State: "RUNNING",
|
|
}, nil
|
|
}
|
|
|
|
// Otherwise, create a new session
|
|
execution, err := c.service.StartTask(ctx, req.TaskID, req.AgentProfileID, req.ExecutorID, req.Priority, req.Prompt, req.WorkflowStepID)
|
|
if err != nil {
|
|
return dto.StartTaskResponse{}, err
|
|
}
|
|
|
|
resp := dto.StartTaskResponse{
|
|
Success: true,
|
|
TaskID: execution.TaskID,
|
|
AgentExecutionID: execution.AgentExecutionID,
|
|
TaskSessionID: execution.SessionID,
|
|
State: string(execution.SessionState),
|
|
}
|
|
|
|
// Include worktree info if available
|
|
if execution.WorktreePath != "" {
|
|
resp.WorktreePath = &execution.WorktreePath
|
|
}
|
|
if execution.WorktreeBranch != "" {
|
|
resp.WorktreeBranch = &execution.WorktreeBranch
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
// ResumeTaskSession resumes a specific task session execution.
|
|
func (c *Controller) ResumeTaskSession(ctx context.Context, req dto.ResumeTaskSessionRequest) (dto.ResumeTaskSessionResponse, error) {
|
|
execution, err := c.service.ResumeTaskSession(ctx, req.TaskID, req.TaskSessionID)
|
|
if err != nil {
|
|
return dto.ResumeTaskSessionResponse{}, err
|
|
}
|
|
|
|
resp := dto.ResumeTaskSessionResponse{
|
|
Success: true,
|
|
TaskID: execution.TaskID,
|
|
AgentExecutionID: execution.AgentExecutionID,
|
|
TaskSessionID: execution.SessionID,
|
|
State: string(execution.SessionState),
|
|
}
|
|
|
|
if execution.WorktreePath != "" {
|
|
resp.WorktreePath = &execution.WorktreePath
|
|
}
|
|
if execution.WorktreeBranch != "" {
|
|
resp.WorktreeBranch = &execution.WorktreeBranch
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
// GetTaskSessionStatus returns the status of a task session including whether it's resumable
|
|
func (c *Controller) GetTaskSessionStatus(ctx context.Context, req dto.TaskSessionStatusRequest) (dto.TaskSessionStatusResponse, error) {
|
|
status, err := c.service.GetTaskSessionStatus(ctx, req.TaskID, req.TaskSessionID)
|
|
if err != nil {
|
|
return dto.TaskSessionStatusResponse{
|
|
SessionID: req.TaskSessionID,
|
|
TaskID: req.TaskID,
|
|
Error: err.Error(),
|
|
}, nil
|
|
}
|
|
return status, nil
|
|
}
|
|
|
|
// StopTask stops task execution
|
|
func (c *Controller) StopTask(ctx context.Context, req dto.StopTaskRequest) (dto.SuccessResponse, error) {
|
|
reason := req.Reason
|
|
if reason == "" {
|
|
reason = "stopped via API"
|
|
}
|
|
|
|
if err := c.service.StopTask(ctx, req.TaskID, reason, req.Force); err != nil {
|
|
return dto.SuccessResponse{}, err
|
|
}
|
|
|
|
return dto.SuccessResponse{Success: true}, nil
|
|
}
|
|
|
|
// PromptTask sends a prompt to a running task
|
|
func (c *Controller) PromptTask(ctx context.Context, req dto.PromptTaskRequest) (dto.PromptTaskResponse, error) {
|
|
result, err := c.service.PromptTask(ctx, req.TaskID, req.TaskSessionID, req.Prompt, req.Model, req.PlanMode)
|
|
if err != nil {
|
|
return dto.PromptTaskResponse{}, err
|
|
}
|
|
|
|
return dto.PromptTaskResponse{
|
|
Success: true,
|
|
StopReason: result.StopReason,
|
|
}, nil
|
|
}
|
|
|
|
// CompleteTask marks a task as complete
|
|
func (c *Controller) CompleteTask(ctx context.Context, req dto.CompleteTaskRequest) (dto.CompleteTaskResponse, error) {
|
|
if err := c.service.CompleteTask(ctx, req.TaskID); err != nil {
|
|
return dto.CompleteTaskResponse{}, err
|
|
}
|
|
|
|
return dto.CompleteTaskResponse{
|
|
Success: true,
|
|
Message: "task completed",
|
|
}, nil
|
|
}
|
|
|
|
// RespondToPermission responds to a permission request
|
|
func (c *Controller) RespondToPermission(ctx context.Context, req dto.PermissionRespondRequest) (dto.PermissionRespondResponse, error) {
|
|
if err := c.service.RespondToPermission(ctx, req.SessionID, req.PendingID, req.OptionID, req.Cancelled); err != nil {
|
|
return dto.PermissionRespondResponse{}, err
|
|
}
|
|
|
|
return dto.PermissionRespondResponse{
|
|
Success: true,
|
|
SessionID: req.SessionID,
|
|
PendingID: req.PendingID,
|
|
}, nil
|
|
}
|
|
|
|
// CancelAgent interrupts the current agent turn without terminating the process.
|
|
func (c *Controller) CancelAgent(ctx context.Context, req dto.CancelAgentRequest) (dto.CancelAgentResponse, error) {
|
|
if err := c.service.CancelAgent(ctx, req.SessionID); err != nil {
|
|
return dto.CancelAgentResponse{}, err
|
|
}
|
|
|
|
return dto.CancelAgentResponse{
|
|
Success: true,
|
|
SessionID: req.SessionID,
|
|
}, nil
|
|
}
|