|
|
||
|---|---|---|
| agent | ||
| backend | ||
| cmd | ||
| contrib/elisp | ||
| data | ||
| detach | ||
| doc | ||
| elevate | ||
| env | ||
| fs | ||
| fsedsl | ||
| kde@9a1704d728 | ||
| lib9p | ||
| log | ||
| paths | ||
| prompts | ||
| sandbox | ||
| session | ||
| skills | ||
| tools/lsp | ||
| toolsrv | ||
| .gitignore | ||
| .gitmodules | ||
| .plan.md | ||
| AGENTS.md | ||
| Containerfile | ||
| Ollie | ||
| README.md | ||
| go.mod | ||
| go.sum | ||
| justfile | ||
| lsp_definition | ||
| ollie-remote | ||
| pull.sh | ||
README.md
ollie
Ollie is an AI agent runtime. A single binary (olliesrv) exposes agent sessions
over a single surface: a 9P2000 filesystem. The core is deliberately minimal;
capabilities come from composing small pieces — tool executables, skill files,
metadata sidecars — rather than building a monolithic framework.
Getting started
git clone --recurse-submodules https://git.lneely.de/lkn/ollie.git
cd ollie
just
Requires just:
cargo install just
See doc/USAGE.md for usage instructions.
Configuration
Environment: ~/.config/ollie/env
OLLIE_BACKEND=openai # ollama | openai | anthropic | copilot | kiro (default: ollama)
OLLIE_OLLAMA_URL= # base URL for Ollama (default: http://localhost:11434)
OLLIE_OPENAI_URL=https://openrouter.ai/api
OLLIE_OPENAI_KEY=sk-or-...
OLLIE_ANTHROPIC_KEY=sk-ant-...
OLLIE_COPILOT_TOKEN=...
OLLIE_KIRO_TOKEN=... # bearer token or sqlite:// path (auto-detected from Kiro CLI if unset)
OLLIE_MODEL=qwen/qwen3-235b-a22b
OLLIE_TOOLS_PATH=~/.config/ollie/tools # directory for tool executables + .meta files
OLLIE_MEMORY_PATH=~/.config/ollie/memory # directory for memory files
OLLIE_ELEVATE_SOCKET=${XDG_RUNTIME_DIR}/ollie/elevate.sock
OLLIE_COMPLETE_BACKEND=ollama # backend for code completion
OLLIE_COMPLETE_MODEL=qwen3:latest # model for code completion
OLLIE_ENABLED_BACKENDS=openrouter,kiro # backends available for routing (comma-separated)
OLLIE_ROUTE_BACKEND=ollama # backend for the /route classifier
OLLIE_ROUTE_MODEL=qwen3:8b # model for task routing
Shell environment variables take precedence over the env file.
Repository layout
Single Go module with one Git submodule (kde).
| Directory | Language | Description |
|---|---|---|
agent/ |
Go | Agent loop, history, hooks, prompt resolution, commands |
backend/ |
Go | LLM providers (Anthropic, OpenAI, Ollama, Gemini, Copilot, CodeWhisperer) |
toolsrv/ |
Go | Tool server, dynamic tool dispatch, sandboxed execution, remote execution |
tools/lsp/ |
Go | LSP bridge daemon + client library (gopls, clangd, intelephense) |
session/ |
Go | Session lifecycle, config, persistence |
fs/ |
Go | 9P filesystem tree for session namespace |
detach/ |
Go | Background process management |
elevate/ |
Go | Elevation broker (privilege escalation) |
sandbox/ |
Go | Landlock sandbox config |
cmd/olliesrv/ |
Go | The main binary |
cmd/ollie-9p/ |
Go | 9P client |
cmd/ollie-remote/ |
Go | Remote execution binary |
kde/ |
C++/Qt6 | KDE plasmoid, GUI, Kate plugin, KRunner (submodule) |
data/agents/ |
JSON | Agent configs |
data/prompts/ |
Markdown | System prompt templates |
data/tools/ |
Mixed | Tool executables (scripts + compiled) + .meta sidecar files |
data/skills/ |
Markdown | Domain knowledge modules |
doc/ |
Markdown | Architecture docs, usage guide |
Architecture
graph TB
subgraph Frontends
ACME[Plan 9 acme]
EMACS[Emacs<br>ellie.el]
KDE[KDE<br>GUI / Kate / KRunner]
end
subgraph "olliesrv"
P9[9P Filesystem<br>session/ namespace]
SM[Session Manager]
AG[agent.Agent<br>loop · compaction<br>prompt templates]
ROUTE[Router<br>complete · generate · route]
TOOLS[toolsrv.Server<br>dynamic dispatch · sandbox]
end
subgraph "LLM Backends"
OLL[Ollama]
OAI[OpenAI / OpenRouter]
ANT[Anthropic]
COP[Copilot]
KIRO[Kiro]
end
subgraph Execution
LOCAL[Tool Scripts<br>loaded via 9P write<br>· shell, reasoning_think<br>· file_*, lsp_*, memory_*<br>· gui_*, subagent_*]
REMOTE[ollie-remote<br>via SSH · no embedded tools]
end
ACME & EMACS & KDE --> P9
P9 --> SM
SM --> AG
AG --> ROUTE
AG --> TOOLS
AG -->|streaming| OLL & OAI & ANT & COP & KIRO
TOOLS --> LOCAL
TOOLS -->|SSH| REMOTE
olliesrv exposes the agent runtime over a single surface: a 9P2000 filesystem.
KDE GUI, Kate plugin, and KRunner all use 9P internally. For a deep dive into the
filesystem, see doc/resources/9p.md.
Tool registry: zero built-in tools
No tools are compiled into the Go binary. Every tool — including shell and reasoning_think — is an external executable loaded dynamically through the 9P filesystem. Tool definitions, schemas, and execution logic live entirely in the filesystem.
Loading tools
Tools are loaded by writing their name to session/{sname}/agent/{aname}/tools:
# List currently loaded tools
ollie-9p read session/{sname}/agent/{aname}/tools
# Load a tool — becomes a native callable function
echo file_glob | ollie-9p write session/{sname}/agent/{aname}/tools
# Global catalog: all discoverable tools on disk
ollie-9p read tools
The tool_load tool is itself a bash script that does exactly this — it wraps the 9P write in a callable function so the LLM can load tools during a turn.
Agent autoLoad configuration
Agent configs declare which tools to load at startup via autoLoad:
{
"autoLoad": ["file_read", "file_edit", "file_glob", "file_grep", "file_write", "tool_load"]
}
Each agent profile (default, copilot, explorer, librarian, navigator, taskmanager, theo) has its own autoLoad list. The allowTools field provides a secondary security gate.
Declarative registration
Tools are executables in ~/.config/ollie/tools/. Each one has a .meta sidecar
file declaring its JSON schema, documentation, concurrency class, tier, and
privilege requirements. The registry reads .meta files — it never inspects the
executable itself.
This is declarative tool registration: write a .meta file, drop an
executable in $OLLIE_TOOLS_PATH, and the tool is live. No server restart,
no config file, no code change.
The .meta file (file_glob.meta):
{
"description": "Find files by glob pattern, sorted by mtime (newest first).",
"args": {"type":"object","properties":{"pattern":{"type":"string"}},"required":["pattern"]},
"tier": "cold",
"readOnly": true
}
Everything is sandboxed by default via Landlock. Tools that need to escape
request elevation explicitly ("elevated": true).
Privileged tools: sudo
Tools that need root privileges declare "sudo": true in their .meta. The
dispatch chain becomes a two-gate sequence: elevation approval (escape sandbox)
→ credential prompt → sudo -S execution. The tool itself has no knowledge of
sudo — the privilege wrapping is entirely in the dispatch layer.
{
"description": "Read system logs (dmesg).",
"sudo": true,
"cmd": "system_logs_dmesg",
"args": {"type":"object","properties":{"lines":{"type":"string"}}},
"readOnly": true
}
Credentials are prompted via kdialog/zenity on desktop, or forwarded over
SSH for remote execution. See doc/WRITING_TOOLS.md for
details.
Host-conditional variants
A single .meta file can describe a tool that works differently on different
hosts. Variants are gated by match conditions (binary, file, os, arch,
env, nenv). The first matching variant determines the tool's schema,
documentation, and executable. If no variant matches, the tool is hidden from
the registry — it doesn't exist on this host.
{
"description": "Read system logs.",
"variants": [
{
"match": {"binary": "journalctl"},
"cmd": "system_logs_journald",
"args": {"type":"object","properties":{"unit":{"type":"string"},"since":{"type":"string"}}}
},
{
"match": {"file": "/var/log/syslog"},
"cmd": "system_logs_syslog",
"args": {"type":"object","properties":{"lines":{"type":"string"}}}
}
]
}
On a systemd host the model sees unit filtering and time ranges. On Alpine it
sees tail + grep. On a host with neither, the tool doesn't appear. One .meta
deploys everywhere. See doc/WRITING_TOOLS.md for the
full variant specification.
What you can do
| Capability | How |
|---|---|
| Run an agent | olliesrv → any frontend (ellie, KDE, acme, o tui) |
| Terminal TUI | o tui — tmux + two shell commands, no widgets |
| Remote execution | Set remote=user@host in session config |
| Multi-agent | subagent_spawn from within a session, or shell scripts |
| Code completion | Read/write /complete for fill-in-the-middle |
| One-shot LLM | Write to /generate, read back the response |
| Task routing | Write to /route, read back backend=X model=Y |
| Background jobs | Detach via shell({"elevated":true, "detach":true}) |
| Load tools | Write tool name to agent/{aname}/tools via 9P |
| Custom tools | Drop an executable + .meta in $OLLIE_TOOLS_PATH |
| Privileged tools | Declare "sudo": true in .meta — broker handles elevation + credentials |
| Custom agents | Write a JSON config in agents/ |
| Domain skills | Write a markdown file in skills/ |
Credits
- Plan 9 from Bell Labs — for an interesting system
- @9fans — for the Plan 9 port
- Suckless — for articulating good software development principles
- @simonfxr — for a solid agent baseline to "borrow" from
- @aws — for a solid open-source agent implementation
License
GPLv3