rony-llm-agent/pkg/agent/subagent.go
Victor Vargas 744bb00f88 feat(agent): add SubAgent runtime for nested, specialized agent loops
Implements docs/phase2.md §5 (Sub-agents), pulled forward from the
harness's item 2 work: SubAgent/SubAgentRegistry let a caller run a
nested agent.Loop with its own persona/tools/iteration cap and get its
final response back. Run doesn't set Approver/Sandbox, so a single Ask
approval on the caller's own delegating tool covers the whole nested
run (Ask-permission tools execute unprompted when Config.Approver is
nil). rony-harness consumes this for its delegate tool (builder/planner).
2026-07-09 12:08:32 -07:00

46 lines
1.4 KiB
Go

package agent
import (
"context"
"github.com/VictorVargas/rony-llm-agent/pkg/llm"
"github.com/VictorVargas/rony-llm-agent/pkg/persona"
"github.com/VictorVargas/rony-llm-agent/pkg/tools"
)
// SubAgent describes a specialized agent invocable via a delegate tool (see
// docs/phase2.md §5). The caller (the harness) is responsible for building
// Persona and Tools — the boundary is the same as for the main Loop: this
// package orchestrates, it doesn't decide personas or wire concrete tools.
type SubAgent struct {
Name string
Description string
Persona persona.Persona
Tools tools.Registry
MaxIterations int
}
// Run executes the sub-agent's task to completion using llmClient and the
// given AGENTS.md content, and returns its final response.
func (s SubAgent) Run(ctx context.Context, llmClient llm.LLMClient, agentsMD string, task string) (Response, error) {
cfg := Config{
LLM: llmClient,
Persona: s.Persona,
Tools: s.Tools,
MaxIters: s.MaxIterations,
AgentsMD: agentsMD,
}
if cfg.MaxIters == 0 {
cfg.MaxIters = DefaultMaxIterations
}
return New(cfg).Run(ctx, task)
}
// SubAgentRegistry looks up SubAgents by name for the delegate tool.
type SubAgentRegistry map[string]SubAgent
// Get returns the named sub-agent, if registered.
func (r SubAgentRegistry) Get(name string) (SubAgent, bool) {
s, ok := r[name]
return s, ok
}