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"` Claude AgentConfig `yaml:"claude" json:"claude"` // alias for backward compatibility 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 }