# 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 next `just install-data`. > Edit source files here, then run `just` to 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 Monorepo with Git submodules. Each submodule has its own Go module (except `el` which is Elisp and `kde` which is C++/Qt). ``` ollie/ ← you are here ├── virtfs/ (Go module) Virtual filesystem EDSL (FsNodeDecl, BuildTree, Tree) ├── toolsrv/ (Go) Tool server client library (Conn, Dial) ├── cmd/ │ ├── olliesrv/ (Go) 9P server binary │ │ └── internal/ │ │ ├── fs/ 9P namespace (everything in spec.go) │ │ │ ├── spec.go Single source of truth — entire namespace + handlers │ │ │ └── support.go Shared utilities (streamChat, stripMarkers, dispatch) │ │ ├── agent/ Agent loop, history, hooks │ │ ├── session/ Session lifecycle, persistence │ │ ├── backend/ LLM backends (ollama, openai, anthropic, etc.) │ │ └── bypass/ Sandbox bypass broker │ ├── toolsrv/ (Go) Tool server binary (sandboxed execution) │ │ └── internal/ │ │ ├── fs/ toolsrv 9P namespace (spec.go) │ │ └── sandbox/ Landlock enforcement │ ├── ollie-9p/ (Go) CLI client for the 9P namespace │ └── ollie-remote/ (Go) Remote execution agent ├── tools/ (Go) Tool implementations: │ └── lsp/ LSP bridge + cmd/ binaries ├── kde/ (C++/Qt6) KDE integration: GUI, Kate plugin, KRunner, tray ├── contrib/elisp (Elisp) Emacs frontend (ellie.el) ├── data/agents/ Agent config JSONs (loaded at runtime) ├── data/prompts/ System prompt templates (markdown) ├── data/tools/ Tool executables + .meta sidecar files ├── data/skills/ Domain knowledge modules (markdown) ├── sandbox/ Landlock sandbox config YAML ├── doc/ Architecture docs, usage guide ├── contrib/ Community scripts └── experiments/ Trial notes ``` ### Canonical source for prompts/tools/skills > **⛔ DO NOT edit `~/.config/ollie/` directly — it is an install target. All changes go in this repo.** Prompt and tool files live in `data/`. The `just install-data` target copies them to `~/.config/ollie/`. | Location | Purpose | Deployed by | |----------|---------|-------------| | `data/prompts/`, `data/tools/` | Canonical source for all prompts, tools, and skills | `just install-data` | | `data/skills/` | Domain knowledge modules (markdown) | `just install-data` | | `kde/` | KDE-specific tool scripts (`gui_*`) | `just install-kde` | ## Build System ```bash # Build and install everything: just # Individual build targets: just ninep # olliesrv + ollie-9p just toolsrv # tool server binary just core # core Go packages just kde # KDE integration (cmake with ~/.local prefix) just lsp-tools # LSP tool binaries # Install targets: just install-data # agents, prompts, tools, skills → ~/.config/ollie/ just install-el # ellie.el → ~/.config/emacs/ellie/ # Test: just test # run all tests just test-core # core Go tests just test-9p # 9p integration tests # Lifecycle: just uninstall # remove all installed files just clean # remove build artifacts ``` Requires [just](https://github.com/casey/just): `cargo install just` ## Testing ```bash # All Go tests (excludes cmd/ollie-remote which needs just to build): go test $(go list ./... | grep -v cmd/ollie-remote) # KDE has integration test scripts: cd kde && ./test-e2e.sh # ollie-remote requires the just build pipeline to resolve embedded deps: just ollie-remote just test # or individually: just test-remote ``` **Note on `cmd/ollie-remote`**: This package uses `//go:embed sandbox/default.yaml` and `//go:embed all:tools`. The embedded files are not in the source tree — `just ollie-remote` copies them in before building then cleans up. Running `go test ./...` will fail on this package. Use `just test-remote` or `go test $(go list ./... | grep -v cmd/ollie-remote)` instead. ## 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 `.meta` sidecar JSON file (see `data/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-*.md` files. ## Architecture (key concepts) 1. **One integration surface**: 9P filesystem. Sessions at `session/{sname}/agent/{aname}/`. All control via `ctl` (rdwr: write command, read response). Tools, skills, memory on physical filesystem via env vars. 2. **Two-process model**: Each session runs an `olliesrv` (the 9P namespace, agent loop, backends) and a per-session `toolsrv` (sandboxed tool execution). They communicate over a Unix socket using 9P. The toolsrv is spawned by olliesrv at session creation and has its own namespace (`/proc/new`, `/ctl`, `/tools`, etc.). 3. **Agent loop** (`cmd/olliesrv/internal/agent/loop.go`): Streaming LLM call → parse tool calls → dispatch to toolsrv → loop until no more tool calls or max steps. 4. **Tool dispatch**: ALL tools (including `reasoning_think`, `file_read`, `shell`) execute through the per-session toolsrv. The agent holds a `*toolsrv.Conn` (9P client) and calls `CallTool(name, args)` which writes to `proc/new` and reads the result. Tool registry is per-agent, keyed by agent ID in the protocol. 5. **Sandbox** (`cmd/toolsrv/internal/sandbox/`): Landlock-based. Config in `sandbox/*.yaml` defines filesystem access per profile. Escape via bypass broker (`cmd/olliesrv/internal/bypass/`). 6. **Backends** (`cmd/olliesrv/internal/backend/`): Ollama, OpenAI-compatible, Anthropic, Copilot, Kiro, Gemini. Selectable per-session. 7. **Prompts assembled at runtime**: Agent JSON `prompt` array specifies which prompt files to concatenate. The base system prompt is embedded in the binary and always prepended. 8. **9P namespace declared via virtfs EDSL**: The entire filesystem is a single recursive `FsNodeDecl` tree in `cmd/olliesrv/internal/fs/spec.go`, built by `virtfs.BuildTree()`. Every handler is an inline closure — no indirection. Same pattern in `cmd/toolsrv/internal/fs/spec.go`. ## Key Files | What | Where | |------|-------| | 9P namespace (olliesrv) | `cmd/olliesrv/internal/fs/spec.go` | | 9P namespace (toolsrv) | `cmd/toolsrv/internal/fs/spec.go` | | virtfs EDSL | `virtfs/decl.go`, `virtfs/builder.go` | | Agent loop | `cmd/olliesrv/internal/agent/loop.go` | | Tool server binary | `cmd/toolsrv/` | | Tool server client | `toolsrv/client9p.go` | | Remote execution | `cmd/ollie-remote/` | | Sandbox enforcement | `cmd/toolsrv/internal/sandbox/` | | Session management | `cmd/olliesrv/internal/session/` | | System prompt template | Embedded in binary (`cmd/olliesrv/internal/prompts/`) | | Agent configs | `data/agents/*.json` | | KDE GUI | `kde/gui/` | | Kate plugin | `kde/kate/` | ## Environment Config lives in `~/.config/ollie/env`. Key variables: - `OLLIE_BACKEND` — default backend (ollama, openai, anthropic, copilot, kiro) - `OLLIE_MODEL` — default model - Tools live at `$XDG_CONFIG_HOME/ollie/tools` (default: `~/.config/ollie/tools`) - Memory lives at `$XDG_DATA_HOME/ollie/optmem` (default: `~/.local/share/ollie/optmem`) ## Adding a new tool ### Script-based tool (Python/Bash) 1. Create an executable script in `data/tools/` 2. Create `data/tools/.meta` with JSON metadata: ```json {"description":"...","prompt":"...","args":{...},"tier":"hot","readOnly":false} ``` 3. Run `just install-data` to install ### Compiled tool (Go) 1. Create a package under `tools//cmd//main.go` - Read JSON args from stdin, write result to stdout, exit 0/1 - Share library code in `tools//` (e.g. `tools/lsp/`) 2. Create `.meta` alongside `main.go` in the same `cmd//` directory (same format as above) 3. Add a build target in the justfile that compiles to `{{cfg}}/tools/` **and** installs the `.meta` file alongside it 4. Run `just` to build and install The `.meta` file lives with the code that produces the tool, not in `data/tools/`. See the `lsp-tools` just target 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 1. Write the markdown file in `data/prompts/` 2. If it should be loaded by default, reference it in `data/agents/default.json` 3. Run `just install-data` to install ## Submodule workflow The only remaining submodule is `kde/`. For KDE: ```bash # Update KDE submodule: git submodule update --remote --merge kde # Work in the KDE submodule: cd kde # ... make changes, commit ... git push cd .. git add kde git commit -m "update kde submodule" ``` Each submodule has its own remote at `ssh://lkn@lneely.de:44220/lkn/ollie-{name}.git`. When cloning, use `--recurse-submodules` or run `git submodule update --init --recursive`.