233 lines
9.8 KiB
Markdown
233 lines
9.8 KiB
Markdown
# olliesrv
|
|
|
|
Ollie starts from a simple, slightly silly question: *What might an AI agent look like in [Plan 9](https://www.usenix.org/legacy/publications/compsystems/1995/sum_pike.pdf)?* From there follow two more: *What happens if agent primitives are exposed as an ordinary filesystem?* and *How much can we subtract from agent implementations while still being useful?*
|
|
|
|
`olliesrv` is the answer: a [9P](http://9p.cat-v.org) server that defines what an agent is, then exposes its state and behaviors as regular files and I/O streams. Sessions are directories, conversation is a file you `tail -f`, prompts are writes, tools are executables. Mount it with `9pfuse` and interact with AI sessions using ordinary shell tools — the same primitives scale from simple copilot use cases (`u/complete` manages a session per working directory for ghost-text code completion in [acme](http://acme.cat-v.org)) through interactive shells and one-shot pipelines, up to multi-agent workflows coordinated by scripts, other agents, or both.
|
|
|
|
The goal is integration, not self-sufficiency. Rather than providing orchestration, scheduling, or workflow primitives, olliesrv exposes a stable surface and defers everything else to the surrounding environment (shell scripts, TUIs, web apps, cron, containers, and other OS facilities). Scripting, chaining, monitoring, and automation come from composing olliesrv with tools that already exist, not from building those capabilities into the server.
|
|
|
|
For usage examples, see [doc/USAGE.md](https://github.com/lneely/ollie/blob/main/doc/USAGE.md) in the monorepo.
|
|
|
|
## Filesystem layout
|
|
|
|
```
|
|
ollie/
|
|
backends read: list of ollie-provided backends
|
|
help read: help file (backed by ~/.config/ollie/help.md)
|
|
models read: all models from all backends (tab-separated: backend\tmodel)
|
|
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
|
|
plan r/w: scratch space for agent planning (persisted per session)
|
|
prompt write: submit a prompt to the agent
|
|
prompt.prev read: the last submitted prompt
|
|
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` to `$HOME/bin`.
|
|
|
|
## Usage
|
|
|
|
```sh
|
|
olliesrv start # start daemon (backgrounds itself)
|
|
olliesrv fgstart # start in foreground
|
|
olliesrv stop # stop daemon
|
|
olliesrv status # check if running
|
|
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 `{tool}` steps are allowed in `execute_code`; inline `{code}` steps are rejected. |
|
|
| `-yolo` | Skip the landrun sandbox for all `execute_code` execution. |
|
|
| `-tcp <addr>` | Also listen on a TCP address (e.g. `:564`). |
|
|
| `-mount <path>` | Override the FUSE mount path. |
|
|
|
|
```sh
|
|
olliesrv start -strict # tools only, sandboxed
|
|
olliesrv start -yolo # arbitrary code, no sandbox
|
|
olliesrv start -strict -yolo # tools only, no sandbox
|
|
```
|
|
|
|
## Remote Access
|
|
|
|
Two approaches: mount the 9P namespace directly with `9pfuse`, or reach it over HTTP via `ollie-httpgw`.
|
|
|
|
### 9P mount
|
|
|
|
Because `olliesrv` speaks 9P over TCP, a remote instance can be mounted into the local namespace using `9pfuse`. Agent sessions, tools, and all other filesystem state on a remote host become ordinary local files — no special client needed.
|
|
|
|
```sh
|
|
# On the remote host:
|
|
olliesrv start -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
|
|
|
|
`ollie-httpgw` translates HTTP to 9P, letting any HTTP client interact with `olliesrv` without a 9P library. It's also the backend for the web UI.
|
|
|
|
```sh
|
|
# Connect to local server:
|
|
ollie-httpgw
|
|
|
|
# Connect to remote server:
|
|
ollie-httpgw -net tcp -addr remotehost:9564
|
|
```
|
|
|
|
See [doc/USAGE.md](https://github.com/lneely/ollie/blob/main/doc/USAGE.md) for full details on the gateway and web UI.
|
|
|
|
### 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 or point httpgw at it:
|
|
|
|
```sh
|
|
olliesrv mount localhost:9564 ~/mnt/container-ollie
|
|
# or
|
|
ollie-httpgw -net tcp -addr localhost:9564
|
|
```
|
|
|
|
## 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.
|
|
|
|
## 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: -> execute_code({"code":"find . -name '*.go'","language":"bash"})
|
|
= pkg/agent/core.go
|
|
pkg/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
|
|
```
|