4b8f9aa39b
Build & push image / build-and-push (push) Successful in 33s
Two Phase-2 swarm upgrades: - auto.go: GADFLY_SPECIALISTS=auto routes the review — a selector model (GADFLY_SELECTOR_MODEL, else the review model) reads the changed files + PR description and picks the smallest relevant lens set from the catalog, and may propose ad-hoc lenses for gaps (e.g. migrations). Structured output via majordomo.Generate[T]; capped + de-duped; falls back to the default suite. - delegate.go: GADFLY_WORKER_MODEL adds a delegate_investigation tool so the reviewer offloads mechanical legwork (trace callers, gather usages) to a cheap worker sub-agent that returns an evidence-cited digest — the top model reasons over summaries, not raw file dumps. Workers get an fs-only toolbox (no sub-delegation). Unset = off. resolveSpecialists now also returns the registry + an auto flag. Docs (README Specialists + config table, CLAUDE.md, main.go header) + tests updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
66 lines
2.6 KiB
Go
66 lines
2.6 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"gitea.stevedudenhoeffer.com/steve/majordomo/agent"
|
|
llm "gitea.stevedudenhoeffer.com/steve/majordomo/llm"
|
|
)
|
|
|
|
// defaultWorkerMaxSteps bounds a delegated worker run.
|
|
const defaultWorkerMaxSteps = 8
|
|
|
|
// workerSystemPrompt drives a delegated investigation sub-agent. It does the
|
|
// grunt work (grep/read) and returns a tight, evidence-cited digest so the
|
|
// top reviewer reasons over a summary instead of raw file dumps.
|
|
const workerSystemPrompt = "You are a fast code investigator. Use the read-only tools " +
|
|
"(read_file, grep, find_files, list_dir, get_diff) to answer the SPECIFIC question you are " +
|
|
"given about this repository — nothing more. Be precise and concise: report concrete findings " +
|
|
"with `path:line` evidence, quote only the few lines that matter, and state plainly when " +
|
|
"something is absent. Do NOT speculate or pad. When you have the answer, stop and report it."
|
|
|
|
type delegateArgs struct {
|
|
Task string `json:"task" description:"A specific investigation to carry out by reading/grepping the repo, e.g. 'find every caller of parseAmount and list which ones don't check the error'. Ask for a concrete answer, not an opinion."`
|
|
}
|
|
|
|
// delegateTool lets the reviewer offload mechanical investigation to a (cheaper)
|
|
// worker model. Present only when GADFLY_WORKER_MODEL is configured.
|
|
func (r *repoFS) delegateTool() llm.Tool {
|
|
return llm.DefineTool[delegateArgs](
|
|
"delegate_investigation",
|
|
"Delegate focused legwork to a fast worker agent that reads/greps this repo and reports back "+
|
|
"a concise, evidence-cited digest. Use it to offload mechanical investigation (tracing all "+
|
|
"callers, gathering every usage of a symbol, checking a pattern across many files) so you can "+
|
|
"reason over the summary instead of pulling raw files into your own context.",
|
|
func(ctx context.Context, args delegateArgs) (any, error) {
|
|
task := strings.TrimSpace(args.Task)
|
|
if task == "" {
|
|
return nil, fmt.Errorf("task is required")
|
|
}
|
|
box, err := r.workerToolbox()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
w := agent.New(r.worker, workerSystemPrompt,
|
|
agent.WithToolbox(box),
|
|
agent.WithMaxSteps(envInt("GADFLY_WORKER_MAX_STEPS", defaultWorkerMaxSteps)),
|
|
agent.WithToolErrorLimits(3, 3),
|
|
)
|
|
res, runErr := w.Run(ctx, task)
|
|
out := ""
|
|
if res != nil {
|
|
out = strings.TrimSpace(res.Output)
|
|
}
|
|
if out == "" {
|
|
if runErr != nil {
|
|
return nil, fmt.Errorf("worker investigation failed: %w", runErr)
|
|
}
|
|
return "worker returned no findings", nil
|
|
}
|
|
return out, nil
|
|
},
|
|
)
|
|
}
|