package builtin // Task 管理工具 -- 创建,列出和更新任务. // // 这是 Agent 管理工作流的能力:通过任务系统追踪多步骤工作的进展, // 将复杂任务分解为可追踪的子任务. // // 包含三个工具: // - TaskCreate:创建新任务 // - TaskList:列出所有任务 // - TaskUpdate:更新任务状态 // // 任务存储在内存中,线程安全(sync.RWMutex). // ConcurrencySafe: true import ( "context" "encoding/json" "fmt" "strings" "sync" "time" "git.flytoex.net/yuanwei/flyto-agent/pkg/permission" "git.flytoex.net/yuanwei/flyto-agent/pkg/tools" ) // TaskStatus 是任务状态. type TaskStatus string const ( TaskStatusPending TaskStatus = "pending" TaskStatusInProgress TaskStatus = "in_progress" TaskStatusDone TaskStatus = "done" TaskStatusFailed TaskStatus = "failed" TaskStatusCancelled TaskStatus = "cancelled" ) // Task 是一个任务. type Task struct { ID string `json:"id"` Title string `json:"title"` Description string `json:"description,omitempty"` Status TaskStatus `json:"status"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } // TaskStore 是任务存储,线程安全. type TaskStore struct { mu sync.RWMutex tasks map[string]*Task nextID int } // NewTaskStore 创建一个新的任务存储. func NewTaskStore() *TaskStore { return &TaskStore{ tasks: make(map[string]*Task), nextID: 1, } } // Create 创建一个新任务. func (s *TaskStore) Create(title, description string) *Task { s.mu.Lock() defer s.mu.Unlock() id := fmt.Sprintf("task_%d", s.nextID) s.nextID++ now := time.Now() task := &Task{ ID: id, Title: title, Description: description, Status: TaskStatusPending, CreatedAt: now, UpdatedAt: now, } s.tasks[id] = task return task } // Get 获取一个任务. func (s *TaskStore) Get(id string) (*Task, bool) { s.mu.RLock() defer s.mu.RUnlock() task, ok := s.tasks[id] return task, ok } // List 列出所有任务. func (s *TaskStore) List() []*Task { s.mu.RLock() defer s.mu.RUnlock() result := make([]*Task, 0, len(s.tasks)) for _, task := range s.tasks { result = append(result, task) } return result } // Update 更新任务状态. func (s *TaskStore) Update(id string, status TaskStatus) (*Task, bool) { s.mu.Lock() defer s.mu.Unlock() task, ok := s.tasks[id] if !ok { return nil, false } task.Status = status task.UpdatedAt = time.Now() return task, true } // ========== TaskCreate 工具 ========== // TaskCreateTool 是创建任务的工具. type TaskCreateTool struct { store *TaskStore } // NewTaskCreateTool 创建一个 TaskCreate 工具实例. func NewTaskCreateTool(store *TaskStore) *TaskCreateTool { return &TaskCreateTool{store: store} } // taskCreateInput 是 TaskCreate 工具的输入参数. type taskCreateInput struct { Title string `json:"title"` Description string `json:"description,omitempty"` } // Name 返回工具名称. func (t *TaskCreateTool) Name() string { return "TaskCreate" } // Description 返回工具描述. func (t *TaskCreateTool) Description(ctx context.Context) string { return "Creates a new task for tracking work progress. " + "Tasks start in 'pending' status and can be updated as work progresses." } // InputSchema 返回工具的 JSON Schema 输入定义. func (t *TaskCreateTool) InputSchema() json.RawMessage { return json.RawMessage(`{ "type": "object", "properties": { "title": { "type": "string", "description": "The title of the task" }, "description": { "type": "string", "description": "A detailed description of the task" } }, "required": ["title"] }`) } // Metadata 返回工具元数据. func (t *TaskCreateTool) Metadata() tools.Metadata { return tools.Metadata{ ConcurrencySafe: true, ReadOnly: false, Destructive: false, SearchHint: "task create new todo add", PermissionClass: permission.PermClassGeneric, AuditOperation: "write", } } // Execute 创建一个新任务. func (t *TaskCreateTool) Execute(ctx context.Context, input json.RawMessage, progress tools.ProgressFunc) (*tools.Result, error) { var params taskCreateInput if err := json.Unmarshal(input, ¶ms); err != nil { return nil, fmt.Errorf("taskcreate: invalid input: %w", err) } if params.Title == "" { return &tools.Result{ Output: "error: title is required", IsError: true, }, nil } task := t.store.Create(params.Title, params.Description) data, _ := json.MarshalIndent(task, "", " ") return &tools.Result{ Output: fmt.Sprintf("Task created:\n%s", string(data)), IsError: false, Data: task, }, nil } // ========== TaskList 工具 ========== // TaskListTool 是列出任务的工具. type TaskListTool struct { store *TaskStore } // NewTaskListTool 创建一个 TaskList 工具实例. func NewTaskListTool(store *TaskStore) *TaskListTool { return &TaskListTool{store: store} } // taskListInput 是 TaskList 工具的输入参数. type taskListInput struct { Status string `json:"status,omitempty"` // 过滤状态 } // Name 返回工具名称. func (t *TaskListTool) Name() string { return "TaskList" } // Description 返回工具描述. func (t *TaskListTool) Description(ctx context.Context) string { return "Lists all tasks. Optionally filter by status (pending, in_progress, done, failed, cancelled)." } // InputSchema 返回工具的 JSON Schema 输入定义. func (t *TaskListTool) InputSchema() json.RawMessage { return json.RawMessage(`{ "type": "object", "properties": { "status": { "type": "string", "description": "Filter tasks by status (pending, in_progress, done, failed, cancelled)", "enum": ["pending", "in_progress", "done", "failed", "cancelled"] } } }`) } // Metadata 返回工具元数据. func (t *TaskListTool) Metadata() tools.Metadata { return tools.Metadata{ ConcurrencySafe: true, ReadOnly: true, Destructive: false, SearchHint: "task list show all todos", PermissionClass: permission.PermClassReadOnly, AuditOperation: "read", } } // Execute 列出所有任务. func (t *TaskListTool) Execute(ctx context.Context, input json.RawMessage, progress tools.ProgressFunc) (*tools.Result, error) { var params taskListInput if input != nil && len(input) > 0 { if err := json.Unmarshal(input, ¶ms); err != nil { return nil, fmt.Errorf("tasklist: invalid input: %w", err) } } allTasks := t.store.List() // 过滤状态 var tasks []*Task if params.Status != "" { validStatuses := map[string]bool{ "pending": true, "in_progress": true, "done": true, "failed": true, "cancelled": true, } if !validStatuses[params.Status] { return &tools.Result{ Output: fmt.Sprintf("error: invalid status '%s'. Valid statuses: pending, in_progress, done, failed, cancelled", params.Status), IsError: true, }, nil } filterStatus := TaskStatus(params.Status) for _, task := range allTasks { if task.Status == filterStatus { tasks = append(tasks, task) } } } else { tasks = allTasks } if len(tasks) == 0 { if params.Status != "" { return &tools.Result{ Output: fmt.Sprintf("No tasks with status '%s'", params.Status), IsError: false, }, nil } return &tools.Result{ Output: "No tasks found", IsError: false, }, nil } // 构建输出 var builder strings.Builder fmt.Fprintf(&builder, "Tasks (%d):\n", len(tasks)) for _, task := range tasks { fmt.Fprintf(&builder, "\n [%s] %s (status: %s)\n", task.ID, task.Title, task.Status) if task.Description != "" { fmt.Fprintf(&builder, " Description: %s\n", task.Description) } fmt.Fprintf(&builder, " Created: %s | Updated: %s\n", task.CreatedAt.Format(time.RFC3339), task.UpdatedAt.Format(time.RFC3339)) } return &tools.Result{ Output: builder.String(), IsError: false, Data: tasks, }, nil } // ========== TaskUpdate 工具 ========== // TaskUpdateTool 是更新任务状态的工具. type TaskUpdateTool struct { store *TaskStore } // NewTaskUpdateTool 创建一个 TaskUpdate 工具实例. func NewTaskUpdateTool(store *TaskStore) *TaskUpdateTool { return &TaskUpdateTool{store: store} } // taskUpdateInput 是 TaskUpdate 工具的输入参数. type taskUpdateInput struct { ID string `json:"id"` Status string `json:"status"` } // Name 返回工具名称. func (t *TaskUpdateTool) Name() string { return "TaskUpdate" } // Description 返回工具描述. func (t *TaskUpdateTool) Description(ctx context.Context) string { return "Updates the status of an existing task. " + "Valid statuses: pending, in_progress, done, failed, cancelled." } // InputSchema 返回工具的 JSON Schema 输入定义. func (t *TaskUpdateTool) InputSchema() json.RawMessage { return json.RawMessage(`{ "type": "object", "properties": { "id": { "type": "string", "description": "The task ID to update" }, "status": { "type": "string", "description": "The new status for the task", "enum": ["pending", "in_progress", "done", "failed", "cancelled"] } }, "required": ["id", "status"] }`) } // Metadata 返回工具元数据. func (t *TaskUpdateTool) Metadata() tools.Metadata { return tools.Metadata{ ConcurrencySafe: true, ReadOnly: false, Destructive: false, SearchHint: "task update status change progress", PermissionClass: permission.PermClassGeneric, AuditOperation: "edit", } } // Execute 更新任务状态. func (t *TaskUpdateTool) Execute(ctx context.Context, input json.RawMessage, progress tools.ProgressFunc) (*tools.Result, error) { var params taskUpdateInput if err := json.Unmarshal(input, ¶ms); err != nil { return nil, fmt.Errorf("taskupdate: invalid input: %w", err) } if params.ID == "" { return &tools.Result{ Output: "error: id is required", IsError: true, }, nil } if params.Status == "" { return &tools.Result{ Output: "error: status is required", IsError: true, }, nil } // 验证状态值 validStatuses := map[string]bool{ "pending": true, "in_progress": true, "done": true, "failed": true, "cancelled": true, } if !validStatuses[params.Status] { return &tools.Result{ Output: fmt.Sprintf("error: invalid status '%s'. Valid statuses: pending, in_progress, done, failed, cancelled", params.Status), IsError: true, }, nil } task, ok := t.store.Update(params.ID, TaskStatus(params.Status)) if !ok { return &tools.Result{ Output: fmt.Sprintf("error: task not found: %s", params.ID), IsError: true, }, nil } data, _ := json.MarshalIndent(task, "", " ") return &tools.Result{ Output: fmt.Sprintf("Task updated:\n%s", string(data)), IsError: false, Data: task, }, nil }