You've already forked agentic-coding-workflow
bugfix
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
// runner.go 是回合驱动器:给定一个 Step,要么经 acp.SessionService.Create 以特权
|
||||
// owner 身份创建新 session,要么在崩溃 session 的同一 worktree 上 ResumeSession,然后
|
||||
// 经 acp.TurnRunner.SendPromptAndWait 阻塞跑完一个回合。封装重试/恢复策略。
|
||||
package orchestrator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yan1h/agent-coding-workflow/internal/acp"
|
||||
"github.com/yan1h/agent-coding-workflow/internal/infra/errs"
|
||||
)
|
||||
|
||||
// ACPSessions 是 runner 创建 session 所需的窄接口(acp.SessionService 满足)。
|
||||
type ACPSessions interface {
|
||||
Create(ctx context.Context, c acp.Caller, in acp.CreateSessionInput) (*acp.Session, error)
|
||||
}
|
||||
|
||||
// ACPSessionLookup 是 runner 查找 step 已有 session 所需的窄接口(acp.Repository 满足)。
|
||||
type ACPSessionLookup interface {
|
||||
GetSessionByStepID(ctx context.Context, stepID uuid.UUID) (*acp.Session, error)
|
||||
GetCrashedSessionForResume(ctx context.Context, stepID uuid.UUID) (*acp.Session, error)
|
||||
}
|
||||
|
||||
// turnResult 是一次回合驱动的结果。
|
||||
type turnResult struct {
|
||||
SessionID uuid.UUID
|
||||
StopReason string
|
||||
}
|
||||
|
||||
// runner 封装 spawn-or-resume + SendPromptAndWait。
|
||||
type runner struct {
|
||||
sessions ACPSessions
|
||||
turns acp.TurnRunner
|
||||
lookup ACPSessionLookup
|
||||
turnTimeout time.Duration
|
||||
}
|
||||
|
||||
// NewRunner 构造回合驱动器(由 app.go 装配 StepHandler 时调用)。turnTimeout<=0 时
|
||||
// 退回 10 分钟默认值。
|
||||
func NewRunner(sessions ACPSessions, turns acp.TurnRunner, lookup ACPSessionLookup, turnTimeout time.Duration) *runner {
|
||||
if turnTimeout <= 0 {
|
||||
turnTimeout = 10 * time.Minute
|
||||
}
|
||||
return &runner{sessions: sessions, turns: turns, lookup: lookup, turnTimeout: turnTimeout}
|
||||
}
|
||||
|
||||
// driveTurn 为 step 驱动一个回合:
|
||||
//
|
||||
// 1. 若该 step 已有崩溃/退出 session → ResumeSession(同 worktree)。
|
||||
// 2. 否则若该 step 已有活跃 session → 复用(幂等:避免重试重复 spawn)。
|
||||
// 3. 否则 → Create 一个新 session(特权 owner 身份;OrchestratorStepID 反向关联)。
|
||||
//
|
||||
// requirementNumber 用于把新 session 绑定到 req/<slug>-N worktree,使同一 run 的各
|
||||
// 阶段 step 共享同一 worktree(resume 也据此回到原 worktree)。
|
||||
//
|
||||
// 然后 SendPromptAndWait(受 turnTimeout 约束)。返回 sessionID + 原始 stopReason。
|
||||
func (r *runner) driveTurn(ctx context.Context, run *Run, step *Step, requirementNumber int) (turnResult, error) {
|
||||
owner := acp.Caller{UserID: run.OwnerID, IsAdmin: false}
|
||||
|
||||
var sessionID uuid.UUID
|
||||
|
||||
// 1. 崩溃 session → resume(同一 worktree)。
|
||||
if crashed, err := r.lookup.GetCrashedSessionForResume(ctx, step.ID); err == nil && crashed != nil {
|
||||
resumed, rerr := r.turns.ResumeSession(ctx, owner, crashed.ID)
|
||||
if rerr != nil {
|
||||
return turnResult{}, rerr
|
||||
}
|
||||
sessionID = resumed.ID
|
||||
} else if live, lerr := r.lookup.GetSessionByStepID(ctx, step.ID); lerr == nil && live != nil && live.Status.IsActive() {
|
||||
// 2. 已有活跃 session → 复用。
|
||||
sessionID = live.ID
|
||||
} else {
|
||||
// 3. 创建新 session(特权 owner;反向关联到 step)。
|
||||
stepID := step.ID
|
||||
in := acp.CreateSessionInput{
|
||||
WorkspaceID: run.WorkspaceID,
|
||||
AgentKindID: step.AgentKindID,
|
||||
InitialPrompt: nil, // 由 SendPromptAndWait 单独发送,确保阻塞等待回合
|
||||
OrchestratorStepID: &stepID,
|
||||
}
|
||||
if requirementNumber > 0 {
|
||||
// 绑定到 req 分支 worktree,使同一 run 的各阶段共享同一 worktree。
|
||||
rn := requirementNumber
|
||||
in.RequirementNumber = &rn
|
||||
}
|
||||
created, cerr := r.sessions.Create(ctx, owner, in)
|
||||
if cerr != nil {
|
||||
return turnResult{}, cerr
|
||||
}
|
||||
sessionID = created.ID
|
||||
}
|
||||
|
||||
if sessionID == uuid.Nil {
|
||||
return turnResult{}, errs.New(errs.CodeInternal, "orchestrator: no session id after spawn/resume")
|
||||
}
|
||||
|
||||
turnCtx, cancel := context.WithTimeout(ctx, r.turnTimeout)
|
||||
defer cancel()
|
||||
stop, err := r.turns.SendPromptAndWait(turnCtx, sessionID, step.Prompt)
|
||||
if err != nil {
|
||||
return turnResult{SessionID: sessionID}, err
|
||||
}
|
||||
return turnResult{SessionID: sessionID, StopReason: stop}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user