This repository has been archived on 2026-08-16. You can view files and clone it, but cannot push or open issues or pull requests.
ollie-9p/README.md

334 lines
12 KiB
Markdown

# olliesrv
`olliesrv` is a [9P](http://9p.cat-v.org) server that exposes the ollie agent runtime as a synthetic filesystem. Sessions are directories, conversation is a file, prompts are writes, tools are executables. It also embeds a D-Bus adapter (`org.ollie.SessionManager`) for KDE desktop clients.
Mount it with `9pfuse` and interact with sessions using `echo`, `cat`, `tail`, and `rm`. The same interface works for interactive shells, editor plugins, scripts, web UIs, and multi-agent coordination.
For usage examples, see [doc/USAGE.md](../doc/USAGE.md) in the monorepo.
## Filesystem layout
```
ollie/
agents read: list of available agent configs
backends read: list of ollie-provided backends
complete r/w: code completion (write JSON, read result; per-fid, like /net/dns)
ctl write: root-level control commands
generate r/w: one-shot generation (write JSON, read result; per-fid)
help read: help file (backed by ~/.config/ollie/help.md)
models read: all models from all backends (tab-separated: backend\tmodel)
route r/w: model routing (write JSON, read "backend=X model=Y"; per-fid)
tools r/w: tool prompt lookup (write tool name, read documentation; per-fid)
s/ dir: sessions and session management scripts
new r/w: read: KV template; write: create session
idx read: index of all sessions (id, state, cwd, backend, model — one per line)
sh exec: interactive chat shell
ls exec: list active sessions
kill exec: kill a session by ID
b exec: one-shot query: create session, submit prompt, wait, print result, kill
bfg exec: batch foreground: submit prompt, wait, print result
bbg exec: batch background: submit prompt, print session path, return immediately
cleanup exec: kill all idle sessions
Session directories (multi-turn, idle<=>running):
<session-id>/ rm -r to kill; mv to rename
cfg r/w: KV snapshot of config and current state; write partial KV to mutate
read fields: state, backend, model, agent, cwd,
maxTokens, temperature, frequencyPenalty, presencePenalty
writable fields: backend, model, agent, cwd, maxTokens, temperature,
frequencyPenalty, presencePenalty
(state is read-only; silently ignored on write)
chat read: cumulative conversation history
context read: full message history as JSONL (one message per line)
cost read: cumulative cost in USD (if reported by backend)
ctl write: stop | <command>
ctxsz read: estimated context size vs context window
env read: session environment variables (OLLIE_SESSION_ID, OLLIE_*)
fifo.in write: queue a prompt for later execution
fifo.out read: pop the next queued prompt
models read: available models from the backend
offset read: byte offset in chat immediately after the last user prompt
peer/ dir: peer links (touch to add, rm to remove — both sides; write to send prompt)
plan r/w: scratch space for agent planning (persisted per session)
proc/ dir: detached background processes
prompt write: submit a prompt to the agent
prompt.prev read: the last submitted prompt
state read: current state (idle, thinking, calling: <tool>)
statewait read: blocks until state changes; returns new state
systemprompt read: fully rendered system prompt for this session
tail exec: exec tail -f chat
usage read: token counts (input, output, requests; [estimated] if not reported by backend)
```
Agent configs, prompt templates, tools, skills, and scripts all live on disk at `~/.config/ollie/` and are read directly by the runtime — they are not served through 9P.
Session IDs are Unix nanosecond timestamps with a random suffix (e.g. `1744276689123456789-2b986c`), so `ls s/` sorted lexicographically gives creation order.
## Building
```sh
mk
```
Installs `olliesrv` and `ollie-9p` to `$HOME/bin`.
## ollie-9p
`ollie-9p` is the 9P client used internally by agents and tool scripts to interact with the server. It auto-discovers the server via `$NAMESPACE` and identifies the caller via `$OLLIE_UNAME`.
```
ollie-9p [-a addr] <command> <path>
```
| Command | Description |
|---|---|
| `read path` | Read file contents to stdout |
| `write path` | Write stdin to file |
| `rdwr path` | Write stdin then read response (request-response files) |
| `stat path` | Print file metadata |
| `ls [-l] [-d] path` | List directory contents |
| `create path` | Create a file |
| `remove path` | Remove a file/directory |
| `mkdir path` | Create a directory |
Examples:
```sh
# Submit a prompt
echo "explain this code" | ollie-9p write s/ollie/prompt
# Load a tool
echo 'file_edit' | ollie-9p rdwr tools
# Route a task to a model
echo 'implement login page' | ollie-9p rdwr route
# Create a session
echo "cwd=$PWD" | ollie-9p write s/new
# Read session state
ollie-9p read s/ollie/state
```
Tool scripts use `ollie-9p` rather than the FUSE mount so they work in sandboxed environments where the mount may not be visible.
## Usage
```sh
olliesrv # start the server (foreground)
olliesrv mount <addr> [mnt] # mount a remote 9P server via 9pfuse
olliesrv mount <addr> [mnt] # mount a remote 9P server via 9pfuse
```
The server listens on a Unix socket in the Plan 9 namespace (`$NAMESPACE/ollie`) and optionally mounts via `9pfuse` to `$HOME/mnt/ollie` (or `$OLLIE`).
`olliesrv mount` is for remote instances: it calls `9pfuse <addr> <mnt>` and defaults the mountpoint to `$HOME/mnt/<addr>`.
### Flags
| Flag | Effect |
|------|--------|
| `-strict` | Only promoted tools are allowed; inline `shell` commands are rejected. |
| `-yolo` | Skip the landrun sandbox for all execution. |
| `-tcp <addr>` | Also listen on a TCP address (e.g. `:564`). |
| `-mount <path>` | Override the FUSE mount path. |
| `-nodbus` | Disable the embedded D-Bus adapter (implied by `-tcp`). |
```sh
olliesrv -strict # tools only, sandboxed
olliesrv -yolo # arbitrary code, no sandbox
olliesrv -strict -yolo # tools only, no sandbox
```
## Remote Access
### 9P mount
`olliesrv` speaks 9P over TCP. A remote instance can be mounted into the local namespace using `9pfuse`:
```sh
# On the remote host:
olliesrv -tcp :9564
# Locally:
olliesrv mount remotehost:9564 ~/mnt/remotehost
ls ~/mnt/remotehost # s/, ...
```
Sessions created under `~/mnt/remotehost/s/` run on the remote host, so tool calls execute close to the remote filesystem rather than over the wire.
### HTTP gateway
See [httpgw/](../httpgw/) for the HTTP REST gateway (`ollie-httpgw`).
### Container
A `Containerfile` is included (at the monorepo root) for running a self-contained remote server:
```sh
podman build --network=host -t olliesrv .
podman run --network=host -e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY olliesrv
```
Then mount locally:
```sh
olliesrv mount localhost:9564 ~/mnt/container-ollie
```
## Sessions
### Create
```sh
cat $OLLIE/s/new # show required/optional KV pairs
echo "cwd=$PWD" > $OLLIE/s/new
echo "cwd=$PWD backend=ollama model=qwen3:8b" > $OLLIE/s/new
```
Valid keys: `cwd` (required), `backend`, `model`, `agent`.
### Send a prompt
```sh
echo "what files are in the current directory?" > $OLLIE/s/<session-id>/prompt
```
Writes dispatch asynchronously on close; the shell returns immediately.
### Read the conversation
```sh
cat $OLLIE/s/<session-id>/chat # full history snapshot
tail -f $OLLIE/s/<session-id>/chat # follow output as it arrives
```
### Check state
```sh
cat $OLLIE/s/<session-id>/state
# idle | thinking | calling: <toolname>
```
### Control
```sh
echo stop > $OLLIE/s/<session-id>/ctl # interrupt current turn
echo compact > $OLLIE/s/<session-id>/ctl # summarize context
echo clear > $OLLIE/s/<session-id>/ctl # clear history
echo kill > $OLLIE/s/<session-id>/ctl # kill session
echo "rn my-name" > $OLLIE/s/<session-id>/ctl
echo "model qwen3:8b" > $OLLIE/s/<session-id>/ctl
```
`ctl` accepts only recognized commands: `stop`, `kill`, `rn <name>`, `save`, `compact`, `clear`, `backend`, `model`, `models`, `agents`, `agent`, `sessions`, `cwd`, `skills`, `tools`, `context`, `usage`, `cost`, `history`, `irw`, `help`. The `/` prefix is added automatically. Unrecognized input is rejected with an error.
### Switch backend, model, or agent
```sh
echo ollama > $OLLIE/s/<session-id>/backend
echo qwen3:8b > $OLLIE/s/<session-id>/model
echo myagent > $OLLIE/s/<session-id>/agent
```
Writes to `backend`, `model`, and `agent` are rejected when the agent is not idle. Check `state` to confirm the change took effect.
### Kill and rename
```sh
rm -r $OLLIE/s/<session-id> # kill
mv $OLLIE/s/<session-id> $OLLIE/s/my-friendly-name # rename
```
Rename is rejected if the agent is running or the target name already exists. All open file handles into the session are updated automatically.
## Stateless endpoints: generate, route, complete
Three root-level files provide request/response access without session state. They follow the [Plan 9 /net/dns pattern](https://9fans.github.io/plan9port/man/man3/ndb.html): open the file, write a request, then read the response from the same file descriptor. The result is per-fid (per open file descriptor) and consumed on first read.
Because the write and read must happen on the same fd, a simple `echo > file && cat file` won't work — that opens two separate fds. Use `exec` with a persistent fd, or `9p rdwr`.
### generate
One-shot LLM generation with no session context.
**Request** (JSON): `{"prompt": "...", "system": "...", "backend": "...", "model": "..."}`
All fields except `prompt` are optional. If the write is not valid JSON, the entire string is used as the prompt.
**Response**: the model's text output.
```sh
# Using exec to keep the fd open:
exec 3<> $OLLIE/generate
echo '{"prompt": "what is 2+2?", "backend": "ollama", "model": "qwen3:8b"}' >&3
cat <&3
exec 3>&-
# Plain text (uses default backend/model from env):
exec 3<> $OLLIE/generate
echo 'what is 2+2?' >&3
cat <&3
exec 3>&-
```
### route
Model selection: given a task description, picks the best backend+model from all available models.
**Request** (JSON): `{"task": "...", "backend": "..."}` — `backend` is optional (restricts candidates).
Plain text is treated as the task.
**Response**: `backend=<name> model=<name>`
The classifier uses `OLLIE_ROUTE_BACKEND` (default: `ollama`) and `OLLIE_ROUTE_MODEL` (default: `qwen3:8b`).
```sh
exec 3<> $OLLIE/route
echo 'write a bash script to parse JSON' >&3
cat <&3
exec 3>&-
# → backend=openrouter model=anthropic/claude-sonnet-4-20250514
```
### complete
Code completion: given file context (prefix/suffix around the cursor), returns a suggested insertion.
**Request** (JSON): `{"cwd": "...", "file": "...", "prefix": "...", "suffix": "...", "context": "..."}`
All fields except `prefix` are optional. Plain text is treated as the prefix.
Uses `OLLIE_COMPLETE_BACKEND` and `OLLIE_COMPLETE_MODEL`.
**Response**: the completion text.
```sh
exec 3<> $OLLIE/complete
echo '{"prefix": "func main() {\n\t", "suffix": "\n}", "file": "main.go"}' >&3
cat <&3
exec 3>&-
```
## Example shell session
```sh
$ echo "cwd=$PWD" > $OLLIE/s/new
$ ls $OLLIE/s/
new
1744276689123456789-2b986c
$ cd $OLLIE/s/1744276689123456789-2b986c
$ tail -f chat &
$ echo "list the go files in $PWD" > prompt
user: list the go files in /home/lkn/src/ollie
assistant: -> shell({"cmd":"find . -name '*.go'"})
= agent/core.go
agent/loop.go
...
assistant: The Go source files are: core.go, loop.go, ...
$ cat state
idle
$ mv $OLLIE/s/1744276689123456789-2b986c $OLLIE/s/ollie-demo
```