334 lines
12 KiB
Markdown
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
|
|
```
|