From 63a9f0b1d6b3f24e8eff43bca28127d51c377332 Mon Sep 17 00:00:00 2001 From: Ollie Agent Date: Tue, 4 Aug 2026 19:06:29 +0200 Subject: [PATCH] usage: rewrite for o CLI, move raw 9P to reference section --- doc/usage.md | 690 +++++++++++++++++++++++---------------------------- 1 file changed, 309 insertions(+), 381 deletions(-) diff --git a/doc/usage.md b/doc/usage.md index de781d7..4509ccb 100644 --- a/doc/usage.md +++ b/doc/usage.md @@ -9,14 +9,6 @@ olliesrv # start the server (foreground) # Stop by sending SIGTERM or via the supervisor (e.g. rc-service ollie stop) ``` -Mount the 9P namespace locally: - -```sh -ollie-9p mount unix!/tmp/ns.$USER.$DISPLAY/ollie ~/mnt/ollie -cd ~/mnt/ollie -ls session/ -``` - ### TCP listener To also accept connections over TCP (e.g. for remote access): @@ -36,62 +28,154 @@ 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`). -The rendered context window for any session is readable at `session/{id}/agent/{aid}/context` — the exact message array sent to the backend on the last turn. Combined with `session/{id}/agent/{aid}/systemprompt`, this gives full visibility into what the model sees. +--- + +## `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 +``` + +### 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 +``` --- -Examples below use `9p` for interactive use. +## `o tui` — tmux Terminal UI -## One-shot generation +`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: -The `generate` file provides stateless prompt→response without creating a session: - -```sh -echo 'summarize this repo' | ollie-9p rdwr generate -cat main.go | ollie-9p rdwr generate # pipe content as the prompt -echo '{"prompt":"explain recursion"}' | ollie-9p rdwr generate # JSON form +``` +┌──────────────────────────────────┐ +│ o tail | grep │ ← chat stream +│ │ +├──────────────────────────────────┤ +│ o prompt │ ← input REPL +└──────────────────────────────────┘ +Agent state shown in tmux status bar + idle=green calling=orange thinking=blue paused=gray ``` -For programmatic use from any language, write a prompt to the `generate` file and -read back the response. - -## Code completion - -The `complete` file provides fill-in-the-middle code completion: +### Usage ```sh -echo '{"file":"main.go","prefix":"func ","suffix":""}' | ollie-9p rdwr complete +eval $(o env myproject default) +o tui ``` -## Routing +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. -The `route` file selects the best backend+model for a task description: +No polling. Each read blocks until data arrives. The status bar updates on every +state transition via `statewait` — zero CPU when idle. -```sh -echo 'implement login page' | ollie-9p rdwr route -# backend=anthropic model=claude-sonnet-4-20250514 -``` +### tmux tips -Use this to dispatch work to the appropriate tier without hardcoding model names. +- `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-`) -### Raw session access +--- -```sh -9p read session/new # show the spec template -printf 'cwd=%s\n---\nSummarize this.\n' "$PWD" | 9p write session/new -9p read session//cfg # state, backend, model, agent, cwd, params -9p read session//agent/0/chat -9p rm session/ # cancel and remove -``` - -Valid spec keys: `name` (auto-generated if omitted), `cwd` (required), `agent`, -`backend`, `model`. - -### Generation parameters +## Generation parameters Parameters are set in the agent config JSON and can be overridden at runtime by -writing `key=value` lines to `session/{id}/cfg`. Runtime writes take precedence over -the agent config. All are optional; omitted values use the backend's default. +writing `key=value` to the agent config. Runtime overrides take precedence: + +```sh +echo "temperature=0.3" | ollie-9p write session/mysession/agent/0/cfg +``` | Key | Type | Description | |---|---|---| @@ -124,74 +208,25 @@ Example agent config: } ``` -Example runtime override: +## Sandbox -```sh -echo "temperature=0.3" | 9p write session/mysession/agent/0/cfg -echo "topP=0.9" | 9p write session/mysession/agent/0/cfg -``` - -### 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. - -## AI pipelines - -`generate` composes naturally with Unix pipes: - -```sh -echo "write a haiku about filesystems" | ollie-9p rdwr generate | wc -w -cat error.log | ollie-9p rdwr generate # content IS the prompt -``` - -Chain multiple generation steps: - -```sh -echo "list 5 blog post ideas" | ollie-9p rdwr generate | head -3 | ollie-9p rdwr generate -``` - -## Code completion - -The `complete` file provides fill-in-the-middle code completion: - -One copilot session is maintained per working directory. The session ID is derived -deterministically from the cwd, so concurrent completions in the same directory share -a single session and never spawn duplicates. The conversation history is cleared after -each completion to keep context fresh. - -If a session enters a failed state, it is automatically killed and recreated on the -next invocation. - -### Model recommendations - -Completion models should be small and fast — the goal is insertion text, not reasoning. -Good choices for `OLLIE_COMPLETE_MODEL`: - -| Model | Backend | Notes | -|---|---|---| -| `qwen3:4b` | ollama | Use with `/no_think` or a `PARAMETER stop ` Modelfile to disable reasoning | -| `phi-4-mini` | ollama | ~3.8B, strong at code, no thinking mode | -| `llama3.1:8b` | ollama | Straightforward, no chain-of-thought overhead | -| `openai/gpt-4o-mini` | openai | Fast and cheap via OpenRouter | - -Avoid "thinking" models (Qwen3 8B+ with default settings, DeepSeek-R1) — they burn -tokens on internal reasoning chains that add latency without improving completion -quality. +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. +independent parallel subtasks, and persistent concurrent sessions for workflows +that need coordination or long-lived state. ### Subagent delegation -`subagent_spawn` is a tool script that forks N ephemeral agents with a shared prompt, -waits for all to finish, and returns their results concatenated in submission order. It -is the right tool when work can be split into independent subtasks that don't need to -talk to each other. - -The agent invokes it via `shell`: +`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 { @@ -199,13 +234,6 @@ The agent invokes it via `shell`: } ``` -Or from a shell step: - -```sh -subagent_spawn -n 3 "review this diff for security issues" -subagent_spawn -n 1 -agent reviewer -cwd /path/to/repo "check for style violations" -``` - **Flags** | Flag | Description | @@ -217,63 +245,37 @@ subagent_spawn -n 1 -agent reviewer -cwd /path/to/repo "check for style violatio | `-cwd DIR` | Working directory for subagents (default: `pwd`) | | `-keep` | Do not remove sessions after collecting results | -With `-n 1` (the default) the result is just the single agent's output. With `-n > 1` -results are separated by `=== {id} ===` headers. - ### Concurrent sessions -For workflows where multiple long-lived agents work in parallel and hand off to each -other, the mechanism is direct prompt passing via the filesystem. Each agent has a -`prompt` file that accepts writes; writing to it queues a new turn, so a busy agent -will not drop a message. - -**Create named sessions:** +For long-lived parallel agents, create named sessions and pass prompts between +them via the filesystem: ```sh -printf 'name=writer\ncwd=%s\n' "$PWD" | 9p write session/new -printf 'name=reviewer\ncwd=%s\n' "$PWD" | 9p write session/new +printf 'name=writer\ncwd=%s\n' "$PWD" | o write session/new +printf 'name=reviewer\ncwd=%s\n' "$PWD" | o write session/new +o write prompt "Draft a design doc" # with session=writer agent=0 +o tail # read writer's response ``` -**Send a prompt and read the response:** - -```sh -# Queue a prompt on a named session -printf '%s\n' "Draft a design doc for the new cache layer." | 9p write session/writer/agent/0/prompt - -# Wait for the turn to finish (statewait blocks until state changes) -until [ "$(9p read session/writer/agent/0/statewait)" = "idle" ]; do :; done - -# Read only the new output (offset marks the byte position after the user prompt) -offset=$(9p read session/writer/agent/0/offset) -9p read session/writer/agent/0/chat | tail -c +$((offset + 1)) -``` - -**Key files per session:** +Key files per agent: | File | Mode | Description | |---|---|---| -| `prompt` | write | Queue a new turn; writes are enqueued if agent is busy | +| `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` immediately after the last user prompt | +| `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 (if reported by backend) | - -Reading `offset` before writing `prompt`, then extracting `chat[offset:]` after the -turn completes, isolates exactly the model's response for that turn. +| `cost` | read | Cumulative cost in USD | ### Predefined workflows When coordination logic is known in advance, a shell script can drive the entire -workflow. Named sessions make routing unambiguous; `statewait` eliminates polling -races. No external coordinator process is needed — the script is the coordinator. - -The following example implements a write → review → test loop. The developer agent -revises until the reviewer approves, then the tester validates: +workflow using named sessions and `statewait`: ```sh #!/bin/sh @@ -283,10 +285,6 @@ printf 'name=developer\nagent=developer\ncwd=%s\n' "$cwd" | 9p write session/new printf 'name=reviewer\nagent=reviewer\ncwd=%s\n' "$cwd" | 9p write session/new printf 'name=tester\nagent=tester\ncwd=%s\n' "$cwd" | 9p write session/new -# Send a prompt to a named session and return the model's response. -# Opens statewait before writing the prompt so the idle baseline is captured -# before the agent starts processing. offset marks the byte position after the -# user prompt in chat; everything from that position to EOF is the model response. send() { sid=$1; shift exec 3< session/$sid/agent/0/statewait @@ -333,26 +331,145 @@ rm -r session/developer session/reviewer session/tester ``` The agents have no knowledge of each other — the script holds all the routing logic. -A human can intervene at any time by writing directly to any session's `prompt`. -Each agent can use a different `agent=`, `backend=`, or `model=` — set them in the -`session/new` spec lines. +Each agent can use a different `agent=`, `backend=`, or `model=`. -### Self-generating workflows +--- -Because agents have access to `shell`, a session can write and run a workflow -script without any human involvement. Given a task description and knowledge of the -filesystem layout, an agent can decompose the work, create the named sessions, write -the coordination script to a temp file, and execute it — all in a single turn. +## 9P filesystem reference -The architecture documents (`ARCHITECTURE.md`, `USAGE.md`) and tool descriptions -(available via `$OLLIE_TOOLS_PATH`) are the agent's reference. No additional scaffolding is needed. +The 9P namespace is the lowest-level interface to ollie. Everything — sessions, +agents, prompts, tools, state — is a file. The `o` CLI wraps these operations, +but knowing the raw filesystem is useful for scripting, debugging, and +understanding how things work. + +### Mounting + +```sh +ollie-9p mount unix!/tmp/ns.$USER.$DISPLAY/ollie ~/mnt/ollie +cd ~/mnt/ollie +ls session/ +``` + +Or connect remotely: + +```sh +ollie-9p mount tcp!server:9564 ~/mnt/ollie +``` + +### Raw session operations + +```sh +# Create a session +echo "name=worker" > session/new + +# Submit a prompt +echo "fix the bug in main.go" > session/worker/agent/0/prompt + +# Stream the response +cat session/worker/agent/0/chat + +# Check state +cat session/worker/agent/0/state + +# Block until state changes +cat session/worker/agent/0/statewait + +# Read offset (byte position after user prompt) +cat session/worker/agent/0/offset +``` + +### Network transparency + +9P is a network protocol. Mount the agent namespace from any machine and every +session, tool, and config value is accessible as if local: + +```sh +# From another machine: +9p -a 'tcp!server:5640' read session/worker/agent/0/state +``` + +No SSH tunneling, no port forwarding, no API gateway. + +### Shell composability + +Because operations are file I/O, they compose with the full Unix toolkit: + +```sh +# Fan out a prompt to all agents +for s in session/*/agent/*/prompt; do echo "run tests" > "$s"; done + +# Wait for all agents to finish +for s in session/*/agent/*/statewait; do cat "$s" > /dev/null; done + +# Grep all agent plans +grep -r "TODO" session/*/plan + +# Monitor costs +cat session/*/agent/*/cost + +# Strip block markup from chat output +cat session/{id}/agent/{aid}/chat | grep -vE '^\[\[\[.*' +``` + +Pipe agents into `awk`. Filter with `grep`. Schedule with `cron`. Orchestrate +with a 10-line shell script instead of a framework. + +### Plan 9 patterns + +- **`/net/dns` pattern** — `/complete`, `/generate`, `/route` are stateless + per-fid: write a request, read back the result +- **`ctl` files** — control operations (stop, kill, rename, compact) are writes + to a control file, not method calls +- **Blocking reads** — `statewait` blocks until state changes, replacing event + subscriptions with a simple `cat` +- **Multiplexing** — multiple clients mount the same server simultaneously, + each with an independent view through per-fid state + +### `/generate` — one-shot LLM + +```sh +echo 'summarize this repo' | ollie-9p rdwr generate +cat main.go | ollie-9p rdwr generate +echo '{"prompt":"explain recursion"}' | ollie-9p rdwr generate +``` + +### `/complete` — code completion + +Fill-in-the-middle: + +```sh +echo '{"file":"main.go","prefix":"func ","suffix":""}' | ollie-9p rdwr complete +``` + +### `/route` — task routing + +```sh +echo 'implement login page' | ollie-9p rdwr route +# backend=anthropic model=claude-sonnet-4-20250514 +``` + +### Key files per agent + +| File | Mode | Description | +|---|---|---| +| `prompt` | write | Queue a new turn | +| `statewait` | read (blocking) | Blocks until state changes | +| `state` | read | Current state | +| `chat` | read | Full conversation history | +| `context` | read | Full message history as JSONL | +| `offset` | read | Byte position after 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 | + +--- ## Store federation -`OLLIE_TOOLS_PATH` is an ordinary filesystem path. Because -`olliesrv` speaks 9P and any remote instance can be mounted locally via `9pfuse`, this -var can point at a remote directory with no code changes — the OS handles the proxying -transparently. +`OLLIE_TOOLS_PATH` is an ordinary filesystem path. Because the server speaks 9P +and any remote instance can be mounted locally, this var can point at a remote +directory with no code changes — the OS handles the proxying transparently. ### Centralized tool distribution @@ -364,42 +481,13 @@ ollie-9p mount toolserver:9564 ~/mnt/toolserver export OLLIE_TOOLS_PATH=~/mnt/toolserver/t ``` -Every agent on every host now uses the same tool scripts. Update tools in one place; -all clients pick up the change immediately. - Set it in `~/.config/ollie/env` for persistence: ``` OLLIE_TOOLS_PATH=~/mnt/toolserver/t ``` -- Markdown rendering of assistant responses -- 9P filesystem browser (navigate sessions, agents, tools, skills) -- Two built-in themes: `midnight` (dark) and `acme` (Plan 9-inspired light); choice - persisted to `localStorage` - -## System prompt override - -The default system prompt is compiled into `olliesrv`. To replace it — for example, -when using a model that needs its own identity preamble — set the `systemPrompt` -field in an agent config JSON to a file path: - -```json -{ - "systemPrompt": "~/.config/ollie/prompts/SYSTEM_PROMPT_QWEN.md", - "prompt": ["$OLLIE_CFG_PATH/prompts/agent-coding.md"], - "temperature": 0.7 -} -``` - -The file is read at session creation time. If it doesn't exist, the embedded default -is used. The override replaces only the base system prompt layer; the operational -model (9P documentation), environment block, and agent-specific prompt -commands are still appended on top. - -Model-specific prompts live at `~/.config/ollie/prompts/SYSTEM_PROMPT_*.md`. -Create an agent config per model family and point `systemPrompt` at the -appropriate file. +--- ## Tool discovery @@ -415,7 +503,7 @@ Each tool has a `.meta` JSON sidecar file alongside the executable: ```json { "description": "Short description (used in system prompt listing).", - "prompt": "## my_tool\n\nFull documentation shown when tool is loaded.\n\n**Args**: ...", + "prompt": "## my_tool\n\nFull documentation shown when tool is loaded.", "args": {"type":"object","required":["path"],"properties":{"path":{"type":"string","description":"File path"}}}, "tier": "cold", "readOnly": true @@ -429,210 +517,50 @@ Each tool has a `.meta` JSON sidecar file alongside the executable: | `args` | JSON Schema for tool input parameters | | `tier` | Result cacheability: `cold`, `warm`, or `hot` (default: hot) | | `readOnly` | Safe for parallel execution with other read tools | -| `cmd` | Executable path or name; overrides co-located binary (see WRITING_TOOLS.md) | +| `cmd` | Executable path or name override | | `sudo` | Requires root privileges; implies elevation | -| `variants` | Array of conditional definitions for heterogeneous hosts (see WRITING_TOOLS.md) | +| `variants` | Conditional definitions for heterogeneous hosts | ### Runtime access -The full prompt for any tool is available on-demand via the `/tools` endpoint: +The full prompt for any tool is available on-demand: ```sh echo 'file_edit' | 9p rdwr ollie/tools # returns file_edit's full documentation ``` -The system prompt only includes names and one-line descriptions to save context. -Agents query `/tools` when they need detailed usage for a specific tool. - ### Adding a tool Drop an executable and its `.meta` file in `$OLLIE_TOOLS_PATH`. The next session created will pick it up automatically. No restart required. -### Host-conditional variants +See [doc/writing-tools.md](writing-tools.md) for the full specification including +host-conditional variants and privileged tools. -Tools can declare multiple **variants** gated by match conditions. The first -variant where all conditions pass determines the tool's schema, documentation, -and executable. If no variant matches, the tool is hidden from the registry. +--- + +## System prompt override + +The default system prompt is compiled into `olliesrv`. To replace it, set the +`systemPrompt` field in an agent config JSON to a file path: ```json { - "description": "Read system logs.", - "variants": [ - { - "match": {"binary": "journalctl"}, - "cmd": "system_logs_journald", - "args": {"type":"object","properties":{"unit":{"type":"string"},"since":{"type":"string"}}} - }, - { - "match": {"file": "/var/log/syslog"}, - "cmd": "system_logs_syslog", - "args": {"type":"object","properties":{"lines":{"type":"string"}}} - } - ] + "systemPrompt": "~/.config/ollie/prompts/SYSTEM_PROMPT_QWEN.md", + "prompt": ["$OLLIE_CFG_PATH/prompts/agent-coding.md"], + "temperature": 0.7 } ``` -Supported match keys: `binary`, `file`, `os`, `arch`, `env`, `nenv`. All -conditions in a match must be true (AND). Values can be a string or array -(OR). See [WRITING_TOOLS.md](WRITING_TOOLS.md) for the full specification. +## Viewing model context -### Privileged tools: `sudo` - -Tools that need root privileges declare `"sudo": true` in their `.meta`. The -dispatch chain becomes a two-gate sequence: elevation approval (escape sandbox) -→ credential prompt → `sudo -S` execution. The tool itself has no knowledge of -sudo — the privilege wrapping is entirely in the dispatch layer. - -```json -{ - "description": "Read system logs (dmesg).", - "sudo": true, - "cmd": "system_logs_dmesg", - "args": {"type":"object","properties":{"lines":{"type":"string"}}}, - "readOnly": true -} -``` - -Credentials are prompted via `kdialog`/`zenity` on desktop, or forwarded over -SSH for remote execution. See [WRITING_TOOLS.md](WRITING_TOOLS.md) for details. +The rendered context window for any session is readable at +`session/{id}/agent/{aid}/context` — the exact message array sent to the +backend on the last turn. Combined with `session/{id}/agent/{aid}/systemprompt`, +this gives full visibility into what the model sees. ## Alternative front-ends -The 9P filesystem is the lowest level interface to ollie. All interfaces are wrappers around filesystem operations. Front-ends: - -- **[ellie](../el/)** — `ellie.el` is the Emacs front-end, just wire it into your `init.el` and `M-x ellie` -- **KDE** — plasmoid, standalone GUI, Kate plugin, KRunner, system tray (see `kde/`) -- **`o`** — terminal CLI and tmux TUI (see below) - -See each repo's README for installation and usage. - ---- - -## `o` — Terminal CLI - -`o` is a multi-call shell script that wraps `ollie-9p` into a human-friendly interface. Installed to `~/bin/o` by `just install-data`. - -### Quick start - -```sh -# Set context first: -export session=myproject agent=default -# or use the env helper: -eval $(o env myproject default) - -# Then interact: -o tail # stream chat output -o prompt # interactive prompt REPL -o stop # interrupt the agent -o kill # kill the session -``` - -### Commands - -| Command | Description | -|---|---| -| `o read ` | Read a file from the namespace | -| `o write [data]` | Write to a file (args or stdin) | -| `o readloop ` | Read in a loop (re-reads after 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 prompt` | Interactive multi-line prompt REPL | -| `o tail` | Stream chat output | -| `o tui` | Launch tmux TUI (see below) | -| `o env [session] [agent]` | Print export statements for context | - -### Path resolution - -Paths are resolved based on context (`$session`, `$agent`): -- **Root paths** (`session/*`, `agents`, `models`, `help`, `ctl`) → used as-is -- **Agent files** (`chat`, `state`, `prompt`, etc.) → require `$session` + `$agent` -- **Session files** (`env`, `ctl`, `name`, etc.) → require `$session` only - -```sh -o read models # root-level, no context needed -o ls session # list all sessions -export session=foo agent=bar -o read chat # → session/foo/agent/bar/chat -o readloop statewait # blocks until agent state changes -``` - -### Prompt REPL slash commands - -Inside `o prompt`, slash commands control the agent without sending a prompt: - -``` -/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 -/help — list commands -``` - -Multi-line input: type lines, press Enter on a blank line to send. Ctrl+D exits. - -### Environment - -| Variable | Purpose | -|---|---| -| `session` | Active session name | -| `agent` | Active agent name | - -Context must be set explicitly via `export` or `eval $(o env ...)`. Commands that require context will fail fast with a clear error message if not set. - ---- - -## `o tui` — tmux Terminal UI - -`o tui` composes a complete agent interaction environment from four tmux panes, each running a blocking 9P read or a REPL. The layout exploits ollie's streaming file semantics: `chat` blocks until new tokens arrive, `statewait` blocks until the agent state changes. - -### Layout - -``` -┌─────────────────────────────┬───────────────┐ -│ │ statewait │ -│ chat stream │ (reactive — │ -│ (tokens appear live) │ updates on │ -│ │ state │ -│ │ change) │ -│ ├───────────────┤ -│ │ $ shell │ -│ │ (session + │ -│ │ agent │ -│ │ exported) │ -├─────────────────────────────┴───────────────┤ -│ > prompt REPL (multi-line, /commands) │ -└─────────────────────────────────────────────┘ -``` - -### How it works - -```sh -export session=myproject agent=default -o tui -``` - -1. Requires `$session` and `$agent` to be set (fails with error if missing). -2. Creates a tmux session named `o-`. -3. Splits into 4 panes, each with `session` and `agent` exported: - - **Pane 0** (left, large): `o tail` — blocking read on `chat`, streams tokens as they arrive. - - **Pane 1** (top-right): `o readloop statewait` — blocks until agent state changes (`idle` → `thinking` → `calling: tool` → `idle`), then prints and blocks again. - - **Pane 2** (bottom-right): bare shell with context set — run `o stop`, `o read cost`, `o ctl compact`, etc. - - **Pane 3** (bottom, full-width): `o prompt` — multi-line REPL with slash commands. -4. Focuses the prompt pane and attaches. - -### Key insight - -No polling. Each pane performs a **blocking read** on a 9P file that only returns data when something changes. The server wakes the reader (via `sync.Cond`) when new tokens/state arrive. This gives reactive, instant updates with zero CPU overhead when idle. - -### tmux tips within `o tui` - -- `Ctrl+b ;` — toggle between last two panes (fast prompt ↔ shell switch) -- `Ctrl+b z` — zoom any pane to full screen (great for long chat output) -- `Ctrl+b [` — scroll mode (navigate chat history with arrow keys, `q` exits) -- `Ctrl+b d` — detach (session keeps running, reattach with `tmux attach -t o-`) +- **[ellie](../el/)** — Emacs front-end (`ellie.el`) +- **KDE** — plasmoid, standalone GUI, Kate plugin, KRunner, system tray +- **Web** — built-in HTTP server (see `olliesrv -web`) \ No newline at end of file