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).
5.3 KiB
AGENTS.md — Working in rony-llm-agent
Repo status: Go source code exists. The library is implemented across pkg/agent, pkg/config, pkg/llm, pkg/persona, pkg/rag, and pkg/tools. All public interfaces are defined, core adapters for OpenAI, llama.cpp, ChromaDB, and Ollama embeddings are in place. Tests exist for the agent loop, sandbox, config loader, persona loader, RAG memory, tool registry, and LLM clients.
What to read first
| If you need... | Read... |
|---|---|
| Package layout and entrypoints | docs/architecture.md |
| Per-package boundaries | docs/components.md |
| Features not yet planned | docs/phase2.md — Phase 2 backlog |
| A specific package's API | pkg/<name>/README.md — mirrors docs; check code for actual signatures |
These three docs are the source of truth. Read them before writing any code. The pkg/*/README.md files mirror content from these docs; they're convenient but architecture.md is canonical.
How to build and test
# Requires Go 1.26+ — required for os.Root, iter.Seq2, unique.Handle
go mod tidy # resolves imports, creates go.sum
go test ./... # all packages
go test -race ./... # race detector (always use in CI once implemented)
Run golangci-lint if installed. No Makefile configured yet.
Key constraints to never break
Go version
Requires Go 1.26+. The module declares it in go.mod. Using older Go will not compile because of os.Root, iter.Seq2, unique.Handle.
Hexagonal architecture — package boundaries
- Interfaces (ports) live at the top level of each
pkg/<name>/directory. These define the public API. - Implementations (adapters) go in
pkg/<name>/internal/or subdirectories likepkg/llm/providers/openai/,pkg/rag/backends/chroma/. - Package names are lowercase, singular (
agent,tools,llm,rag,persona). - No external dependencies in domain packages — only stdlib and SDKs of providers (in adapters).
Module path
github.com/VictorVargas/rony-llm-agent — imported as-is by downstream products.
Streaming uses iter.Seq2
The library is designed around Go 1.23+'s iter.Seq2[T, error] for streaming LLM output. When implementing or reading code, this is the primary stream pattern.
Sandbox uses os.Root (Go 1.24+)
Filesystem sandboxing must use os.Root, not path string prefix checks. This is non-negotiable for security — naive prefix checks can't handle symlinks, TOCTOU, or path encoding attacks.
Testing conventions
- Use
pkg/llm/mock.MockLLMClient(or its constructorsNew(),NewWithGenerate(),NewWithStream(),NewWithMatch()) for deterministic tests that don't call real APIs. - Use
pkg/rag/embeddings/mock.MockEmbedderfor embedding tests. - All public API must be concurrent-safe (documented in architecture.md).
- Every function that can block takes
ctx context.Contextas the first parameter.
Current test coverage
| Package | Test file | Purpose |
|---|---|---|
pkg/agent |
loop_test.go, integration_test.go |
Agent loop iterations, tool execution, streaming |
pkg/config |
config_test.go |
YAML loading, defaults, precedence |
pkg/llm/providers/openai |
client_test.go |
OpenAI HTTP client |
pkg/llm/providers/llamacpp |
client_test.go |
llama.cpp server adapter |
pkg/persona |
persona_test.go |
Persona loading, system prompt assembly |
pkg/rag |
memory_test.go |
Add/search/forget operations |
pkg/tools |
registry_test.go, sandbox/sandbox_test.go |
Tool registration, sandbox validation |
pkg/rag/backends/chroma |
chroma_test.go |
ChromaDB upsert and search |
Code style conventions
- Interfaces end with capability names:
LLMClient,Loop,Embedder,Memory. - Sentinel errors prefixed with
Err:ErrToolNotFound,ErrSandboxViolation,ErrPersonaNotFound,ErrConfigNotFound. - Constructors use
Newfor the primary andNewXxxfor variants. - Error wrapping uses
%w, never lossy formatting.
Relationship to downstream products
This library is consumed by:
- rony-harness — TUI agent for software development (the reference implementation that triggered this extraction)
- rony-chat-bot — HTTP chatbot for portfolios/websites
If you're unsure about behavior, check what harness or chat-bot does first. They are the real-world consumers driving design decisions.
Agent skills
Reusable skills for any AI agent live in .agents/skills/<name>/SKILL.md — this is the canonical, tool-agnostic location read by OpenCode, Claude Code, Cursor, and other modern agents. Each skill's SKILL.md starts with a YAML frontmatter (name, description) followed by instructions. Do not mirror skills into .opencode/skills/ — opencode reads .agents/skills/ natively.
Phase 2 awareness
Phase 2 is now in progress (started 2026-07-09). Sub-agents (docs/phase2.md §5) landed first: pkg/agent.SubAgent/SubAgentRegistry (pkg/agent/subagent.go) let a caller run a specialized, nested agent.Loop and get its final response back — the harness uses this for its delegate tool (builder/planner). MCP server/client, full RAG pipeline with Qdrant/sqlite-vec backends, skills system, and observability remain unimplemented; don't start those unless explicitly asked. Reference docs/phase2.md for spec when needed.