rony-llm-agent/pkg/agent
Victor Vargas 49353485e5 feat(agent): thread the new turn through Loop as llm.Message
Run/RunStream took the new turn as a bare string, which had nowhere
to carry ContentPart attachments. Both now take an llm.Message
(Role is forced to RoleUser regardless of what the caller sets), so a
caller building a multimodal turn just fills in Content/Parts on it
instead of the loop needing a second, parallel parameter.
subagent.go and every test call site are updated to wrap their string
prompt as llm.Message{Role: llm.RoleUser, Content: ...} — SubAgent.Run
itself is untouched, it still takes a plain task string.
2026-07-16 22:24:03 -07:00
..
integration_test.go feat(agent): thread the new turn through Loop as llm.Message 2026-07-16 22:24:03 -07:00
loop.go feat(agent): thread the new turn through Loop as llm.Message 2026-07-16 22:24:03 -07:00
loop_test.go feat(agent): thread the new turn through Loop as llm.Message 2026-07-16 22:24:03 -07:00
README.es.md feat: add history messages, reasoning content, and adaptive language support 2026-07-05 16:17:37 -07:00
README.md feat: add history messages, reasoning content, and adaptive language support 2026-07-05 16:17:37 -07:00
subagent.go feat(agent): thread the new turn through Loop as llm.Message 2026-07-16 22:24:03 -07:00
subagent_test.go feat(agent): add SubAgent runtime for nested, specialized agent loops 2026-07-09 12:08:32 -07:00
thinking_budget_test.go feat(agent): thread the new turn through Loop as llm.Message 2026-07-16 22:24:03 -07:00
unparsed_toolcall_test.go feat(agent): thread the new turn through Loop as llm.Message 2026-07-16 22:24:03 -07:00

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 (Ask permission) require confirmation

See also