Export persona.DiscoverAgentsMD and add agent.Config.AgentsMD so project and global AGENTS.md rules actually reach the model. Previously buildInitialMessages always called AssembleSystemPrompt with an empty string, so no AGENTS.md content was ever injected despite the discovery logic already existing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| integration_test.go | ||
| loop.go | ||
| loop_test.go | ||
| README.es.md | ||
| README.md | ||
pkg/agent
The main loop that runs an LLM agent with guardrails.
Responsibility
Coordinate the iterative cycle between the LLM and tool execution:
while iteration < MaxIterations:
response = llm.Generate(messages, tools)
if no tool calls: return response
for tool_call in response.ToolCalls:
if needs_approval: ask_user()
result = execute(tool_call)
append tool result to messages
Public API
type Loop interface {
Run(ctx context.Context, input string, history ...llm.Message) (Response, error)
RunStream(ctx context.Context, input string, history ...llm.Message) iter.Seq2[Chunk, error]
}
type Config struct {
LLM llm.LLMClient
Persona persona.Persona
Tools tools.Registry
Sandbox Sandbox
MaxIters int
Approver Approver // nil = auto-approve all
OnIteration func(Iteration) // observability hook
}
type Response struct {
Content string
ToolCalls []tools.Call
Iterations int
Duration time.Duration
TokenUsage llm.TokenUsage
}
Guarantees
- Termination: Always terminates (max iterations, error, or final response)
- Idempotency: Re-running with the same input produces the same output (given the same LLM)
- Observability: Each iteration emits an OpenTelemetry span
- Approval: Destructive tools (
Askpermission) require confirmation
See also
- pkg/tools — Tool execution
- pkg/llm — LLMClient interface
- pkg/persona — Persona assembly