The FUSE-based 9P mount (mount/) was unused — all actual mounting uses 9pfuse via ollie-remount. Remove the package, the olliesrv mount subcommand, and the go-fuse dependency. |
||
|---|---|---|
| agent | ||
| backend | ||
| cmd | ||
| contrib | ||
| dbus | ||
| detach | ||
| doc | ||
| elevate | ||
| env | ||
| fs/session | ||
| kde@d473fb4e4c | ||
| log | ||
| paths | ||
| prompts | ||
| sandbox | ||
| session | ||
| skills | ||
| tools | ||
| toolsrv | ||
| .gitignore | ||
| .gitmodules | ||
| .gitmodules~ | ||
| .plan.md | ||
| AGENTS.md | ||
| Containerfile | ||
| README.md | ||
| go.mod | ||
| go.sum | ||
| justfile | ||
| pull.sh | ||
README.md
ollie
Ollie is an AI agent runtime. A single binary (olliesrv) exposes agent sessions
over two surfaces: a 9P2000 filesystem (the canonical interface) and a
D-Bus service (org.ollie.SessionManager). Both are thin adapters over the
same core — the agent loop, tool dispatch, sandboxing, and multi-backend routing.
Choice of surface is per-deployment: 9P for its Unix composability, D-Bus for
desktop integration, or both.
The core is deliberately minimal. Capabilities come from composing small pieces — shell scripts, tool scripts, skill files — 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 scripts
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 u/complete
OLLIE_COMPLETE_MODEL=qwen3:latest # model for u/complete
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 two Git submodules for frontends.
| 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, registry, sandboxed execution, remote execution |
session/ |
Go | Session lifecycle, config, persistence |
fs/session/ |
Go | 9P filesystem tree for session namespace |
dbus/ |
Go | D-Bus adapter (org.ollie.SessionManager) |
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, tray (submodule) |
agents/ |
JSON | Agent configs |
prompts/ |
Markdown | System prompt templates |
tools/ |
Python/Bash | Tool scripts (file_read, lsp_, memory_, etc.) |
scripts/ |
Shell | Session management, utilities, plumbing |
doc/ |
Markdown | Architecture docs, usage guide |
Architecture
graph TB
subgraph Frontends
TER[Terminal<br>b / bfg / sh]
ACME[Plan 9 acme]
EMACS[Emacs<br>ellie.el]
KDE[KDE<br>plasmoid / GUI / Kate<br>KRunner / tray]
end
subgraph "olliesrv"
P9[9P Filesystem<br>session/ namespace]
DBUS[D-Bus Adapter<br>org.ollie.SessionManager]
SM[Session Manager]
AG[agent.Agent<br>loop · compaction<br>prompt templates]
ROUTE[Router<br>complete · generate · route]
TOOLS[toolsrv.Server<br>dynamic registry · dispatch]
SB[Landlock Sandbox]
end
subgraph "LLM Backends"
OLL[Ollama]
OAI[OpenAI / OpenRouter]
ANT[Anthropic]
COP[Copilot]
KIRO[Kiro]
end
subgraph Execution
LOCAL[Tool Scripts<br>file_* · lsp_* · memory_*<br>gui_* · subagent_*]
REMOTE[ollie-remote<br>via SSH]
RTOOL[Remote Tools]
end
TER & ACME & EMACS -->|9P| P9
KDE -->|D-Bus| DBUS
P9 --> SM
DBUS --> SM
SM --> AG
AG --> ROUTE
AG --> TOOLS
TOOLS --> SB
AG -->|streaming| OLL & OAI & ANT & COP & KIRO
SB --> LOCAL
SB -->|SSH| REMOTE --> RTOOL
olliesrv is a single binary. Which surface it exposes is a flag:
| Mode | Flags | Interface | Frontends |
|---|---|---|---|
| +9p +dbus | (default) | 9P filesystem + D-Bus signals | All |
| +9p -dbus | -nodbus |
9P filesystem only | Terminal, acme, Emacs, web |
| -9p +dbus | -no9p |
D-Bus only | KDE (plasmoid, GUI, Kate, KRunner, tray) |
9P: everything is a file
The 9P server exposes the agent runtime as a synthetic filesystem. Every session
is a directory; every operation is a read or write. There is no client library,
no SDK, no protocol buffer — echo, cat, tail, and rm are the API.
# Create a session
echo "name=worker" > /mnt/ollie/session/new
# Submit a prompt
echo "fix the bug in main.go" > /mnt/ollie/session/worker/agent/0/prompt
# Stream the response
tail -f /mnt/ollie/session/worker/agent/0/chat
# Check state
cat /mnt/ollie/session/worker/agent/0/state
# One-shot generation (no session needed)
exec 3<>/mnt/ollie/generate; echo 'explain monads' >&3; cat <&3; exec 3>&-
# Route a task to the best model
exec 3<>/mnt/ollie/route; echo 'redesign the auth system' >&3; cat <&3; exec 3>&-
# → backend=kiro model=claude-sonnet-4-20250514
Network transparency
9P is a network protocol. Mount the agent from any machine:
9pfuse 'tcp!server:5640' /mnt/ollie
No SSH tunneling, no port forwarding, no API gateway. One mount and every session, every tool, every config value is accessible as if local.
Shell composability
Because operations are file I/O, they compose with the full Unix toolkit:
# Fan out a prompt to all agents
for s in /mnt/ollie/session/*/agent/*/prompt; do echo "run tests" > "$s"; done
# Wait for all agents to finish
for s in /mnt/ollie/session/*/agent/*/statewait; do cat "$s" > /dev/null; done
# Grep all agent plans
grep -r "TODO" /mnt/ollie/session/*/plan
# Monitor costs
paste /mnt/ollie/session/*/agent/*/cost
Pipe agents into awk. Filter with grep. Schedule with cron. Orchestrate
with a 10-line shell script instead of a framework.
Plan 9 patterns
/net/dnspattern —/complete,/generate,/routeare stateless per-fid: write a request, read back the resultctlfiles — control operations (stop, kill, rename, compact) are writes to a control file, not method calls- Blocking reads —
statewaitblocks until state changes, replacing event subscriptions with a simplecat - Multiplexing — multiple clients mount the same server simultaneously, each with an independent view through per-fid state
D-Bus: desktop integration
The D-Bus interface (org.ollie.SessionManager) is embedded in olliesrv
(enabled by default, or exclusively via -no9p). It provides typed method
calls and real-time signals.
# Create a session
dbus-send --session --dest=org.ollie.SessionManager --type=method_call --print-reply \
/org/ollie/SessionManager org.ollie.SessionManager.CreateSession \
string:"$PWD" string:"" string:"" string:"default" string:"" string:""
# Submit a prompt
dbus-send --session --dest=org.ollie.SessionManager --type=method_call \
/org/ollie/SessionManager org.ollie.SessionManager.Submit \
string:"SESSION_ID" string:"fix the bug"
# Read state
dbus-send --session --dest=org.ollie.SessionManager --type=method_call --print-reply \
/org/ollie/SessionManager org.ollie.SessionManager.GetState string:"SESSION_ID"
Signals
D-Bus signals push events without polling:
ChatUpdated— new chat output (session_id, offset, text)StateChanged— transition notificationsSessionCreated/SessionKilled/SessionRenamed— lifecycle eventsProcessDetached/ProcessExited— background process events
GUI frontends subscribe once and react — no timers, no file watches, no busy loops.
KDE integration
- Plasmoid — embed an agent chat widget in the Plasma panel
- Kate plugin — inline AI assistance in the text editor
- KRunner — launch prompts from the desktop search bar
- System tray — status indicator and quick actions
- Standalone GUI — full-featured Qt6 chat application
Tool registry: dynamic native tools
Tools are executable scripts in ~/.config/ollie/tools/. Each one declares its
metadata — JSON schema, documentation, concurrency class — in header comments.
No server restart needed. No MCP server. No protocol. Just a file with a header.
# Discover all available tools
cat session/{id}/tools
# Load one into your session — becomes a native callable function
echo file_glob | 9p write session/{id}/tools
# The model sees it as a first-class tool on the next turn.
The header:
#!/usr/bin/env bash
# args_json: {"type":"object","properties":{"pattern":{"type":"string"}},"required":["pattern"]}
# ollie:prompt
# ## file_glob
# Find files by glob pattern.
# ollie:end
Everything is sandboxed by default via Landlock. Tools that need to escape
request elevation explicitly ("elevated": true).
What you can do
| Capability | How |
|---|---|
| Run an agent | olliesrv → s/sh or any frontend |
| Remote execution | Set remote=user@host in session config |
| Multi-agent | subagent_spawn from within a session, or shell scripts |
| Code completion | u/complete from acme, or read/write /complete |
| 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}) |
| Custom tools | Drop a script in $OLLIE_TOOLS_PATH |
| 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