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:
Levi Neely 2026-07-31 20:47:15 +02:00
parent b28a56e89a
commit 5911315408
7 changed files with 46 additions and 372 deletions

1
.gitignore vendored
View File

@ -6,3 +6,4 @@
.claude
.kiro
__pycache__/
olliesrv

View File

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

View File

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

View File

@ -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"
```

View File

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

View File

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

View File

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