# Usage ## Server ### Start and stop ```sh olliesrv # start the server (foreground) # Stop by sending SIGTERM or via the supervisor (e.g. rc-service ollie stop) ``` ### TCP listener To also accept connections over TCP (e.g. for remote access): ```sh olliesrv -tcp :9564 ``` ### Debug logging Log level is controlled by `OLLIE_LOG` (default: `warn`). Set to `debug` for verbose output: ```sh OLLIE_LOG=debug olliesrv ``` Valid levels: `debug`, `info`, `warn`, `error`. Per-subsystem overrides follow the pattern `OLLIE__LOG=debug` where `` matches the logger's tag (e.g. `OLLIE_SESSION_LOG`). --- ## `o` — Terminal CLI `o` is a multi-call shell script that wraps the 9P namespace into a human-friendly interface. Installed to `~/bin/o` by `just install-data`. ### Quick start ```sh eval $(o env myproject default) # set session + agent context o prompt # interactive prompt REPL ``` In another terminal: ```sh eval $(o env myproject default) o tail # stream chat output ``` ### Commands | Command | Description | |---|---| | `o prompt` | Interactive multi-line prompt REPL | | `o tail` | Stream chat output | | `o read ` | Read any file in the namespace | | `o write [data]` | Write to any file | | `o readloop ` | Read in a loop (re-reads after each return) | | `o ls [path]` | List namespace entries | | `o ctl ` | Send raw control command to agent | | `o stop` | Interrupt the running agent | | `o kill` | Kill the active session | | `o tui` | Launch tmux TUI | | `o env [session] [agent]` | Print export statements for context | ### Context Context is set via `$session` and `$agent` environment variables: ```sh eval $(o env myproject default) # export session=myproject agent=default # Now every o command addresses the right agent automatically: o read state # → session/myproject/agent/default/state o readloop statewait ``` ### Path resolution Paths are slash-delimited 9P namespace paths. The `o` CLI resolves them based on context: | Path type | Example | Resolves to | |---|---|---| | Root-level | `o read models` | `models` (no context needed) | | Agent file | `o read state` | `session/$session/agent/$agent/state` | | Session file | `o read env` | `session/$session/env` | | Full path | `o read session/foo/agent/bar/chat` | Used as-is | Root-level paths (`session/`, `agents`, `models`, `help`, `ctl`, `tools`) and absolute paths (starting with `session/`) bypass context resolution entirely. Agent-level files (`chat`, `state`, `prompt`, `statewait`, `cfg`, `plan`, `log`, `cost`, `offset`, `context`, `systemprompt`) require both `$session` and `$agent`. Session-level files (`env`, `ctl`, `id`, `name`, `paused`) require only `$session`. ### Prompt REPL `o prompt` is an interactive multi-line REPL with readline editing: ``` $ o prompt [myproject/default] Type . on a blank line to send, Ctrl+D to exit. Slash commands: /stop /compact /kill /clear /model /backend /cwd /help > write a fibonacci function in python and include a test case . ``` Type your prompt across multiple lines, then send with `.` on its own line. Slash commands control the agent without sending a prompt: | Command | Description | |---|---| | `/stop` | Interrupt running agent | | `/compact` | Compact context window | | `/kill` | Kill the session (exits REPL) | | `/clear` | Clear history | | `/model X` | Switch model | | `/backend X` | Switch backend | | `/cwd X` | Change working directory | | `/quit` or `/q` | Kill the tmux TUI | | `/help` | List commands | ### One-shot generation No session or agent needed — `o generate` sends a prompt and returns a response: ```sh o generate "explain monads in one sentence" cat error.log | o generate # pipe content as the prompt echo "write a haiku about filesystems" | o generate | wc -w ``` Chain multiple steps: ```sh echo "list 5 blog post ideas" | o generate | head -3 | o generate ``` --- ## `o tui` — tmux Terminal UI `o tui` composes `o tail` and `o prompt` into a single tmux layout — two shell commands in two panes, with agent state in the tmux status bar: ``` ┌──────────────────────────────────┐ │ o tail | grep │ ← chat stream │ │ ├──────────────────────────────────┤ │ o prompt │ ← input REPL └──────────────────────────────────┘ Agent state shown in tmux status bar idle=green calling=orange thinking=blue paused=gray ``` ### Usage ```sh eval $(o env myproject default) o tui ``` 1. Requires `$session` and `$agent` to be set. 2. Creates a tmux session named `o-`. 3. Two panes: - **Pane 0** (top, 80%): `o tail | grep` — streams chat output, filters block markers. - **Pane 1** (bottom, 20%): `o prompt` — multi-line REPL with slash commands. 4. A background loop reads `statewait` and updates the tmux status bar instantly. 5. Focuses the prompt pane and attaches. No polling. Each read blocks until data arrives. The status bar updates on every state transition via `statewait` — zero CPU when idle. ### tmux tips - `Ctrl+b ;` — toggle between last two panes (fast prompt ↔ tail switch) - `Ctrl+b z` — zoom any pane to full screen - `Ctrl+b [` — scroll mode (arrow keys, `q` to exit) - `Ctrl+b d` — detach (session keeps running, reattach with `tmux attach -t o-`) --- ## Generation parameters Parameters are set in the agent config JSON and can be overridden at runtime by writing `key=value` to the agent config. Runtime overrides take precedence: ```sh echo "temperature=0.3" | ollie-9p write session/mysession/agent/{aname}/cfg ``` | Key | Type | Description | |---|---|---| | `maxTokens` | int | Max output tokens (0 = no limit) | | `maxCompletionTokens` | int | OpenAI o-series alternative to maxTokens | | `temperature` | float | Sampling temperature | | `topP` | float | Nucleus sampling threshold | | `topK` | int | Top-K sampling (Anthropic, Ollama) | | `minP` | float | Min-P sampling (Ollama) | | `topA` | float | Top-A sampling (Ollama) | | `frequencyPenalty` | float | Frequency penalty | | `presencePenalty` | float | Presence penalty | | `repetitionPenalty` | float | Repetition penalty (Ollama) | | `reasoning` | int | Thinking budget in tokens (Anthropic); 0 = disabled | | `reasoningEffort` | string | `low`, `medium`, `high` (OpenAI o-series) | | `includeReasoning` | bool | Include reasoning in response (OpenRouter) | | `responseFormat` | string | `json_object`, `text`, etc. (OpenAI) | | `stop` | string | Comma-separated stop sequences | | `verbosity` | string | Output detail level (internal, not sent to API) | Example agent config: ```json { "prompt": "You are a helpful assistant.", "temperature": 0.7, "topP": 0.95, "reasoning": 10000, "stop": ["\n\n"] } ``` ## Sandbox Every `shell` invocation (and promoted tool call) runs inside a Landlock sandbox configured by `~/.config/ollie/sandbox/.yaml`. The sandbox wraps each invocation as a new command, so config changes (e.g. granting access to an additional directory) take effect on the next call without restarting the server or session. ## Multi-agent workflows Two patterns cover most multi-agent use cases: ephemeral subagent delegation for independent parallel subtasks, and persistent concurrent sessions for workflows that need coordination or long-lived state. ### Subagent delegation `subagent_spawn` forks N ephemeral agents with a shared prompt, waits for all to finish, and returns their results concatenated. Use it when work splits into independent subtasks: ```json { "steps": [{"tool": "subagent_spawn", "args": "-n 3 summarize this module"}] } ``` **Flags** | Flag | Description | |---|---| | `-n N` | Number of subagents to spawn (default: 1) | | `-agent A` | Agent config for each subagent | | `-backend B` | Backend override | | `-model M` | Model override | | `-cwd DIR` | Working directory for subagents (default: `pwd`) | | `-keep` | Do not remove sessions after collecting results | ### Concurrent sessions For long-lived parallel agents, create named sessions and pass prompts between them via the filesystem: ```sh o write session/new "name=writer cwd=$PWD" o write session/new "name=reviewer cwd=$PWD" agent=$(o ls session/writer/agent | grep -v '^new$') o write "session/writer/agent/$agent/prompt" "Draft a design doc" o read "session/writer/agent/$agent/chat" # read writer's response ``` Key files per agent: | File | Mode | Description | |---|---|---| | `prompt` | write | Queue a new turn; enqueued if agent is busy | | `statewait` | read (blocking) | Blocks until state changes; returns new state | | `state` | read | Current state: `idle`, `running`, `failed: ` | | `chat` | read | Full conversation history | | `context` | read | Full message history as JSONL | | `offset` | read | Byte position in `chat` after the last user prompt | | `plan` | r/w | Scratch space for agent planning | | `prompt.prev` | read | The last submitted prompt | | `env` | read | Session environment variables | | `cost` | read | Cumulative cost in USD | ### Predefined workflows When coordination logic is known in advance, a shell script can drive the entire workflow using named sessions and `statewait`: ```sh #!/bin/sh cwd=${1:?usage: $0 } # Create named sessions with agent profiles o write session/new "name=developer agent=developer cwd=$cwd" o write session/new "name=reviewer agent=reviewer cwd=$cwd" o write session/new "name=tester agent=tester cwd=$cwd" # Discover agent names (first agent in each session) agent_developer=$(o ls session/developer/agent | grep -v '^new$') agent_reviewer=$(o ls session/reviewer/agent | grep -v '^new$') agent_tester=$(o ls session/tester/agent | grep -v '^new$') send() { sid=$1; shift aname=$1; shift # Open statewait, send prompt, wait for completion, read response exec 3< <(o readloop "session/$sid/agent/$aname/statewait" &) sleep 0.1 o write "session/$sid/agent/$aname/prompt" "$*" until o read "session/$sid/agent/$aname/state" | grep -q idle; do sleep 0.5; done exec 3<&- offset=$(o read "session/$sid/agent/$aname/offset" | tr -d '[:space:]') o read "session/$sid/agent/$aname/chat" | tail -c +$((offset + 1)) } reply=$(send developer "$agent_developer" "Implement a function that parses a JSON config file.") while true; do review=$(send reviewer "$agent_reviewer" "Review the following code. Reply LGTM if ready, or PTAL with feedback. $reply") if ! printf '%s' "$review" | grep -qi "LGTM"; then reply=$(send developer "$agent_developer" "Revise based on this feedback: $review") continue fi result=$(send tester "$agent_tester" "Test the following code. Reply Approved if all tests pass, or Rejected with details. $reply") if printf '%s' "$result" | grep -qi "Approved"; then printf '%s\n' "$reply" break fi reply=$(send developer "$agent_developer" "Fix the following test failures: $result") done o kill developer o kill reviewer o kill tester ``` The script initializes the routing, but agents can discover other sessions via `session/idx` and write to their `prompt` files directly — coordination is a shared filesystem, not a master-slave topology. Each agent can use a different `agent=`, `backend=`, or `model=`. --- > **For implementers**: The raw 9P filesystem reference, store federation, and > tool discovery internals are documented in [doc/whitepaper.md](whitepaper.md). ## Viewing model context The rendered context window for any session is readable at `session/{sname}/agent/{aname}/context` — the exact message array sent to the backend on the last turn. Combined with `session/{sname}/agent/{aname}/systemprompt`, this gives full visibility into what the model sees. ## Alternative front-ends - **[ellie](../el/)** — Emacs front-end (`ellie.el`) - **KDE** — plasmoid, standalone GUI, Kate plugin, KRunner, system tray - **Web** — built-in HTTP server (see `olliesrv -web`)