summaryrefslogtreecommitdiff
path: root/internal/task/task.go
blob: b3e93d3ffc44022c3f03057f19d5e8d830249548 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package task

import "time"

type State string

const (
	StatePending       State = "PENDING"
	StateQueued        State = "QUEUED"
	StateRunning       State = "RUNNING"
	StateReady         State = "READY"
	StateCompleted     State = "COMPLETED"
	StateFailed        State = "FAILED"
	StateTimedOut      State = "TIMED_OUT"
	StateCancelled     State = "CANCELLED"
	StateBudgetExceeded State = "BUDGET_EXCEEDED"
	StateBlocked        State = "BLOCKED"
)

type Priority string

const (
	PriorityHigh   Priority = "high"
	PriorityNormal Priority = "normal"
	PriorityLow    Priority = "low"
)

type AgentConfig struct {
	Type               string   `yaml:"type"               json:"type"`
	Model              string   `yaml:"model"               json:"model"`
	ContextFiles       []string `yaml:"context_files"       json:"context_files"`
	Instructions       string   `yaml:"instructions"        json:"instructions"`
	WorkingDir         string   `yaml:"working_dir"         json:"working_dir"`
	MaxBudgetUSD       float64  `yaml:"max_budget_usd"      json:"max_budget_usd"`
	PermissionMode     string   `yaml:"permission_mode"     json:"permission_mode"`
	AllowedTools       []string `yaml:"allowed_tools"       json:"allowed_tools"`
	DisallowedTools    []string `yaml:"disallowed_tools"    json:"disallowed_tools"`
	SystemPromptAppend string   `yaml:"system_prompt_append" json:"system_prompt_append"`
	AdditionalArgs     []string `yaml:"additional_args"     json:"additional_args"`
	SkipPlanning       bool     `yaml:"skip_planning"       json:"skip_planning"`
}

type RetryConfig struct {
	MaxAttempts int    `yaml:"max_attempts" json:"max_attempts"`
	Backoff     string `yaml:"backoff"      json:"backoff"` // "linear", "exponential"
}

type Task struct {
	ID           string       `yaml:"id"            json:"id"`
	ParentTaskID string       `yaml:"parent_task_id" json:"parent_task_id"`
	Name         string       `yaml:"name"          json:"name"`
	Description string       `yaml:"description" json:"description"`
	Agent       AgentConfig `yaml:"agent"     json:"agent"`
	Timeout     Duration     `yaml:"timeout"    json:"timeout"`
	Retry       RetryConfig  `yaml:"retry"      json:"retry"`
	Priority    Priority     `yaml:"priority"   json:"priority"`
	Tags        []string     `yaml:"tags"       json:"tags"`
	DependsOn   []string     `yaml:"depends_on" json:"depends_on"`
	State             State        `yaml:"-"          json:"state"`
	RejectionComment  string       `yaml:"-"          json:"rejection_comment,omitempty"`
	QuestionJSON      string       `yaml:"-"          json:"question,omitempty"`
	CreatedAt         time.Time    `yaml:"-"          json:"created_at"`
	UpdatedAt         time.Time    `yaml:"-"          json:"updated_at"`
}

// Duration wraps time.Duration for YAML unmarshaling from strings like "30m".
type Duration struct {
	time.Duration
}

func (d *Duration) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var s string
	if err := unmarshal(&s); err != nil {
		return err
	}
	dur, err := time.ParseDuration(s)
	if err != nil {
		return err
	}
	d.Duration = dur
	return nil
}

func (d Duration) MarshalYAML() (interface{}, error) {
	return d.Duration.String(), nil
}

// BatchFile represents a YAML file containing multiple tasks.
type BatchFile struct {
	Tasks []Task `yaml:"tasks"`
}

// ValidTransition returns true if moving from the current state to next is allowed.
func ValidTransition(from, to State) bool {
	transitions := map[State][]State{
		StatePending:   {StateQueued, StateCancelled},
		StateQueued:    {StateRunning, StateCancelled},
		StateRunning:   {StateReady, StateCompleted, StateFailed, StateTimedOut, StateCancelled, StateBudgetExceeded, StateBlocked},
		StateReady:     {StateCompleted, StatePending},
		StateFailed:    {StateQueued}, // retry
		StateTimedOut:  {StateQueued}, // retry
		StateCancelled: {StateQueued}, // restart
		StateBlocked:   {StateQueued}, // answer received → re-queue as resume execution
	}
	for _, allowed := range transitions[from] {
		if allowed == to {
			return true
		}
	}
	return false
}