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.
Go to file
Levi Neely d8e0b9b5ba 9p: add /complete file for stateless code completion
Follows the Plan 9 /net/dns pattern: per-fid request-response.
Write JSON request (or plain prefix), write blocks during LLM call,
read returns the completion result. Per-fid state means concurrent
clients get independent request/response pairs.

Also includes the Complete method, findOrCreateCopilot, and noise
stripping helpers in the session package.
2026-07-18 16:00:54 +02:00
cmd/ollie-9p-mount fs: centralized permission registry; FUSE mutating operations 2026-05-21 11:50:34 +02:00
fs strip 9P namespace to sessions only 2026-07-17 23:08:29 +02:00
prompts 9p: eliminate expensive API calls during readdir/stat 2026-07-18 01:31:06 +02:00
server 9p: add /complete file for stateless code completion 2026-07-18 16:00:54 +02:00
session 9p: add /complete file for stateless code completion 2026-07-18 16:00:54 +02:00
skills add prompts, skills, tools (moved from monorepo root) 2026-07-07 12:07:35 +02:00
tools strip 9P namespace to sessions only 2026-07-17 23:08:29 +02:00
.gitignore gitignore: add build artifact 2026-05-19 15:54:24 +02:00
LICENSE Initial commit: olliesrv 9P server for ollie agent sessions 2026-04-10 01:46:48 +02:00
Makefile add GNU Makefile mirroring mkfile 2026-06-16 06:50:55 +02:00
README.md strip 9P namespace to sessions only 2026-07-17 23:08:29 +02:00
go.mod session: subscribe to event bus for chat log, drop handler from Submit 2026-05-20 16:45:37 +02:00
go.sum session: subscribe to event bus for chat log, drop handler from Submit 2026-05-20 16:45:37 +02:00
main.go 9p: add /complete file for stateless code completion 2026-07-18 16:00:54 +02:00
main_test.go 9p: eliminate expensive API calls during readdir/stat 2026-07-18 01:31:06 +02:00
mkfile add prompts, skills, tools (moved from monorepo root) 2026-07-07 12:07:35 +02:00

README.md

olliesrv

Ollie starts from a simple, slightly silly question: What might an AI agent look like in Plan 9? 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 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) 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 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

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>.

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.
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.

# 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.

# Connect to local server:
ollie-httpgw

# Connect to remote server:
ollie-httpgw -net tcp -addr remotehost:9564

See 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:

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:

olliesrv mount localhost:9564 ~/mnt/container-ollie
# or
ollie-httpgw -net tcp -addr localhost:9564

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>, 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

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.

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