Why: Simple tasks should finish as one assistant step, while complex tasks stay in leader mode, and every kernel step needs the host's tools and events. Evidence: go test ./squad/ -count=1 passed, including direct route, leader entry, skill override, and delegation score fields. Impact: Hosts can inject HostTools and OnEvent; a Router can choose direct or plan without changing product roles. Refs: deliverable-quality-system B2 Co-authored-by: Cursor <cursoragent@cursor.com>
77 lines
1.9 KiB
Go
77 lines
1.9 KiB
Go
package squad
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
)
|
|
|
|
// RouteMode 是分诊结果。direct 不进入多角色流程;plan 进入 leader 委派。
|
|
type RouteMode string
|
|
|
|
const (
|
|
RouteDirect RouteMode = "direct"
|
|
RoutePlan RouteMode = "plan"
|
|
)
|
|
|
|
// RouteDecision 描述这一次目标怎么跑,以及它属于哪一族交付物。
|
|
type RouteDecision struct {
|
|
Mode RouteMode
|
|
Family string
|
|
Reason string
|
|
}
|
|
|
|
// Router 在规划之前做一次分诊。nil 决策表示不干预。
|
|
type Router interface {
|
|
Route(ctx context.Context, goal Goal) (*RouteDecision, error)
|
|
}
|
|
|
|
// applyRoute 把分诊写进运行元数据。显式 RunOptions.Mode 优先,分诊不覆盖。
|
|
func applyRoute(meta *RunMetadata, dec *RouteDecision, explicitMode string) {
|
|
if dec == nil || explicitMode != "" {
|
|
return
|
|
}
|
|
if meta.Metadata == nil {
|
|
meta.Metadata = map[string]string{}
|
|
}
|
|
meta.Metadata["route"] = string(dec.Mode)
|
|
if dec.Family != "" {
|
|
meta.Metadata["deliverable_family"] = dec.Family
|
|
}
|
|
if dec.Reason != "" {
|
|
meta.Metadata["route_reason"] = dec.Reason
|
|
}
|
|
switch dec.Mode {
|
|
case RouteDirect:
|
|
meta.Metadata["route_direct"] = "1"
|
|
case RoutePlan:
|
|
meta.Metadata["mode"] = "leader"
|
|
if meta.Metadata["leader_role"] == "" {
|
|
meta.Metadata["leader_role"] = "leader"
|
|
}
|
|
}
|
|
}
|
|
|
|
// directAssistantPlan 生成只有 assistant 一步的计划。
|
|
func directAssistantPlan(reg *RoleRegistry, goal Goal) (*SquadPlan, error) {
|
|
spec, ok := reg.Get("assistant")
|
|
if !ok {
|
|
return nil, fmt.Errorf("direct route requires a registered assistant role")
|
|
}
|
|
return &SquadPlan{
|
|
Squad: SquadConfig{Agents: []RoleSpec{spec}},
|
|
Workflow: WorkflowPlan{
|
|
WorkflowID: NewPlanID(),
|
|
Title: goal.Text,
|
|
Status: "pending",
|
|
Steps: []StepSpec{{
|
|
ID: "assistant",
|
|
Role: "assistant",
|
|
Title: goal.Text,
|
|
Targets: []string{"output.md"},
|
|
SubWorkspace: "tasks/01_assistant",
|
|
Status: "pending",
|
|
}},
|
|
},
|
|
}, nil
|
|
}
|