From 5911315408dbedbfcae27352fb88254139d0adff Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Fri, 31 Jul 2026 20:47:15 +0200 Subject: [PATCH] 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 --- .gitignore | 1 + AGENTS.md | 6 +- data/prompts/session-9p.md | 5 +- data/prompts/session-dbus.md | 158 --------------------------- doc/ARCHITECTURE.md | 205 +---------------------------------- doc/EVOLUTION.md | 41 ++++++- doc/USAGE.md | 2 +- 7 files changed, 46 insertions(+), 372 deletions(-) delete mode 100644 data/prompts/session-dbus.md diff --git a/.gitignore b/.gitignore index 4edf975..0ed13a4 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ .claude .kiro __pycache__/ +olliesrv diff --git a/AGENTS.md b/AGENTS.md index cf16eae..dd1a756 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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 diff --git a/data/prompts/session-9p.md b/data/prompts/session-9p.md index 6aff945..97e68cf 100644 --- a/data/prompts/session-9p.md +++ b/data/prompts/session-9p.md @@ -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 diff --git a/data/prompts/session-dbus.md b/data/prompts/session-dbus.md deleted file mode 100644 index 48c6e23..0000000 --- a/data/prompts/session-dbus.md +++ /dev/null @@ -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: ` | -| `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" -``` diff --git a/doc/ARCHITECTURE.md b/doc/ARCHITECTURE.md index ec4f701..c01c4bd 100644 --- a/doc/ARCHITECTURE.md +++ b/doc/ARCHITECTURE.md @@ -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: -ARTIFACTS: -``` -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. diff --git a/doc/EVOLUTION.md b/doc/EVOLUTION.md index 0253011..405673f 100644 --- a/doc/EVOLUTION.md +++ b/doc/EVOLUTION.md @@ -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 | \ No newline at end of file +| 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. diff --git a/doc/USAGE.md b/doc/USAGE.md index 1fd7273..366852b 100644 --- a/doc/USAGE.md +++ b/doc/USAGE.md @@ -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`.