docs: remove all D-Bus references, add Phase 13 to EVOLUTION
- Delete data/prompts/session-dbus.md (obsolete operational model) - AGENTS.md: remove dbus/ from layout, single integration surface - ARCHITECTURE.md: remove D-Bus adapter section, embedded adapter, diagrams - EVOLUTION.md: add Phase 13 (9P streaming, D-Bus removal), fix principles, add D-Bus to dead ends table - USAGE.md: remove D-Bus reference - session-9p.md: document chat (streaming) and log (64KB window) files
This commit is contained in:
parent
b28a56e89a
commit
5911315408
|
|
@ -6,3 +6,4 @@
|
|||
.claude
|
||||
.kiro
|
||||
__pycache__/
|
||||
olliesrv
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ ollie/ ← you are here
|
|||
├── toolsrv/ (Go) Tool server, registry, sandboxed execution
|
||||
├── session/ (Go) Session lifecycle, config, persistence
|
||||
├── fs/session/ (Go) 9P filesystem tree
|
||||
├── dbus/ (Go) D-Bus adapter
|
||||
├── cmd/ (Go) Binaries (olliesrv, ollie-9p, ollie-remote)
|
||||
├── tools/ (Go) Tool implementations:
|
||||
│ ├── builtin/ Built-in handlers (shell, reasoning, tool/skill registry)
|
||||
|
|
@ -75,13 +74,12 @@ cd kde && ./test-e2e.sh
|
|||
- Tool scripts: emit structured output (`STATUS=ok`, `STATUS=error`). Image/LSP tools return JSON content blocks.
|
||||
- Prompts: markdown, concise, example-driven. Follow the pattern in existing `tools-*.md` files.
|
||||
## Architecture (key concepts)
|
||||
1. **Two integration surfaces**: 9P filesystem (sessions at `session/{id}/agent/{aid}/`) and D-Bus (`org.ollie.SessionManager`). Tools, skills, memory on physical filesystem via env vars.
|
||||
1. **One integration surface: 9P filesystem (sessions at `session/{id}/agent/{aid}/`). Tools, skills, memory on physical filesystem via env vars.
|
||||
2. **Agent loop** (`agent/loop.go`): Streaming LLM call → parse tool calls → dispatch → loop until no more tool calls or max steps.
|
||||
3. **Tool dispatch** (`toolsrv/`): `shell` is built-in. All others are external scripts resolved from `OLLIE_TOOLS_PATH`.
|
||||
4. **Sandbox** (`sandbox/`): Landlock-based. Config in `sandbox/*.yaml` defines filesystem access per profile.
|
||||
5. **Backends** (`backend/`): Ollama, OpenAI-compatible, Anthropic, Copilot, Kiro, Gemini, CodeWhisperer. Selectable per-session.
|
||||
6. **Prompts assembled at runtime**: Agent JSON `prompt` array specifies which prompt files to concatenate. Static prompt files can be included directly; the base system prompt is embedded in the binary and always prepended.
|
||||
7. **D-Bus** embedded in `olliesrv`: `org.ollie.SessionManager` exposed by the same binary as the 9P server.
|
||||
## Key Files
|
||||
| What | Where |
|
||||
|------|-------|
|
||||
|
|
@ -93,8 +91,6 @@ cd kde && ./test-e2e.sh
|
|||
| Session management | `session/session.go` |
|
||||
| System prompt template | Embedded in binary |
|
||||
| Agent configs | `agents/*.json` |
|
||||
| D-Bus adapter | `dbus/` |
|
||||
| KDE D-Bus client | `kde/plasmoid/plugin/olliedbusclient.cpp` |
|
||||
| Standalone GUI | `kde/gui/` |
|
||||
| Kate plugin | `kde/kate/` |
|
||||
## Environment
|
||||
|
|
|
|||
|
|
@ -65,7 +65,8 @@ Sessions live at `session/`. Each session is a directory; all operations are 9P
|
|||
| File | Mode | Purpose |
|
||||
|---|---|---|
|
||||
| `cfg` | r/w | Session config: backend, model, agent, cwd, params (key=value). Write key=value to change. |
|
||||
| `chat` | read | Full cumulative chat history (tail-able) |
|
||||
| `log` | read | Last 64KB of chat history (sliding window, tail-able) |
|
||||
| `chat` | read | Streaming: blocks until new output arrives, delivers tokens |
|
||||
| `context` | read | Rendered context window |
|
||||
| `cost` | read | Estimated cost |
|
||||
| `ctl` | write | Control commands (see below) |
|
||||
|
|
@ -126,7 +127,7 @@ echo "temperature=0.7" | ollie-9p write session/{id}/cfg
|
|||
|
||||
# Read most recent response
|
||||
offset=$(ollie-9p read session/{id}/offset)
|
||||
ollie-9p read session/{id}/chat | tail -c +$((offset + 1))
|
||||
ollie-9p read session/{id}/agent/{aid}/log | tail -c +$((offset + 1))
|
||||
|
||||
# Read current state
|
||||
ollie-9p read session/{id}/state
|
||||
|
|
|
|||
|
|
@ -1,158 +0,0 @@
|
|||
# D-Bus Operational Model
|
||||
|
||||
Fallback interface when 9P is unavailable. If the 9P filesystem is mounted, use it instead.
|
||||
|
||||
The `org.ollie.SessionManager` D-Bus service runs on the session bus. Your session ID is `${OLLIE_SESSION_ID}`.
|
||||
|
||||
**Service coordinates:**
|
||||
- Service: `org.ollie.SessionManager`
|
||||
- Object: `/org/ollie/SessionManager`
|
||||
- Interface: `org.ollie.SessionManager`
|
||||
|
||||
**Data directories** (local filesystem, read-only to the agent):
|
||||
- `~/.config/ollie/prompts/` — Prompt templates
|
||||
- `~/.config/ollie/skills/` — Domain-specific knowledge files
|
||||
- `~/.config/ollie/tools/` — Tool scripts
|
||||
- `~/.config/ollie/memory/` — Persistent memory files
|
||||
- `~/.config/ollie/agents/` — Agent definitions (JSON)
|
||||
|
||||
**Peer Agent Discovery**: Use `ListSessions` to discover other active sessions:
|
||||
|
||||
```sh
|
||||
dbus-send --session --dest=org.ollie.SessionManager --type=method_call --print-reply \
|
||||
/org/ollie/SessionManager org.ollie.SessionManager.ListSessions
|
||||
```
|
||||
|
||||
Each entry: `id\tstate\tmodel\tagent`
|
||||
|
||||
**Inter-Agent Communication**: Use `PeerSubmit` to send a prompt to another session. Always provide a return path (e.g., [message from $OLLIE_SESSION_ID]).
|
||||
|
||||
## Methods
|
||||
|
||||
All session operations go through the D-Bus interface.
|
||||
|
||||
### Session Lifecycle
|
||||
|
||||
| Method | Args | Returns | Purpose |
|
||||
|---|---|---|---|
|
||||
| `CreateSession` | cwd, backend, model, agent, system_prompt, remote | session_id | Create a new session |
|
||||
| `ListSessions` | — | array of "id\tstate\tmodel\tagent" | Discover sessions |
|
||||
| `KillSession` | session_id | bool | Terminate a session |
|
||||
| `RenameSession` | session_id, new_name | bool | Rename a session |
|
||||
|
||||
### Agent Interaction
|
||||
|
||||
| Method | Args | Returns | Purpose |
|
||||
|---|---|---|---|
|
||||
| `Submit` | session_id, prompt | bool | Submit a prompt to an agent |
|
||||
| `Interrupt` | session_id | bool | Interrupt current agent turn |
|
||||
| `Compact` | session_id | bool | Compact conversation history |
|
||||
| `ClearContext` | session_id | bool | Clear conversation history |
|
||||
|
||||
### Session State
|
||||
|
||||
| Method | Args | Returns | Purpose |
|
||||
|---|---|---|---|
|
||||
| `GetState` | session_id | state | Current state: `idle`, `thinking`, `calling: <tool>` |
|
||||
| `GetChat` | session_id, offset | text, new_offset | Read chat output from byte offset |
|
||||
| `GetConfig` | session_id | key=value text | Read session config (model, backend, agent, cwd, state) |
|
||||
| `SetConfig` | session_id, key, value | bool | Change a config setting (model, cwd) |
|
||||
| `GetContext` | session_id | JSONL | Full conversation context as JSONL messages |
|
||||
| `GetContextSize` | session_id | string | Context token size |
|
||||
| `GetUsage` | session_id | string | Token usage stats |
|
||||
| `GetCost` | session_id | string | Cost tracking |
|
||||
| `GetEnv` | session_id | key=value text | Session environment variables |
|
||||
| `GetPlan` | session_id | string | Read session plan |
|
||||
| `SetPlan` | session_id, plan | bool | Write session plan |
|
||||
| `GetSystemPrompt` | session_id | string | Rendered system prompt |
|
||||
| `GetPreviousPrompt` | session_id | string | Previous prompt text |
|
||||
|
||||
### Peers
|
||||
|
||||
| Method | Args | Returns | Purpose |
|
||||
|---|---|---|---|
|
||||
| `PeerAdd` | session_id, peer_id | bool | Add bidirectional peer link (both sides updated) |
|
||||
| `PeerRemove` | session_id, peer_id | bool | Remove bidirectional peer link (both sides updated) |
|
||||
| `PeerList` | session_id | array of peer_ids | List peers |
|
||||
| `PeerSubmit` | session_id, peer_id, prompt | bool | Send prompt to peer |
|
||||
|
||||
### Detached Processes
|
||||
|
||||
| Method | Args | Returns | Purpose |
|
||||
|---|---|---|---|
|
||||
| `DetachProcess` | session_id | bool | Detach current running process to background |
|
||||
| `ListDetached` | session_id | array of "pid\tcmd\tstarted\tstatus" | List background processes |
|
||||
| `SignalDetached` | session_id, pid, signal | bool | Send signal (TERM, KILL) to detached process |
|
||||
| `GetDetachedOutput` | session_id, pid | string | Read output from detached process |
|
||||
| `DismissDetached` | session_id, pid | bool | Dismiss a finished detached process |
|
||||
|
||||
### Global Services
|
||||
|
||||
| Method | Args | Returns | Purpose |
|
||||
|---|---|---|---|
|
||||
| `ListBackends` | — | array of names | Available backends |
|
||||
| `ListModels` | session_id | array of names | Models for session's backend |
|
||||
| `ListAgents` | — | array of names | Available agent definitions |
|
||||
| `Generate` | prompt, system, backend, model | string | One-shot LLM generation (no session needed) |
|
||||
| `Route` | task, backend | "backend=X model=Y" | Pick a backend+model for a task |
|
||||
| `Complete` | cwd, file, prefix, suffix, context | string | Code completion |
|
||||
|
||||
## Signals
|
||||
|
||||
| Signal | Args | Purpose |
|
||||
|---|---|---|
|
||||
| `SessionCreated` | session_id | New session appeared |
|
||||
| `SessionKilled` | session_id | Session terminated |
|
||||
| `SessionRenamed` | old_id, new_id | Session renamed |
|
||||
| `StateChanged` | session_id, state | Agent state transition |
|
||||
| `ChatUpdated` | session_id, offset, text | New chat output available |
|
||||
| `ProcessDetached` | session_id | A process was detached |
|
||||
| `ProcessExited` | session_id, pid, exit_code | A detached process finished |
|
||||
|
||||
## Operations
|
||||
|
||||
```bash
|
||||
# Discover sessions
|
||||
dbus-send --session --dest=org.ollie.SessionManager --type=method_call --print-reply \
|
||||
/org/ollie/SessionManager org.ollie.SessionManager.ListSessions
|
||||
|
||||
# Create a session
|
||||
dbus-send --session --dest=org.ollie.SessionManager --type=method_call --print-reply \
|
||||
/org/ollie/SessionManager org.ollie.SessionManager.CreateSession \
|
||||
string:"$PWD" string:"" string:"" string:"default" string:"" string:""
|
||||
|
||||
# Submit a prompt
|
||||
dbus-send --session --dest=org.ollie.SessionManager --type=method_call \
|
||||
/org/ollie/SessionManager org.ollie.SessionManager.Submit \
|
||||
string:"SESSION_ID" string:"your prompt here"
|
||||
|
||||
# Get chat output (use gdbus for multi-return)
|
||||
gdbus call --session --dest org.ollie.SessionManager \
|
||||
--object-path /org/ollie/SessionManager \
|
||||
--method org.ollie.SessionManager.GetChat "SESSION_ID" 0
|
||||
|
||||
# One-shot generation (no session)
|
||||
gdbus call --session --dest org.ollie.SessionManager \
|
||||
--object-path /org/ollie/SessionManager \
|
||||
--method org.ollie.SessionManager.Generate "summarize this" "" "" ""
|
||||
|
||||
# Route a task
|
||||
gdbus call --session --dest org.ollie.SessionManager \
|
||||
--object-path /org/ollie/SessionManager \
|
||||
--method org.ollie.SessionManager.Route "implement login page" ""
|
||||
|
||||
# Code completion
|
||||
gdbus call --session --dest org.ollie.SessionManager \
|
||||
--object-path /org/ollie/SessionManager \
|
||||
--method org.ollie.SessionManager.Complete "$PWD" "main.go" "func " "" ""
|
||||
|
||||
# Send prompt to peer
|
||||
dbus-send --session --dest=org.ollie.SessionManager --type=method_call \
|
||||
/org/ollie/SessionManager org.ollie.SessionManager.PeerSubmit \
|
||||
string:"MY_SESSION" string:"PEER_SESSION" string:"your prompt"
|
||||
|
||||
# Kill a session
|
||||
dbus-send --session --dest=org.ollie.SessionManager --type=method_call \
|
||||
/org/ollie/SessionManager org.ollie.SessionManager.KillSession \
|
||||
string:"SESSION_ID"
|
||||
```
|
||||
|
|
@ -2,11 +2,11 @@
|
|||
## Philosophy
|
||||
ollie's design philosophy is Emacs: a small, extensible core. The Go runtime (`agent.Agent`) defines what an agent *is* — an event loop, a backend connection, a message history, and three built-in primitives (`shell`, tool registry, skill registry). Everything else — orchestration, scheduling, workflows, UIs — is pushed out to the surrounding environment.
|
||||
The primary integration philosophy is Plan 9's "everything is a file." `olliesrv` exposes agent state and behaviors as files in a 9P namespace. Any program that can read and write files can drive an agent: shell scripts, editors, web apps, cron, containers.
|
||||
For desktop-native integration, `olliesrv` embeds a D-Bus adapter (`org.ollie.SessionManager`) alongside its 9P interface. The KDE GUI, plasmoid, and web UI connect this way. For D-Bus-only deployments, `-no9p` disables the 9P listener entirely.
|
||||
All clients communicate via 9P. Desktop notifications use D-Bus directly (org.freedesktop.Notifications) for elevation prompts only.
|
||||
Design principles:
|
||||
- **Small extensible core.** The agent runtime is minimal; capabilities come from composing external scripts.
|
||||
- **Three built-in primitives.** `shell`, tool registry (`tool_load`/`tool_list`/`tool_active`), and skill registry (`skill_load`/`skill_list`/`skill_active`) are the only tools compiled into the core. Everything else is a script.
|
||||
- **Two integration paths.** 9P filesystem (canonical) and/or D-Bus (conventional). Same core, different surfaces, selectable via flags.
|
||||
- **One integration path: 9P filesystem. Streaming via blocking reads. No polling.
|
||||
- **No framework lock-in.** Frontends are decoupled via whichever interface they prefer; the core doesn't know or care.
|
||||
## Repository Structure
|
||||
Single Go module (`ollie`) with two Git submodules for decoupled frontends:
|
||||
|
|
@ -17,7 +17,6 @@ ollie/
|
|||
├── toolsrv/ Tool server: sandboxed execution, tool registry, skill management
|
||||
├── session/ Session lifecycle (config, creation, persistence)
|
||||
├── fs/session/ 9P filesystem tree for sessions
|
||||
├── dbus/ D-Bus adapter (org.ollie.SessionManager)
|
||||
├── detach/ Background process management (ring buffer, signal)
|
||||
├── elevate/ Elevation broker (privilege escalation daemon)
|
||||
├── sandbox/ Landlock sandbox config YAML
|
||||
|
|
@ -49,12 +48,10 @@ flowchart TB
|
|||
subgraph Integration["Integration Layer"]
|
||||
direction LR
|
||||
P9["9P Filesystem"]
|
||||
DBUS["org.ollie.SessionManager (D-Bus)"]
|
||||
end
|
||||
subgraph Server["olliesrv"]
|
||||
direction TB
|
||||
NS["9P Namespace\nsession/ · backends · models"]
|
||||
DBA["Embedded D-Bus Adapter"]
|
||||
end
|
||||
subgraph Core["Agent Engine (per session)"]
|
||||
LOOP["Agent Loop\n(agent/loop.go)"]
|
||||
|
|
@ -232,202 +229,4 @@ The `*fs.Tree` IS the session collection. Package functions manage lifecycle:
|
|||
- `KillFromRoot(tree, id)` — kill a session
|
||||
- `RenameFromRoot(tree, old, new)` — rename a session
|
||||
- `Shutdown(tree)` — clean shutdown
|
||||
### Embedded D-Bus Adapter
|
||||
`olliesrv` includes an embedded D-Bus adapter that claims `org.ollie.SessionManager` on the session bus. This provides:
|
||||
- The same method/signal interface used by KDE frontends
|
||||
- `SessionCreated`, `SessionKilled`, `StateChanged`, `ChatUpdated` signals
|
||||
- Lifecycle watchers that bridge session state changes to D-Bus signals
|
||||
The adapter is best-effort: if no session bus is available (e.g., headless/container), `olliesrv` continues functioning as a pure 9P server.
|
||||
## D-Bus Adapter (`dbus/`)
|
||||
The D-Bus adapter is embedded in `olliesrv` (no separate daemon). For D-Bus-only deployments, run `olliesrv -no9p`. This disables the filesystem listener while keeping the full D-Bus interface. Session persistence uses the same `~/.local/share/ollie/sessions/active/` directory.
|
||||
## KDE Integration (`kde/`)
|
||||
The KDE submodule provides several Qt/QML components that connect to `org.ollie.SessionManager` over D-Bus:
|
||||
| Component | Path | Purpose |
|
||||
|---|---|---|
|
||||
| GUI | `kde/gui/` | Standalone Qt window for chat interaction |
|
||||
| Plasmoid | `kde/plasmoid/` | Plasma widget with `OllieDBusClient` |
|
||||
| Kate plugin | `kde/kate/` | In-editor chat pane and ghost completion |
|
||||
| KRunner | `kde/krunner/` | Quick-launch sessions from KRunner |
|
||||
| System tray | `kde/tray/` | Tray icon with session status |
|
||||
All KDE components use `QDBusServiceWatcher` to handle daemon restarts gracefully — recreating the `QDBusInterface` when the service reappears and refreshing the session list.
|
||||
## Tool System
|
||||
### Architecture
|
||||
The tool system has exactly one built-in server (`toolsrv.Server`) that exposes three primitives:
|
||||
| Primitive | Purpose |
|
||||
|---|---|
|
||||
| `shell` | Run a bash command in a sandbox |
|
||||
| `tool_load`/`tool_list`/`tool_active` | Dynamic tool registry |
|
||||
| `skill_load`/`skill_list`/`skill_active` | Dynamic skill registry |
|
||||
All other capabilities are **tool scripts** — executable files discovered from `OLLIE_TOOLS_PATH` (default: `~/.config/ollie/tools/`).
|
||||
### Tool Scripts
|
||||
Tool scripts are plain executables with a `.meta` sidecar JSON file declaring description, parameters, tier, and parallelism class:
|
||||
- `file_read`, `file_write`, `file_edit`, `file_glob`, `file_grep` — filesystem I/O
|
||||
- `lsp_definition`, `lsp_references`, `lsp_rename`, `lsp_symbols`, `lsp_diagnostics`, `lsp_hover`, `lsp_completion` — LSP bridge (Go, `tools/lsp/`)
|
||||
- `memory_remember`, `memory_recall` — persistent memory
|
||||
- `reasoning_think` — scratchpad for externalized reasoning
|
||||
- `subagent_spawn`, `subagent_generate` — sub-agent lifecycle
|
||||
- `route` — orchestrator dispatch
|
||||
- `gui_*` — KDE desktop automation (screenshot, windows, clipboard, input, ...)
|
||||
Tools marked `"readOnly": true` in their `.meta` can be fanned out concurrently by the agent loop.
|
||||
### Sandboxing
|
||||
Every code execution step is wrapped with [landrun](https://github.com/landlock-lsm/landrun) (Landlock LSM). Configuration is YAML-based:
|
||||
```yaml
|
||||
general:
|
||||
best_effort: true
|
||||
filesystem:
|
||||
ro: ["/usr", "/lib", "/etc", "{HOME}/.config/ollie"]
|
||||
rox: ["/usr/bin", "/usr/local/bin"]
|
||||
rw: ["{CWD}", "{TMPDIR}"]
|
||||
network:
|
||||
enabled: true
|
||||
unrestricted: true
|
||||
```
|
||||
Template variables (`{CWD}`, `{HOME}`, `{XDG_*}`, `{OLLIE_*}`) are expanded at runtime. **landrun is mandatory** — there is no unsandboxed fallback. Steps marked `elevated: true` bypass the sandbox via the elevation backend (`x/elevate`).
|
||||
## LLM Backends (`backend/`)
|
||||
All backends implement the streaming-only `Backend` interface:
|
||||
| Backend | Wire Format | Notes |
|
||||
|---|---|---|
|
||||
| `OllamaBackend` | `/api/chat` | Local models, context length from `/api/show` |
|
||||
| `OpenAIBackend` | `/v1/chat/completions` | OpenAI, OpenRouter, any compatible API |
|
||||
| `AnthropicBackend` | `/v1/messages` | Extended thinking, prompt caching |
|
||||
| `GeminiBackend` | Gemini API | Google Gemini models |
|
||||
| `CopilotBackend` | GitHub Copilot API | Copilot Chat |
|
||||
| `CodeWhispererBackend` | CodeWhisperer API | AWS CodeWhisperer |
|
||||
Shared infrastructure:
|
||||
- `streamRequest()` — common HTTP streaming with error classification
|
||||
- `RateLimitError`, `TransientError`, `ContextOverflowError`, `ToolUnsupportedError` — typed errors for loop control
|
||||
- `GenerationParams` — unified sampling parameters across all backends
|
||||
## Multi-Agent System
|
||||
Multi-agent coordination is built entirely on the filesystem primitive:
|
||||
### Mechanism
|
||||
1. **Spawn**: write session spec to `session/new` → new session directory appears
|
||||
2. **Prompt**: write task to `session/{id}/prompt`
|
||||
3. **Observe**: read `session/{id}/state` or block on `session/{id}/statewait`
|
||||
4. **Result**: child writes to `session/{parent_id}/prompt` when done
|
||||
### Delegation Tools
|
||||
| Tool | Semantics |
|
||||
|---|---|
|
||||
| `route` | Orchestrator dispatch; selects backend+model for a task |
|
||||
| `subagent_spawn` | Raw session creation with full control |
|
||||
| `subagent_generate` | JIT agent identity generation |
|
||||
### Callback Protocol
|
||||
```
|
||||
[from={session_id}]
|
||||
STATUS: done|error
|
||||
SUMMARY: <result>
|
||||
ARTIFACTS: <paths>
|
||||
```
|
||||
The parent never actively waits — results arrive as queued prompts.
|
||||
This is the secondary integration path for non-LLM consumers (web UIs, dashboards, external tools) that want a conventional HTTP API without speaking 9P directly.
|
||||
## Frontends
|
||||
Frontends connect via either 9P (filesystem) or D-Bus, depending on preference:
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph Core["agent.Agent"]
|
||||
AG["Agent Engine"]
|
||||
end
|
||||
subgraph Surfaces["Integration Surfaces"]
|
||||
P9["9P Filesystem\n(session/ namespace)"]
|
||||
DB["D-Bus\n(org.ollie.SessionManager)"]
|
||||
end
|
||||
subgraph Frontends
|
||||
ACME["acme (Plan 9)"]
|
||||
EL["ellie (Emacs)"]
|
||||
KG["KDE GUI"]
|
||||
KP["KDE Plasmoid"]
|
||||
KK["Kate Plugin"]
|
||||
KR["KRunner"]
|
||||
HTTP["curl / scripts"]
|
||||
end
|
||||
AG --> P9
|
||||
AG --> DB
|
||||
ACME --> P9
|
||||
EL --> P9
|
||||
HTTP --> P9
|
||||
KG --> DB
|
||||
KP --> DB
|
||||
KK --> DB
|
||||
KR --> DB
|
||||
WEB --> DB
|
||||
```
|
||||
Frontends are fully decoupled — the core doesn't know which interface they use.
|
||||
## Script Namespaces
|
||||
User-facing scripts installed to `~/.config/ollie/scripts/{ns}/`:
|
||||
| Namespace | Purpose | Key Scripts |
|
||||
|---|---|---|
|
||||
| `s/` | Session management | `ls`, `kill`, `cleanup` |
|
||||
| `u/` | Utility compositions | `complete` (code completion), `optimize` (prompt optimization) |
|
||||
| `x/` | System plugins | `elevate` (privilege escalation), `prime` (prompt template renderer) |
|
||||
## Agent Configuration
|
||||
Agent configs are JSON files in `~/.config/ollie/agents/`:
|
||||
```json
|
||||
{
|
||||
"prompt": [
|
||||
"$OLLIE_CFG_PATH/scripts/x/prime SYSTEM_PROMPT",
|
||||
"$OLLIE_CFG_PATH/scripts/x/prime session",
|
||||
"$OLLIE_CFG_PATH/scripts/x/prime agent-coding",
|
||||
"$OLLIE_CFG_PATH/scripts/x/prime tools-file",
|
||||
"$OLLIE_CFG_PATH/scripts/x/prime tools-reasoning"
|
||||
],
|
||||
"hooks": {
|
||||
"agentSpawn": [],
|
||||
"turnError": ["$OLLIE_CFG_PATH/scripts/x/freeloader $OLLIE_SESSION_ID"]
|
||||
},
|
||||
"maxTokens": 8192,
|
||||
"temperature": 0.7,
|
||||
"maxSteps": 50
|
||||
}
|
||||
```
|
||||
The `prompt` array is the key innovation: each element is a shell command whose stdout is concatenated to form the system prompt. This makes prompts composable, dynamic, and environment-aware.
|
||||
## Data Flow
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant F as Frontend
|
||||
participant S as olliesrv
|
||||
participant A as agent.Agent
|
||||
participant B as LLM Backend
|
||||
participant T as toolsrv.Server
|
||||
participant SH as sandbox (landrun)
|
||||
F->>S: write to session/{id}/prompt
|
||||
S->>A: Submit()
|
||||
activate A
|
||||
loop Agent Loop
|
||||
A->>B: ChatStream(messages, tools)
|
||||
activate B
|
||||
B-->>A: stream events
|
||||
deactivate B
|
||||
alt has tool calls
|
||||
A->>T: Dispatch(tool, args)
|
||||
activate T
|
||||
T->>SH: sandboxed execution
|
||||
SH-->>T: result
|
||||
T-->>A: ToolResult
|
||||
deactivate T
|
||||
A->>A: append to history
|
||||
else no tool calls
|
||||
A->>A: turn complete
|
||||
break
|
||||
end
|
||||
end
|
||||
A-->>S: turn complete
|
||||
deactivate A
|
||||
S-->>F: readable via session/{id}/chat
|
||||
```
|
||||
## Extension Points
|
||||
| What | How |
|
||||
|---|---|
|
||||
| Add an LLM backend | Implement `backend.Backend` |
|
||||
| Add a tool | Drop an executable into `OLLIE_TOOLS_PATH` |
|
||||
| Add a tool server | Implement `toolsrv.Runner`, register in `NewToolServer` |
|
||||
| Add a frontend | Connect via 9P (filesystem) or D-Bus (method calls) |
|
||||
| Add a skill | Drop a markdown file into `skills/` |
|
||||
| Add an agent persona | Create a JSON config in `agents/` |
|
||||
| Customize sandbox | Create a YAML in `~/.config/ollie/sandbox/` |
|
||||
## Key Design Decisions
|
||||
1. **Two integration surfaces** — 9P filesystem (canonical, Unix philosophy) and D-Bus (conventional, for GUI/web consumers). Both are thin adapters over the same core.
|
||||
2. **Three built-in primitives** — `shell`, tool registry, skill registry. Everything else is a script. This keeps the core small and makes all other tools equal citizens.
|
||||
3. **Mandatory sandboxing** — no unsandboxed fallback. Security is not optional.
|
||||
4. **Streaming-only backends** — simplifies the interface; blocking APIs wrap as single-event streams.
|
||||
5. **Fire-and-forget sub-agents** — no active waiting, no polling. Results arrive as filesystem writes.
|
||||
6. **Prompt-as-code** — system prompts are assembled by running shell commands, not by concatenating static strings.
|
||||
7. **Per-agent principals** — 9P permission enforcement prevents self-prompting loops and enforces access boundaries without application-level checks.
|
||||
|
|
|
|||
|
|
@ -387,8 +387,8 @@ ollie/ ← single Go module
|
|||
small until complexity is needed.
|
||||
5. **Plan simplicity** — after 5 iterations, planning settled on one markdown
|
||||
checklist file per session.
|
||||
6. **Dual control plane** — 9P for power users/editors, D-Bus for desktop.
|
||||
Same core, different surfaces.
|
||||
6. **Single control plane — 9P for everything.
|
||||
Streaming via blocking reads. No polling.
|
||||
7. **Security by default** — sandboxed execution with explicit elevation,
|
||||
per-session identity.
|
||||
## Dead Ends and Reversals
|
||||
|
|
@ -408,4 +408,39 @@ ollie/ ← single Go module
|
|||
| chatwait | Reverted; acme tails chat directly |
|
||||
| Header comment metadata | Replaced by .meta sidecar JSON; decouples metadata from language |
|
||||
| `contrib/` directory | Renamed to `data/`; nothing was community-contributed |
|
||||
| Script namespaces (s/, u/, x/) | Replaced by 9P request-response files |
|
||||
| Script namespaces (s/, u/, x/) | Replaced by 9P request-response files |
|
||||
| D-Bus adapter | 9P streaming is superior; no polling, no offset tracking, no frozen GUIs |
|
||||
## Phase 13: 9P Streaming & D-Bus Removal (Jul 31)
|
||||
|
||||
The D-Bus adapter (`org.ollie.SessionManager`) is deleted. All clients now
|
||||
use 9P exclusively via blocking reads for natural streaming.
|
||||
|
||||
Key changes:
|
||||
- **`chat` file**: blocking read that delivers tokens as the agent produces
|
||||
them. Per-fid offset. Never EOF (blocks between turns). EOF only on kill.
|
||||
- **`log` file**: replaces old `chat`. Returns last 64KB (sliding window).
|
||||
Non-blocking, tail-able via Qid.Vers.
|
||||
- **`kill`/`.` ctl command**: kills session → EOF on chat readers.
|
||||
- **KDE GUI**: rewritten from scratch. Uses `9p` (plan9port) via QProcess.
|
||||
Streaming via `readyReadStandardOutput`. Plain text tail (8KB).
|
||||
No ChatBlockModel, no D-Bus, no ThemeManager. ~250 lines total.
|
||||
- **Kate plugin**: all D-Bus calls replaced with `9p` subprocess calls.
|
||||
Streaming chat + statewait via persistent QProcess.
|
||||
- **KRunner**: uses `9p` for session listing and one-shot generation.
|
||||
- **Acme frontend**: uses 9fans.net/go plan9/client directly. Blocking
|
||||
read loop on `chat`. No FUSE mount.
|
||||
- **Plasmoid, tray**: deleted (not useful enough to maintain).
|
||||
- **dbus/ package**: deleted (-771 lines from server).
|
||||
|
||||
The only remaining godbus usage: `org.freedesktop.Notifications` for
|
||||
elevation prompts (desktop integration, not ollie protocol).
|
||||
|
||||
Lessons:
|
||||
- 9P's request-response model gives natural streaming for free.
|
||||
Server delays Rread until data arrives. No polling needed.
|
||||
- FUSE mounts do NOT support blocking reads (return EOF immediately).
|
||||
Clients must use the 9P protocol directly.
|
||||
- Never bind a growing string to a QTextArea with word-wrap.
|
||||
QTextDocument relayout is O(n) on the full content.
|
||||
- The `loadEarlier` scroll-triggered cascade was creating 500 delegates
|
||||
on startup. Bounded initial window + explicit scroll-back is correct.
|
||||
|
|
|
|||
|
|
@ -392,7 +392,7 @@ field in an agent config JSON to a file path:
|
|||
|
||||
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/D-Bus documentation), environment block, and agent-specific prompt
|
||||
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`.
|
||||
|
|
|
|||
Loading…
Reference in New Issue