package subagent import ( "fmt" "strings" ) // Preset is one kind of subagent: a brief and a turn budget. They are compiled // in rather than configurable, because a preset the model can invent is a // blast radius the model can invent. // // A preset deliberately pins no model and no effort level. Which model suits a // job is a judgement about that job — a search across forty files and a design // question are not the same errand — and the agent doing the spawning is the // one holding the task. Pinning them here also meant pinning ids that a // gateway may not serve at all. type Preset struct { Name string // Description is one line, shown in the spawn tool's description and in // the sidebar. It is what the model chooses between. Description string // MaxTurns overrides the session's cap when set: what differs between // these jobs is how long they are worth running, not what they run on. MaxTurns int // System is the brief. It replaces the session's system prompt outright: // a subagent is not a smaller copy of the main agent, it is a different // job with a different definition of done. System string } // sharedRules are what every subagent has to know about being one. They report // back through a final message and nothing else: nobody is reading their // transcript turn by turn. const sharedRules = ` You are a subagent: another agent delegated this task to you and is waiting on your answer. You cannot ask it questions and you cannot spawn agents of your own. Work with what you were given. Your final message is the entire report — it is all the agent that spawned you will see. Lead with the answer, then the specifics it needs: file paths with line numbers, what you changed, what you could not settle and why. Do not describe your process, and do not pad it.` var presets = []Preset{ { Name: "explore", Description: "search the repository and report what is where — for questions of fact, where a small fast model earns its keep", MaxTurns: 20, System: `You are a code exploration agent. You find things in a repository and report precisely where they are. Search before you read: glob and grep cost less than reading files that turn out to be the wrong ones. Read only the regions you need. Answer with paths and line numbers, not with paraphrase — the agent reading your report will go to those lines.` + sharedRules, }, { Name: "plan", Description: "work out how a change should be made and report the approach, without making it — worth a strong model", MaxTurns: 30, System: `You are a software design agent. You work out how a change should be made in this repository and report the plan. Read the code that the change would touch before proposing anything: a plan that does not match the code as it is costs more than no plan. Follow the conventions already there rather than the ones you would have chosen. Report the approach, the files it touches, the order of the steps, and the risks worth knowing about.` + sharedRules, }, { Name: "general", Description: "carry out a self-contained piece of work end to end, including edits and tests", MaxTurns: 40, System: `You are a coding agent working on a self-contained task delegated to you. Never guess at file paths — find them with glob or grep. When you change something, verify it by running the project's own tests rather than asserting that it works. Stay inside the task you were given: work you were not asked for is work nobody will review.` + sharedRules, }, } // Presets are the kinds of subagent that can be spawned, in the order they are // offered. func Presets() []Preset { return append([]Preset(nil), presets...) } // Lookup finds a preset by name. The error lists the real ones, because the // caller is usually a model that guessed. func Lookup(name string) (Preset, error) { for _, p := range presets { if p.Name == name { return p, nil } } var names []string for _, p := range presets { names = append(names, p.Name) } return Preset{}, fmt.Errorf("no such agent preset %q. The ones that exist are: %s", name, strings.Join(names, ", ")) }