15 KiB
AGENTS.md
Project-level context for AI agents working in this repository.
⚠️ IMPORTANT: All work must be done in THIS source directory (
~/src/ollie/). Never edit files under~/.config/ollie/— that is an install target. Changes made there are overwritten on the nextmake install-data. Edit source files here, then runmaketo build and install.
Project Overview
Ollie is an AI agent runtime inspired by Plan 9: agent state and behaviors are exposed as files in a 9P namespace. Orchestration, scheduling, and UIs are external — shell scripts, editors, web apps. The core is minimal; capabilities come from composing scripts.
Repository Layout
Single Go module. KDE integration is part of the repository under kde/.
ollie/ ← you are here
├── cmd/
│ ├── olliesrv/ 9P server: sessions, agents, backends
│ │ └── internal/
│ │ ├── agent/ Agent loop, history, compaction, prompts
│ │ ├── backend/ LLM provider implementations
│ │ ├── bypass/ Sandbox bypass broker
│ │ ├── fs/ 9P namespace and handlers
│ │ ├── prompts/ System-prompt resolution
│ │ ├── session/ Session lifecycle and persistence
│ │ └── toolclient/ Local/remote toolsrv process management
│ ├── toolsrv/ Sandboxed tool execution server
│ │ └── internal/
│ │ ├── exec/ Tool execution
│ │ ├── registry/ Per-agent tool registry
│ │ ├── sandbox/ Landlock configuration and enforcement
│ │ └── server/ Namespace specification and process state
│ └── ollie-9p/ 9P client CLI
├── tools/ Compiled tool implementations
│ ├── codeintel/ Tree-sitter code-intelligence tools
│ ├── filetools/ Go file tools
│ ├── lsp/ LSP bridge and client tools
│ └── web/ Web-fetch tool
├── toolsrv/ toolsrv 9P client library and registry types
├── virtfs/ Virtual filesystem declaration EDSL
├── lib9p/ 9P protocol library and native client
├── env/, format/, log/, paths/ Shared Go packages
├── kde/ KDE GUI, Kate, KRunner, and KIO integration
├── contrib/elisp/ Emacs frontend (`ellie.el`)
├── data/agents/ Agent configuration JSON
├── data/prompts/ Prompt templates
├── data/tools/ Script tools and `.meta` files
├── data/skills/ Domain knowledge modules
├── data/scripts/ CLI and integration scripts
├── data/services/ User service files
├── cmd/toolsrv/internal/sandbox/ Installed sandbox configuration source
├── doc/ Architecture and usage documentation
└── experiments/ Experimental code
Canonical source for prompts, tools, and skills
Do not edit ~/.config/ollie/ directly. It is an install target. Runtime data is copied from data/ by make install-data; compiled tools are built into the same runtime tools directory by their build targets. The installed configuration also contains backends.conf, sandbox.yaml, agents, prompts, skills, scripts, and tools.
Build System
The default make target builds, tests, and installs. Build and install are separate phases.
make # build + test + install
make build # build core, 9P, client, tools, and KDE
make core # go build ./...
make ninep # olliesrv and ollie-9p
make client # native lib9p shared library and header
make tools # compiled tools (code-intel, file, LSP, web)
make kde # KDE KF6 integration
make kde-kf5 # KDE KF5 integration
make install-data # runtime configuration, prompts, skills, scripts, and tools
make test # core and lib9p tests
make test-core # cmd/olliesrv, cmd/toolsrv, shared packages
make test-9p # lib9p tests
make clean # remove build artifacts
Requires GNU Make.
Testing
The supported test entry points are:
make test
make test-core
make test-9p
For direct Go testing, use the packages covered by make test-core and make test-9p.
Language & Conventions
- Go (root module): Go 1.25+, standard library preferred, minimal dependencies.
- C++20/Qt6/KF6 (kde): CMake build, dual Qt5/Qt6 support where noted.
- Elisp (el): single file
ellie.el. - Tool scripts: Python 3, Bash, or compiled binaries. Must be executable. Metadata lives in a
.metasidecar JSON file (seedata/tools/*.meta).
Code style
- Go:
gofmt, short variable names, error returns (no panics), table-driven tests. - Tool scripts: emit structured output (
STATUS=ok,STATUS=error). Image/LSP tools return JSON content blocks. - Prompts: markdown, concise, example-driven. Follow the pattern in existing
tools-*.mdfiles.
Architecture (key concepts)
- One integration surface:
olliesrvexposes sessions and agents through a 9P2000 filesystem. Frontends includeo,ollie-9p, KDE, Kate, Emacs, and scripts. Reads fromchat,statewait,eventwait, andfeedprovide blocking/event-driven synchronization. - Session and tool processes: Each session owns an agent runtime in
olliesrvand a separatetoolsrvprocess. They communicate over an authenticated Unix socket using 9P.toolclientcan respawn local toolsrv processes and can deploy/start toolsrv remotely over SSH with socket forwarding. - Agent loop (
cmd/olliesrv/internal/agent/loop.goandturn.go): Stream an LLM response, parse native or text tool calls, execute tools, update history, and repeat until a final response, cancellation, or a configured limit. It includes transient retries, context-overflow compaction, error/stall/replan controls, and tool-result caching. - Dynamic tools: Tools are external executables described by
.metafiles.toolsrvowns discovery and per-agent registries.olliesrvrefreshes the registry, injects common dispatch flags (bypass,timeout,sandbox,background), and calls tools throughtoolsrv.Conn. - Parallel and background dispatch: Non-conflicting tool calls in one turn run concurrently. Metadata scopes schedule
read,write, and global operations; shell-like global operations serialize. Any tool may run in the background, producing a process ID whose output is injected when the process changes or exits. - Sandbox (
cmd/toolsrv/internal/sandbox/): Landlock/landrun policies control filesystem and network access. The installedsandbox.yamlis sourced fromcmd/toolsrv/internal/sandbox/sandbox.yaml. The toolsrv namespace and process state live undercmd/toolsrv/p9.goandcmd/toolsrv/internal/server/. The bypass broker provides policy-controlled escape requests, approval, persistence, and rate limiting. - Backends (
cmd/olliesrv/internal/backend/): Supported names areollama,openai,openrouter,anthropic,copilot,kiro, andgemini. Configuration is read from~/.config/ollie/backends.conf; environment variables are fallback inputs. - Prompt assembly:
cmd/olliesrv/internal/prompts/system_prompt.mdis embedded as the default system prompt. An agent'ssystemPromptcan override it with a filesystem path.promptanduserPromptsentries resolve files, expand environment variables, and support legacy!commandentries. The runtime combines system, environment, agent, and tool sections. - Context management: History tracks messages, usage, costs, cache statistics, and structured task state. Cold/warm/hot result tiers and automatic compaction preserve recent context while summarizing older material.
- Sub-agents:
subagent_spawncreates a transient child session with an independent runtime and context. The child receives a one-time parent-history snapshot and returns only its final reply. Parent/child IDs are retained for tracing; concurrent children are supported. - 9P namespace declaration:
cmd/olliesrv/internal/fs/spec.godeclares the olliesrv namespace. The toolsrv namespace is declared bycmd/toolsrv/p9.gousingcmd/toolsrv/internal/server.Spec; process state and handlers are incmd/toolsrv/internal/server/. Both use thevirtfsEDSL andvirtfs.BuildTree().
Key Files
| What | Where |
|---|---|
| 9P namespace (olliesrv) | cmd/olliesrv/internal/fs/spec.go |
| 9P namespace (toolsrv) | cmd/toolsrv/p9.go, cmd/toolsrv/internal/server/server.go |
| virtfs EDSL | virtfs/decl.go, virtfs/builder.go |
| Agent loop and dispatch | cmd/olliesrv/internal/agent/loop.go, turn.go |
| Runtime and prompt assembly | cmd/olliesrv/internal/agent/runtime.go, prompt_resolver.go |
| Tool server binary | cmd/toolsrv/ |
| Tool server client and process lifecycle | toolsrv/client9p.go, cmd/olliesrv/internal/toolclient/ |
| Remote tool execution | cmd/olliesrv/internal/toolclient/spawn.go |
| Sandbox enforcement | cmd/toolsrv/internal/sandbox/ |
| Bypass broker | cmd/olliesrv/internal/bypass/ |
| Session management and persistence | cmd/olliesrv/internal/session/ |
| Embedded default system prompt | cmd/olliesrv/internal/prompts/system_prompt.md |
| Agent configs | data/agents/*.json |
| Backend configuration | data/backends.conf, ~/.config/ollie/backends.conf |
| Compiled tools | tools/codeintel/, tools/filetools/, tools/lsp/, tools/web/ |
| KDE GUI | kde/gui/ |
| Kate plugin | kde/kate/ |
Environment
Runtime configuration is read from ~/.config/ollie/ (or $XDG_CONFIG_HOME/ollie/). The backend configuration file is backends.conf, not env.
backend = ...inbackends.confselects the default backend; a sessionbackend=...can override it.OLLIE_BACKENDis a fallback when no configured backend or session backend is selected.OLLIE_MODELis a legacy/environment model fallback; configured backend sections can setmodel = ....- The runtime path helpers honor
XDG_CONFIG_HOME,XDG_DATA_HOME, andXDG_RUNTIME_DIR. - The Makefile install targets use
~/.config/ollie,~/.local/share/ollie, and~/.local/bindirectly; non-default XDG locations require adjusting the install targets or copying the installed files manually. - Runtime tools are discovered from
$XDG_CONFIG_HOME/ollie/tools(default:~/.config/ollie/tools). - OptMem runtime data is under
$XDG_DATA_HOME/ollie/optmem(default:~/.local/share/ollie/optmem).
Adding a new tool
Script-based tool (Python/Bash)
- Create an executable script in
data/tools/<name> - Create
data/tools/<name>.metawith JSON metadata:{"description":"...","prompt":"...","args":{...},"tier":"hot","readOnly":false} - Run
make install-datato install
Compiled tool (Go)
- Create a package under
tools/<family>/cmd/<name>/main.go- Read JSON args from stdin, write result to stdout, exit 0/1
- Share library code in
tools/<family>/(e.g.tools/lsp/)
- Create
<name>.metaalongsidemain.goin the samecmd/<name>/directory (same format as above) - Add a build target in the Makefile that compiles to
$(CFG)/tools/<name>and installs the.metafile alongside it - Run
maketo build and install
The .meta file lives with the code that produces the tool, not in data/tools/. See the tools target in the Makefile for the canonical pattern.
Both paths produce the same result: an executable + .meta in $XDG_CONFIG_HOME/ollie/tools.
The registry doesn't distinguish between scripts and binaries.
Adding a new prompt
- Write the markdown file in
data/prompts/ - If it should be loaded by default, reference it in
data/agents/default.json - Run
make install-datato install
KDE development
KDE integration is part of this repository under kde/. Build and install it through the root Makefile targets (make kde or make kde-kf5).
Key Lessons (Aug 14–17 session)
-
Unix permissions ARE the enforcement mechanism. Sub-agents get GID "subagent"; top-level agents get GID "agent". File modes control access. Don't invent authorization layers when
chmodworks. -
Context cancellation propagates automatically. Interrupting a parent kills all sub-agents at arbitrary depth through Go's
context.Contextchain. No explicit cleanup code needed. -
New features should be wiring, not construction. If a feature requires more than ~50 lines, you're probably building infrastructure that already exists. Sub-agents: 43 lines. Goals: ~40 lines of handler. The rest is prompt.
-
No pointless indirection. Thin wrappers, thin delegations, adapter functions that just call another function — these are banned. Call the real thing directly. Move the code, don't wrap it.
-
Shared code goes in shared packages.
ollie/toolsrvis importable by botholliesrvandtoolsrvbinaries. Don't duplicate functions across internal packages. -
The plan file is per-agent, NOT per-session. Each agent owns its own plan at
session/{s}/agent/{a}/plan. -
The goal file is per-session. Writing to
session/{s}/goaltriggers a workflow (default: conductor). The goal text is NEVER overwritten by status changes. -
Subtraction > addition. Removing
maxStepswas -52 lines. The timeout on sub-agents replaced it with 4 lines. Always look for what to remove first. -
The 9P namespace is the API. Every capability is a file. Read, Write, or Rdwr. BlockOnce and Stream are special cases of Read. That's the entire interface.
-
Rdwris an atomic operation — not a variant of read or write. It's write-then-read as one unit. Sub-agents, session creation, tool execution, and generation all use this primitive. -
Persisted state can contain garbage. When debugging impossible errors, check if the data itself is corrupted. A failed command's stderr captured and stored as state will return that error on every subsequent read — the bug isn't in the code, it's in the data.
-
Environment variables don't always propagate. Tools run through toolsrv, which sets specific env vars. If a tool calls another binary that expects
$USERor$OLLIE_UNAME, verify those are actually set in the execution context. Provide explicit fallbacks. -
Go build cache is aggressive. When source changes don't appear in the binary, the cache may be stale. Use
go clean -cacheor verify withgo version -m <binary>to check the embedded module version. Touch files if needed. -
Separate index files for separate concerns. Don't cram session and agent data into one line.
session/idxlists sessions;session/{s}/agent/idxlists agents per session. Simpler parsing, fewer race conditions, cleaner code.