Why: the squad core has to compile without agent-native or agent-intent. Ports stay in the core; adapters and the CLI own the concrete clients. Evidence: GOWORK=off go test -count=1 ./squad/ ./store/ ./textextract/ ./schema/ passed, and the intent, sqlite, native, and cmd modules passed their own go test. Impact: core go.mod keeps error-code-system only. Nested gitlinks stay. Callers register the native backend through agent-squad/adapters/native. Refs: D1 Co-authored-by: Cursor <cursoragent@cursor.com>
116 lines
3.7 KiB
Go
116 lines
3.7 KiB
Go
package squadintent
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
|
|
"agent-squad/squad"
|
|
)
|
|
|
|
// Catalog 把识别快照渲染为 squad 的能力目录数据视图(规划者提示词 + 计划校验用)。
|
|
// 闸门未启用(nil Gate)时返回 nil。
|
|
func (g *Gate) Catalog() *squad.IntentCatalog {
|
|
snap := g.Snapshot()
|
|
if g == nil || snap == nil {
|
|
return nil
|
|
}
|
|
catalog := &squad.IntentCatalog{
|
|
Revision: snap.Revision(),
|
|
Markdown: snap.CatalogMarkdown(),
|
|
Specs: make(map[string]squad.IntentSpecView, len(snap.SpecIDs())),
|
|
}
|
|
for _, cs := range snap.Catalog() {
|
|
view := squad.IntentSpecView{
|
|
ID: cs.ID,
|
|
Description: cs.Description,
|
|
Risk: cs.Risk,
|
|
RequiresConfirm: cs.RequiresConfirm,
|
|
Handler: cs.Handler,
|
|
Metadata: cs.Metadata,
|
|
}
|
|
for _, sl := range cs.Slots {
|
|
view.Slots = append(view.Slots, squad.IntentSlotView{
|
|
Key: sl.Key, Required: sl.Required, Label: sl.Label,
|
|
Question: sl.Question, Enum: sl.Enum, Format: sl.Format,
|
|
})
|
|
if sl.Required {
|
|
view.RequiredSlots = append(view.RequiredSlots, sl.Key)
|
|
}
|
|
}
|
|
catalog.Specs[cs.ID] = view
|
|
}
|
|
return catalog
|
|
}
|
|
|
|
// IntentHandler 宿主注册的系统功能处理函数:返回写入步骤 targets 的正文。
|
|
type IntentHandler func(ctx context.Context, step squad.IntentStep) (string, error)
|
|
|
|
// Runner 系统功能执行器:按 spec.Handler 查宿主处理函数并调用;
|
|
// echo=true 时未注册的 handler 以物化文本回退(开发/演示用,正文明确声明未真实执行)。
|
|
type Runner struct {
|
|
gate *Gate
|
|
handlers map[string]IntentHandler
|
|
echo bool
|
|
}
|
|
|
|
// Runner 基于闸门快照构建执行器。handlers 以 spec.Handler 为键。
|
|
func (g *Gate) Runner(handlers map[string]IntentHandler) *Runner {
|
|
return &Runner{gate: g, handlers: handlers}
|
|
}
|
|
|
|
// EchoRunner 演示执行器:所有调用物化为文本(AGENT_SQUAD_INTENT_ECHO 场景)。
|
|
func (g *Gate) EchoRunner() *Runner {
|
|
return &Runner{gate: g, echo: true}
|
|
}
|
|
|
|
// Run 实现 squad.IntentRunner。未知意图/未知 handler fail-closed(不静默降级)。
|
|
func (r *Runner) Run(ctx context.Context, step squad.IntentStep) (*squad.ExecResult, error) {
|
|
if r == nil || r.gate == nil || r.gate.Snapshot() == nil {
|
|
return nil, fmt.Errorf("intent runner: gate not configured")
|
|
}
|
|
spec, ok := r.gate.Snapshot().SpecByID(step.IntentID)
|
|
if !ok {
|
|
return nil, fmt.Errorf("intent runner: snapshot 中不存在意图 %s", step.IntentID)
|
|
}
|
|
handlerKey := spec.Handler
|
|
h, registered := r.handlers[handlerKey]
|
|
if !registered {
|
|
if !r.echo {
|
|
return nil, fmt.Errorf("intent runner: handler %q 未注册(fail-closed,不静默降级)", handlerKey)
|
|
}
|
|
text := echoText(step, handlerKey, false)
|
|
return textResult(step, text), nil
|
|
}
|
|
text, err := h(ctx, step)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return textResult(step, text), nil
|
|
}
|
|
|
|
func textResult(step squad.IntentStep, text string) *squad.ExecResult {
|
|
patches := make(map[string][]byte, len(step.Targets))
|
|
for _, t := range step.Targets {
|
|
patches[t] = []byte(text)
|
|
}
|
|
return &squad.ExecResult{Success: true, Patches: patches, Logs: "intent:" + step.IntentID}
|
|
}
|
|
|
|
func echoText(step squad.IntentStep, handlerKey string, _ bool) string {
|
|
var b strings.Builder
|
|
b.WriteString("# 系统功能调用(EchoRunner 物化,未真实执行)\n\n")
|
|
fmt.Fprintf(&b, "- intent_id: %s\n- handler: %s\n- title: %s\n", step.IntentID, handlerKey, step.Title)
|
|
keys := make([]string, 0, len(step.Slots))
|
|
for k := range step.Slots {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys)
|
|
for _, k := range keys {
|
|
fmt.Fprintf(&b, "- slot %s = %s\n", k, step.Slots[k])
|
|
}
|
|
b.WriteString("\n宿主应用注册真实 handler 后,此文件将是系统功能的真实输出。\n")
|
|
return b.String()
|
|
}
|