usage: rewrite for o CLI, move raw 9P to reference section
This commit is contained in:
parent
d314c7d4ee
commit
63a9f0b1d6
690
doc/usage.md
690
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_<TAG>_LOG=debug` where `<TAG>` 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 <path>` | Read any file in the namespace |
|
||||
| `o write <path> [data]` | Write to any file |
|
||||
| `o readloop <path>` | Read in a loop (re-reads after each return) |
|
||||
| `o ls [path]` | List namespace entries |
|
||||
| `o ctl <cmd>` | 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-<session>`.
|
||||
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-<session>`)
|
||||
|
||||
### 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/<session-id>/cfg # state, backend, model, agent, cwd, params
|
||||
9p read session/<session-id>/agent/0/chat
|
||||
9p rm session/<session-id> # 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/<name>.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 <think>` 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/<name>.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: <reason>` |
|
||||
| `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 <path>` | Read a file from the namespace |
|
||||
| `o write <path> [data]` | Write to a file (args or stdin) |
|
||||
| `o readloop <path>` | Read in a loop (re-reads after return) |
|
||||
| `o ls [path]` | List namespace entries |
|
||||
| `o ctl <cmd>` | 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-<session>`.
|
||||
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-<session>`)
|
||||
- **[ellie](../el/)** — Emacs front-end (`ellie.el`)
|
||||
- **KDE** — plasmoid, standalone GUI, Kate plugin, KRunner, system tray
|
||||
- **Web** — built-in HTTP server (see `olliesrv -web`)
|
||||
Loading…
Reference in New Issue