spec is 0666; offset/statewait/systemprompt/models/ctxsz/usage are 0444 explicitly. Remove mode cases for state, backend, model, agent, cwd, params, cwdwait, usagewait, ctxszwait. Update README. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|---|---|---|
| .gitignore | ||
| LICENSE | ||
| README.md | ||
| go.mod | ||
| go.sum | ||
| main.go | ||
| mkfile | ||
| server.go | ||
README.md
olliesrv
A 9P server that exposes 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.
For usage examples, see doc/USAGE.md in the monorepo.
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/)
tools-file.md read: text editing instructions
tools-reasoning.md read: reasoning instructions
tools-memory.md read: memory instructions
tools-subagent.md read: subagent usage
tools-elevate.md read: controlled escape from execute_code sandbox
Prompt files have no automatic effect. They are injected into the system prompt
by the `prime` script (x/prime), typically via preTurn hooks in agent configs.
The base system prompt (SYSTEM_PROMPT.md) is loaded directly by Go at session
creation and is not served via p/. The assembled system prompt is visible at
s/<id>/systemprompt.
s/ dir: sessions, session management scripts, and batch job scripts
new r/w: read: KV template; write: create session or batch job (see below)
idx read: index of all sessions and batch jobs (id, state, cwd, backend/agent — one per line)
sh exec: interactive chat shell
ls exec: list active sessions
kill exec: kill a session by ID
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 s/ path; wrapper around job
cleanup exec: remove all batch jobs with state "done"
Writing to s/new creates either an interactive session or a batch job depending on format:
key=value pairs only (no --- separator) => interactive session
key=value pairs + \n---\n + prompt body => batch job (one-and-done lifecycle)
Interactive session directories (multi-turn, idle<=>running):
<session-id>/ rm -r to kill; mv to rename
spec r/w: KV snapshot of config and current state; write partial KV to mutate
read fields: state, backend, model, agent, cwd, usage, ctxsz, offset,
maxTokens, temperature, frequencyPenalty, presencePenalty
writable fields: backend, model, agent, cwd, maxTokens, temperature,
frequencyPenalty, presencePenalty
(state, usage, ctxsz, offset are read-only; silently ignored on write)
chat read: cumulative conversation history
ctl write: stop | <command>
fifo.in write: queue a prompt for later execution
fifo.out read: pop the next queued prompt
offset read: byte offset in chat immediately after the last user prompt
prompt write: submit a prompt to the agent
statewait read: blocks until state changes; returns new state
usage read: token counts (input, output, requests; [estimated] if not reported by backend)
ctxsz read: estimated context size vs context window
models read: available models from the backend
systemprompt read: fully rendered system prompt for this session
tail exec: exec tail -f chat
Batch job directories (one-and-done: running => done | failed):
<job-id>/ rm -r to cancel and remove
spec read: verbatim spec as submitted to s/new
state read: running | done | failed: <reason>
statewait read: blocks until terminal state; returns final state
result read: assistant reply (populated when done)
usage read: token counts
ctxsz read: context size
log read: raw event log (tool calls, assistant output)
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/)
idx read: tool index (name, description, args — synthesized at read time)
<script> r/w: tool script content
tr/ dir: saved transcripts (read, backed by OLLIE_TRANSCRIPT_PATH or ~/.config/ollie/transcript/)
<timestamp>-chat.md read: transcript saved at session end
tmp/ dir: temporary files (r/w, backed by OLLIE_TMP_PATH or $XDG_DATA_HOME/ollie/tmp/)
<file> r/w: scratch files; supports create, read, write, rm
u/ dir: utility scripts (read, backed by ~/.config/ollie/scripts/u/)
<script> exec: utility script; compositions of s/ primitives
x/ dir: system exec and plugins (read, backed by ~/.config/ollie/scripts/x/)
<plugin> exec: server-invoked plugin (e.g. elevation backends)
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 the Store interface (from core/pkg/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 |
/m |
FlatDirStore |
Store |
/p |
FlatDirStore |
Store |
/sk |
SkillStore |
Store |
/t |
ToolStore |
Store |
/tr |
FlatDirStore |
Store |
/tmp |
FlatDirStore |
Store |
/u |
UtilStore |
Store |
/x |
ExecStore |
Store |
/s |
SessionStore |
Store |
To swap a backing store (e.g. replace /m with an object store), implement the Store interface and wire it in New(). The store interface requires only Stat, List, Open, Create, Delete, 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
mk
Installs olliesrv to $HOME/bin.
Usage
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>.
Sessions
Create
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
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
cat $OLLIE/s/<session-id>/chat # full history snapshot
tail -f $OLLIE/s/<session-id>/chat # follow output as it arrives
Check state
cat $OLLIE/s/<session-id>/state
# idle | thinking | calling: <toolname>
Control
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>, compact, clear, backend, model, models, agents, agent, sessions, cwd, skills, tools, context, usage, history, irw, help. The / prefix is added automatically. Unrecognized input is rejected with an error.
Switch backend, model, or agent
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
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.
Agents
Agent configs live in a/ and are backed by ~/.config/ollie/agents/. They're plain JSON files.
ls $OLLIE/a/ # list agents
cat $OLLIE/a/default.json # read config
cp $OLLIE/a/default.json $OLLIE/a/yolo.json # copy
mv $OLLIE/a/old.json $OLLIE/a/new.json # rename
rm $OLLIE/a/scratch.json # delete
Example shell session
$ 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