Start a new task from a closed task

This commit is contained in:
Ian Gulliver
2026-10-22 09:05:24 -07:00
parent 88c0199845
commit fedd4ad673
13 changed files with 589 additions and 365 deletions
+64 -2
View File
@@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"log/slog"
"math"
"strings"
"time"
@@ -19,6 +20,13 @@ func (m *Manager) Fork(ctx context.Context, src api.Task, project api.Project, n
return t, err
}
func forkOrigin(t api.Task, from string) string {
if t.ForkedAt == nil {
return "continued from " + from
}
return fmt.Sprintf("forked from %s at snapshot %d", from, *t.ForkedAt)
}
func forkTitle(src api.Task, n int) string {
return fmt.Sprintf("%s (fork of #%d)", src.Title, n)
}
@@ -172,7 +180,7 @@ func (m *Manager) fork(ctx context.Context, src api.Task, project api.Project, n
return api.Task{}, "", fmt.Errorf("%w: snapshot %d of task %s has no recorded agent message at or before it to fork the conversation from", ErrConflict, n, src.Id)
}
t, err := m.createTask(ctx, project, api.Task{
return m.forkSession(ctx, project, api.Task{
Title: title,
MergeTitle: src.MergeTitle,
CreatedBy: creator.Login,
@@ -181,7 +189,61 @@ func (m *Manager) fork(ctx context.Context, src api.Task, project api.Project, n
ForkedAt: &n,
WorkflowStage: src.WorkflowStage,
ParentTaskId: src.ParentTaskId,
}, snap.CommitSha, creator.Login)
}, snap.CommitSha, creator, srcSess, at)
}
func (m *Manager) Continue(ctx context.Context, srcSess api.AgentSession, src api.Task, project api.Project, creator api.User, text, model, sendID, client string, images ...string) (api.Task, error) {
if creator.GitName == "" || creator.GitEmail == "" {
return api.Task{}, ErrNoGitIdentity
}
if src.Status == api.TaskStatusActive {
return api.Task{}, fmt.Errorf("%w: task %s is still active", ErrConflict, src.Id)
}
if srcSess.Role != api.Implementer {
return api.Task{}, fmt.Errorf("%w: session %s is a %s, not a task's agent", ErrConflict, srcSess.Id, srcSess.Role)
}
if srcSess.Vendor == api.Antigravity {
return api.Task{}, fmt.Errorf("%w: session %s ran on Antigravity, and agy can't fork a conversation", ErrConflict, srcSess.Id)
}
unlock := m.lockDelivery("continue:" + srcSess.Id)
defer unlock()
ctx = context.WithoutCancel(ctx)
prior, sent, err := m.priorSend(ctx, sendID)
if err != nil {
return api.Task{}, err
}
if sent {
return m.store.GetTask(ctx, prior.TaskId)
}
events, err := m.store.TaskEvents(ctx, src.Id, 0, -1)
if err != nil {
return api.Task{}, err
}
at := forkPoint(events, srcSess.Id, math.MaxInt64)
if at == "" {
return api.Task{}, fmt.Errorf("%w: session %s has no recorded agent message to continue the conversation from", ErrConflict, srcSess.Id)
}
t, sessionID, err := m.forkSession(ctx, project, api.Task{
Title: src.Title + " (continued)",
MergeTitle: src.MergeTitle,
CreatedBy: creator.Login,
ForkedFrom: &src.Id,
}, project.DefaultBranch, creator, srcSess, at)
if err != nil {
return api.Task{}, err
}
_, _, err = m.Message(ctx, sessionID, creator.Login, text, model, sendID, client, images...)
if err != nil {
return api.Task{}, fmt.Errorf("task %s created, but its agent did not start: %w", t.Id, err)
}
return t, nil
}
func (m *Manager) forkSession(ctx context.Context, project api.Project, task api.Task, base string, creator api.User, srcSess api.AgentSession, at string) (api.Task, string, error) {
t, err := m.createTask(ctx, project, task, base, creator.Login)
if err != nil {
return api.Task{}, "", err
}