130 lines
3.8 KiB
Go
130 lines
3.8 KiB
Go
package agents
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"g.fc.run/misc/byre/internal/api"
|
|
"g.fc.run/misc/byre/internal/git"
|
|
"g.fc.run/misc/byre/internal/mcp"
|
|
"g.fc.run/misc/byre/internal/store"
|
|
)
|
|
|
|
func (m *Manager) CreateTask(ctx context.Context, project api.Project, title, createdBy, base string) (api.Task, error) {
|
|
id := store.NewID()
|
|
t := api.Task{
|
|
Id: id,
|
|
ProjectId: project.Id,
|
|
Title: title,
|
|
MergeTitle: title,
|
|
Branch: "byre/" + id,
|
|
WorktreePath: filepath.Join(m.worktreeRoot, project.Id, id),
|
|
Status: api.TaskStatusActive,
|
|
CreatedBy: createdBy,
|
|
CreatedAt: time.Now().UTC(),
|
|
}
|
|
|
|
var err error
|
|
t.BaseRef, err = git.AddWorktree(project.RepoPath, t.WorktreePath, t.Branch, base)
|
|
if err != nil {
|
|
return api.Task{}, err
|
|
}
|
|
|
|
err = m.store.CreateTask(ctx, t)
|
|
if err != nil {
|
|
return api.Task{}, err
|
|
}
|
|
return t, nil
|
|
}
|
|
|
|
type createTaskArgs struct {
|
|
Title string `json:"title"`
|
|
Prompt string `json:"prompt"`
|
|
Base string `json:"base"`
|
|
Model string `json:"model"`
|
|
}
|
|
|
|
func (m *Manager) createTaskTool(l *live) mcp.Tool {
|
|
return mcp.Tool{
|
|
Name: "create_task",
|
|
Description: "Create a new byre task in this project, with its own branch and worktree, and start an agent on it with prompt as its first message. " +
|
|
"Use it to run independent pieces of work in parallel. The new agent sees nothing of your context, so the prompt must be a complete brief. " +
|
|
"base picks the starting commit: default_branch starts from the project's default branch; this_task starts from this task's HEAD, " +
|
|
"which includes your committed work but not uncommitted edits (byre commits at the end of each turn). " +
|
|
"Returns at once with the new task's id; the new agent runs on its own.",
|
|
InputSchema: json.RawMessage(`{
|
|
"type": "object",
|
|
"properties": {
|
|
"title": {"type": "string", "description": "Short task title, shown in the task list."},
|
|
"prompt": {"type": "string", "description": "The new agent's first message: a complete, self-contained brief."},
|
|
"base": {"type": "string", "enum": ["default_branch", "this_task"]},
|
|
"model": {"type": "string", "description": "Model for the new agent. Defaults to this session's model."}
|
|
},
|
|
"required": ["title", "prompt", "base"]
|
|
}`),
|
|
Handler: func(ctx context.Context, raw json.RawMessage) (string, error) {
|
|
return m.createTaskFromAgent(ctx, l, raw)
|
|
},
|
|
}
|
|
}
|
|
|
|
func (m *Manager) createTaskFromAgent(ctx context.Context, l *live, raw json.RawMessage) (string, error) {
|
|
args := createTaskArgs{}
|
|
err := json.Unmarshal(raw, &args)
|
|
if err != nil {
|
|
return "", fmt.Errorf("malformed create_task call: %w", err)
|
|
}
|
|
|
|
if strings.TrimSpace(args.Title) == "" || strings.TrimSpace(args.Prompt) == "" {
|
|
return "", errors.New("title and prompt are required")
|
|
}
|
|
|
|
sess, err := m.store.GetSession(ctx, l.id)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
project, err := m.store.GetProject(ctx, l.projectID)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
creator, err := m.store.GetUser(ctx, sess.CreatedBy)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
base := ""
|
|
switch args.Base {
|
|
case "default_branch":
|
|
base = project.DefaultBranch
|
|
case "this_task":
|
|
base, err = git.Head(l.dir)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
default:
|
|
return "", fmt.Errorf("base must be default_branch or this_task, not %q", args.Base)
|
|
}
|
|
|
|
model := args.Model
|
|
if model == "" {
|
|
model = sess.Model
|
|
}
|
|
|
|
task, err := m.CreateTask(ctx, project, args.Title, creator.Login, base)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
_, err = m.create(ctx, task, creator, args.Prompt, model, agentActor, nil)
|
|
if err != nil {
|
|
return "", fmt.Errorf("task %s created, but its agent did not start: %w", task.Id, err)
|
|
}
|
|
|
|
return fmt.Sprintf("Task %s %q created from %s and its agent started.", task.Id, task.Title, task.BaseRef), nil
|
|
}
|