324 lines
13 KiB
Markdown
324 lines
13 KiB
Markdown
# olliesrv
|
|
|
|
A 9P server that exposes [ollie](https://github.com/lneely/ollie) agent sessions as a virtual filesystem. Mount it with `9pfuse` and interact with AI sessions using ordinary shell tools.
|
|
|
|
The goal is integration, not self-sufficiency. Rather than providing orchestration, scheduling, or workflow primitives, olliesrv exposes a stable surface — sessions as directories, conversation as files — and defers everything else to the surrounding environment. Scripting, chaining, monitoring, and automation come from composing olliesrv with tools that already exist, not from building those capabilities into the server.
|
|
|
|
## Filesystem layout
|
|
|
|
```
|
|
ollie/
|
|
a/ dir: agent configs (r/w, backed by ~/.config/ollie/agents/)
|
|
<name>.json r/w: agent config; supports mv, cp, rm
|
|
backends read: list of ollie-provided backends
|
|
help read: help file (backed by ~/.config/ollie/help.md)
|
|
p/ dir: prompt templates (read, backed by ~/.config/ollie/prompts/)
|
|
00_base.md read: tone, accuracy, task execution, environment
|
|
01_ollie.md read: session identity, filesystem layout, session lifecycle
|
|
02_reasoning.md read: reasoning instructions
|
|
03_memory.md read: memory instructions
|
|
04_task.md read: task planning instructions
|
|
05_edit-text.md read: text editing instructions
|
|
06_skills.md read: skill discovery and loading
|
|
07_tools.md read: tool script usage
|
|
|
|
Prompt files are assembled in lexical order and rendered as Go templates at session
|
|
start. Changes take effect the next time the system prompt is rebuilt: on new session
|
|
creation, `/cwd` change, or `/agent` switch.
|
|
|
|
b/ dir: batch jobs and job scripts
|
|
new r/w: read: KV template; write: submit a job spec
|
|
idx read: job index (id, status, cwd, agent — one per line)
|
|
job exec: core batch job runner (submit, wait, print result)
|
|
q exec: foreground one-shot query; thin wrapper around job
|
|
sched exec: submit background job; prints b/ path; wrapper around job
|
|
<job-id>/ rm -r to cancel and remove
|
|
spec read: original job spec as submitted
|
|
status read: running | done | failed: <reason>
|
|
result read: assistant reply (populated when done)
|
|
usage read: token counts
|
|
ctxsz read: context size
|
|
pl/ dir: plans
|
|
s/ dir: sessions and session management scripts
|
|
new r/w: read: KV template; write: create session
|
|
idx read: session index (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
|
|
<session-id>/ rm -r to kill session; mv to rename
|
|
agent r/w: active agent name
|
|
backend r/w: active backend name
|
|
chat read: cumulative conversation history
|
|
ctl write: stop | <command>
|
|
ctxsz read: estimated context size vs context window
|
|
dequeue read: pop the next queued prompt
|
|
enqueue write: queue a prompt for later execution
|
|
mcp read: MCP server list
|
|
model r/w: active model name
|
|
models read: available models from the backend
|
|
prompt write: submit a prompt to the agent
|
|
state read: current agent state (idle, thinking, calling: <tool>)
|
|
systemprompt read: fully rendered system prompt for this session
|
|
usage read: token counts (input, output, requests; [estimated] if not reported by backend)
|
|
cwd r/w: working directory for tool execution and system prompt
|
|
sk/ dir: skills (r/w, from OLLIE_SKILLS_PATH or ~/.config/ollie/skills/)
|
|
<name>.md r/w: skill SKILL.md content
|
|
t/ dir: tool scripts (r/w, backed by ~/.config/ollie/tools/)
|
|
<script> r/w: tool script content
|
|
u/ dir: utility scripts (read, backed by ~/.config/ollie/scripts/u/)
|
|
<script> exec: utility script; compositions of b/ primitives
|
|
```
|
|
|
|
Session IDs are Unix nanosecond timestamps with a random suffix (e.g. `1744276689123456789-2b986c`), so `ls s/` sorted lexicographically gives creation order.
|
|
|
|
## Backing stores
|
|
|
|
Each 9P directory endpoint is backed by a named store implementing one of the store interfaces (`ReadableStore`, `ReadWriteStore`, `BlobStore`, or `Store`). The server routes all filesystem operations through the store — it has no direct knowledge of what underlies it.
|
|
|
|
| Path | Default backing | Interface |
|
|
|-------|------------------------|-----------------|
|
|
| `/a` | `FlatDirStore` | `Store` |
|
|
| `/b` | `BatchStore` | `Store` |
|
|
| `/m` | `FlatDirStore` | `Store` |
|
|
| `/p` | `FlatDirStore` | `ReadableStore` |
|
|
| `/pl` | `FlatDirStore` | `Store` |
|
|
| `/sk` | `SkillStore` | `Store` |
|
|
| `/t` | `ToolStore` | `Store` |
|
|
| `/u` | `UtilStore` | `Store` |
|
|
| `/s` | `SessionStore` | `Store` |
|
|
|
|
To swap a backing store (e.g. replace `/pl` with a vector database or `/m` with an object store), implement the appropriate interface and wire it in `New()`. The store interface requires only `Stat`, `List`, `Get`, `Put`, `Delete`, `Create`, and `Rename` — authentication, connection management, and credential rotation are internal concerns of the implementation.
|
|
|
|
`SessionStore` is the exception: it holds live session state and is tightly coupled to the server's session lifecycle. Replacing it is possible in principle but requires understanding the session management internals.
|
|
|
|
## 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
|
|
```
|
|
|
|
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`).
|
|
|
|
## Sessions
|
|
|
|
### Create a session
|
|
|
|
```sh
|
|
cat ~/mnt/ollie/s/new # show required/optional KV pairs
|
|
echo "cwd=/home/lkn/src/myproject" > ~/mnt/ollie/s/new
|
|
echo "cwd=/home/lkn/src/myproject backend=ollama model=qwen3:8b" > ~/mnt/ollie/s/new
|
|
```
|
|
|
|
Valid keys: `cwd` (required), `backend`, `model`, `agent`.
|
|
|
|
A new session directory appears under `s/`.
|
|
|
|
### Send a prompt
|
|
|
|
```sh
|
|
echo "what files are in the current directory?" > ~/mnt/ollie/s/<session-id>/prompt
|
|
```
|
|
|
|
Writes dispatch asynchronously on close, so the shell returns immediately. The agent runs in the background.
|
|
|
|
### Read the conversation
|
|
|
|
```sh
|
|
cat ~/mnt/ollie/s/<session-id>/chat # full history snapshot
|
|
tail -f ~/mnt/ollie/s/<session-id>/chat # follow output as it arrives
|
|
```
|
|
|
|
The `chat` file is an append-only log of the full conversation.
|
|
|
|
### Check agent state
|
|
|
|
```sh
|
|
cat ~/mnt/ollie/s/<session-id>/state
|
|
# idle | thinking | calling: <toolname>
|
|
```
|
|
|
|
### Control a session
|
|
|
|
```sh
|
|
echo stop > ~/mnt/ollie/s/<session-id>/ctl # interrupt the current turn
|
|
echo compact > ~/mnt/ollie/s/<session-id>/ctl # summarize context
|
|
echo clear > ~/mnt/ollie/s/<session-id>/ctl # clear session history
|
|
echo kill > ~/mnt/ollie/s/<session-id>/ctl # kill session
|
|
echo "rn my-name" > ~/mnt/ollie/s/<session-id>/ctl # rename session
|
|
echo "model qwen3:8b" > ~/mnt/ollie/s/<session-id>/ctl
|
|
```
|
|
|
|
`ctl` accepts only recognized commands: `stop`, `kill`, `rn <name>`, `compact`, `clear`, `backend`, `model`, `models`, `agents`, `agent`, `sessions`, `cwd`, `skills`, `tools`, `mcp`, `context`, `usage`, `history`, `irw`, `help`. The `/` prefix is added automatically. Unrecognized input is rejected with an error.
|
|
|
|
### Switch backend, model, or agent
|
|
|
|
```sh
|
|
echo ollama > ~/mnt/ollie/s/<session-id>/backend
|
|
echo qwen3:8b > ~/mnt/ollie/s/<session-id>/model
|
|
echo myagent > ~/mnt/ollie/s/<session-id>/agent
|
|
```
|
|
|
|
Writes to `backend`, `model`, and `agent` are rejected with an error when the agent is not idle. Writes to `ctl` return an error for unrecognized commands; recognized commands are dispatched asynchronously. Check `state` to confirm the change took effect.
|
|
|
|
### Kill a session
|
|
|
|
```sh
|
|
rm -r ~/mnt/ollie/s/<session-id>
|
|
```
|
|
|
|
### Rename a session
|
|
|
|
```sh
|
|
mv ~/mnt/ollie/s/<session-id> ~/mnt/ollie/s/my-friendly-name
|
|
```
|
|
|
|
Rename is rejected if the agent is running or the target name already exists. All open file handles into the session are updated automatically.
|
|
|
|
## Batch jobs
|
|
|
|
`b/` provides ephemeral one-shot agent runs. Each job is a single prompt → single result with no persistent session state. Jobs run concurrently; each gets its own entry under `b/`.
|
|
|
|
### Submit a job
|
|
|
|
```sh
|
|
cat ~/mnt/ollie/b/new # show the spec template
|
|
```
|
|
|
|
Write a spec to `b/new`:
|
|
|
|
```
|
|
name=my-job
|
|
cwd=/home/lkn/src/myproject
|
|
agent=default
|
|
backend=anthropic
|
|
model=claude-sonnet-4-6
|
|
parallel=1
|
|
---
|
|
Summarize the top-level Go files in the current directory.
|
|
```
|
|
|
|
```sh
|
|
printf 'name=my-job\ncwd=%s\n---\nSummarize this repo.\n' "$PWD" > ~/mnt/ollie/b/new
|
|
```
|
|
|
|
Valid header keys: `name` (optional; auto-generated if omitted), `cwd` (required), `agent`, `backend`, `model`, `output`, `parallel`.
|
|
|
|
Setting `parallel=N` creates N independent jobs named `{name}-0` through `{name}-N-1`, all running the same prompt concurrently.
|
|
|
|
### Poll and read results
|
|
|
|
```sh
|
|
cat ~/mnt/ollie/b/<job-id>/status # running | done | failed: <reason>
|
|
cat ~/mnt/ollie/b/<job-id>/result # assistant reply (when done)
|
|
cat ~/mnt/ollie/b/<job-id>/usage # token counts
|
|
cat ~/mnt/ollie/b/<job-id>/ctxsz # context size
|
|
cat ~/mnt/ollie/b/<job-id>/spec # original spec
|
|
cat ~/mnt/ollie/b/idx # all jobs: id status cwd agent
|
|
```
|
|
|
|
### Remove a job
|
|
|
|
```sh
|
|
rm -r ~/mnt/ollie/b/<job-id> # cancel if running, then remove
|
|
```
|
|
|
|
### Shell wrappers
|
|
|
|
`b/job`, `b/q`, and `b/sched` are shell scripts that wrap the `b/` namespace for one-liners. They are exposed directly via the mounted filesystem:
|
|
|
|
```sh
|
|
$OLLIE/b/job "Summarize this repo" # submit, wait, print result
|
|
echo "what is 2+2?" | $OLLIE/b/q # foreground query via stdin
|
|
$OLLIE/b/sched "Run a background task" # submit and return b/ path
|
|
$OLLIE/b/job -parallel 4 "Write a haiku" # run N times concurrently
|
|
$OLLIE/b/job -backend ollama -model qwen3:8b "Explain this" < main.go
|
|
```
|
|
|
|
`b/q` is a thin wrapper around `b/job`. `b/sched` wraps `b/job -bg` and prints the `b/{id}` path for each submitted job.
|
|
|
|
### AI pipelines
|
|
|
|
Because `b/job` and `b/q` write results to stdout, they compose naturally with Unix pipes. Any tool that reads stdin and writes stdout is a pipeline stage.
|
|
|
|
```sh
|
|
echo "write a haiku about filesystems" | $OLLIE/b/q | wc -w
|
|
cat error.log | $OLLIE/b/q "what is causing this error?" | $OLLIE/b/q "suggest a fix"
|
|
```
|
|
|
|
Multi-stage pipelines can chain LLM calls, shell transforms, and other tools:
|
|
|
|
```sh
|
|
$OLLIE/b/q "list 5 blog post ideas" | grep -v "^$" | head -3 | $OLLIE/b/q "expand the best one"
|
|
```
|
|
|
|
`u/optimize` is a worked example of a pipeline built on `b/q`. It generates N candidate prompts in parallel, then judges them to return the best:
|
|
|
|
```sh
|
|
$OLLIE/u/optimize -n 3 -cm qwen/qwen3-8b -jm anthropic/claude-opus-4-6 "explain recursion"
|
|
```
|
|
|
|
Internally, `u/optimize` calls `b/q -parallel N` for candidate generation and `b/q` again for judging — two LLM stages composed via shell variables, with stderr used for progress and stdout carrying the result. The output can be piped directly into another stage:
|
|
|
|
```sh
|
|
$OLLIE/u/optimize "translate this to Spanish" >[2]/dev/null | $OLLIE/b/q < input.txt
|
|
```
|
|
|
|
## Agents
|
|
|
|
Agent configs live in `a/` and are backed by `~/.config/ollie/agents/`. They're plain JSON files.
|
|
|
|
```sh
|
|
ls ~/mnt/ollie/a/ # list agents
|
|
cat ~/mnt/ollie/a/default.json # read an agent config
|
|
cp ~/mnt/ollie/a/default.json ~/mnt/ollie/a/yolo.json # copy an agent
|
|
mv ~/mnt/ollie/a/old.json ~/mnt/ollie/a/new.json # rename
|
|
rm ~/mnt/ollie/a/scratch.json # delete
|
|
```
|
|
|
|
## Backends
|
|
|
|
```sh
|
|
cat ~/mnt/ollie/backends # list available backends
|
|
```
|
|
|
|
## Help
|
|
|
|
```sh
|
|
cat ~/mnt/ollie/help # show help (from ~/.config/ollie/help.md)
|
|
```
|
|
|
|
## Example shell session
|
|
|
|
```sh
|
|
$ echo "cwd=/home/lkn/src/ollie" > ~/mnt/ollie/s/new
|
|
$ ls ~/mnt/ollie/s/
|
|
new
|
|
1744276689123456789-2b986c
|
|
$ cd ~/mnt/ollie/s/1744276689123456789-2b986c
|
|
$ tail -f chat &
|
|
$ echo "list the go files in /home/lkn/src/ollie" > prompt
|
|
user: list the go files in /home/lkn/src/ollie
|
|
assistant: -> execute_code({"code":"find /home/lkn/src/ollie -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 ~/mnt/ollie/s/1744276689123456789-2b986c ~/mnt/ollie/s/ollie-demo
|
|
$ ls ~/mnt/ollie/s/
|
|
new
|
|
ollie-demo
|
|
```
|