Files
Aetheel/docs/opencode-integration-summary.md
Tanmay Karande ec8bd80a3d first commit
2026-02-13 23:56:09 -05:00

233 lines
8.2 KiB
Markdown

# OpenCode Runtime Integration — Summary
> Integration of OpenCode CLI as the agent runtime for Aetheel.
> Completed: 2026-02-13
---
## Overview
OpenCode CLI has been integrated as the AI "brain" for Aetheel, replacing the placeholder `smart_handler` with a full agent runtime. The architecture is directly inspired by OpenClaw's `cli-runner.ts` and `cli-backends.ts`, adapted for OpenCode's API and Python.
---
## Files Created & Modified
### New Files
| File | Purpose |
|------|---------|
| `agent/__init__.py` | Package init for the agent module |
| `agent/opencode_runtime.py` | Core runtime — ~750 lines covering both CLI and SDK modes |
| `docs/opencode-setup.md` | Comprehensive setup guide |
| `docs/opencode-integration-summary.md` | This summary document |
### Modified Files
| File | Change |
|------|--------|
| `main.py` | Rewired to use `ai_handler` backed by `OpenCodeRuntime` instead of placeholder `smart_handler` |
| `.env.example` | Added all OpenCode config variables |
| `requirements.txt` | Added optional `opencode-ai` SDK dependency note |
---
## Architecture
```
Slack Message → ai_handler() → OpenCodeRuntime.chat() → OpenCode → LLM → Response
```
### Two Runtime Modes
1. **CLI Mode** (default) — Spawns `opencode run` as a subprocess per request.
Direct port of OpenClaw's `runCliAgent()``runCommandWithTimeout()` pattern
from `cli-runner.ts`.
2. **SDK Mode** — Connects to `opencode serve` via the official Python SDK
(`opencode-ai`). Uses `client.session.create()``client.session.chat()`
for lower latency and better session management.
### Component Diagram
```
┌─────────────────────┐
│ Slack │
│ (messages) │
└──────┬──────────────┘
│ WebSocket
┌──────▼──────────────┐
│ Slack Adapter │
│ (slack_adapter.py) │
│ │
│ • Socket Mode │
│ • Event handling │
│ • Thread isolation │
└──────┬──────────────┘
│ ai_handler()
┌──────▼──────────────┐
│ OpenCode Runtime │
│ (opencode_runtime) │
│ │
│ • Session store │
│ • System prompt │
│ • Mode routing │
└──────┬──────────────┘
┌────┴────┐
│ │
▼ ▼
CLI Mode SDK Mode
┌──────────┐ ┌──────────────┐
│ opencode │ │ opencode │
│ run │ │ serve API │
│ (subproc)│ │ (HTTP/SDK) │
└──────────┘ └──────────────┘
│ │
└──────┬───────┘
┌──────▼──────┐
│ LLM │
│ (Anthropic, │
│ OpenAI, │
│ Gemini) │
└─────────────┘
```
---
## Key Components (OpenClaw → Aetheel Mapping)
| OpenClaw (`cli-runner.ts`) | Aetheel (`opencode_runtime.py`) |
|---|---|
| `CliBackendConfig` | `OpenCodeConfig` dataclass |
| `runCliAgent()` | `OpenCodeRuntime.chat()` |
| `buildCliArgs()` | `_build_cli_args()` |
| `runCommandWithTimeout()` | `subprocess.run(timeout=...)` |
| `parseCliJson()` / `collectText()` | `_parse_cli_output()` / `_collect_text()` |
| `pickSessionId()` | `_extract_session_id()` |
| `buildSystemPrompt()` | `build_aetheel_system_prompt()` |
| Session per thread | `SessionStore` (thread_ts → session_id) |
---
## Key Design Decisions
### 1. Dual-Mode Runtime (CLI + SDK)
- **CLI mode** is the default because it requires no persistent server — just `opencode` in PATH.
- **SDK mode** is preferred for production because it avoids cold-start latency and provides better session management.
- The runtime gracefully falls back from SDK → CLI if the server is unreachable or the SDK is not installed.
### 2. Session Isolation per Thread
- Each Slack thread (`thread_ts`) maps to a unique OpenCode session via the `SessionStore`.
- New threads get new sessions; replies within a thread reuse the same session.
- Stale sessions are cleaned up after `session_ttl_hours` (default 24h).
### 3. System Prompt Injection
- `build_aetheel_system_prompt()` constructs a per-message system prompt with the bot's identity, guidelines, and context (user name, channel, DM vs. mention).
- This mirrors OpenClaw's `buildAgentSystemPrompt()` from `cli-runner/helpers.ts`.
### 4. Output Parsing (from OpenClaw)
- The `_parse_cli_output()` method tries JSON → JSONL → raw text, matching OpenClaw's `parseCliJson()` and `parseCliJsonl()`.
- The `_collect_text()` method recursively traverses JSON objects to find text content, a direct port of OpenClaw's `collectText()`.
### 5. Built-in Commands Bypass AI
- Commands like `status`, `help`, `time`, and `sessions` are handled directly without calling the AI, for instant responses.
---
## Configuration Reference
All settings go in `.env`:
```env
# Runtime mode
OPENCODE_MODE=cli # "cli" or "sdk"
# Model (optional — uses OpenCode default if not set)
OPENCODE_MODEL=anthropic/claude-sonnet-4-20250514
# CLI mode settings
OPENCODE_COMMAND=opencode # path to the opencode binary
OPENCODE_TIMEOUT=120 # seconds before timeout
# SDK mode settings (only needed when OPENCODE_MODE=sdk)
OPENCODE_SERVER_URL=http://localhost:4096
OPENCODE_SERVER_PASSWORD= # optional HTTP basic auth
OPENCODE_SERVER_USERNAME=opencode # default username
# Workspace directory for OpenCode
OPENCODE_WORKSPACE=/path/to/project
# Output format
OPENCODE_FORMAT=text # "text" or "json"
```
CLI flags can override config:
```bash
python main.py --cli # force CLI mode
python main.py --sdk # force SDK mode
python main.py --model anthropic/claude-sonnet-4-20250514
python main.py --test # echo-only (no AI)
```
---
## OpenCode Research Summary
### OpenCode CLI
- **What:** Go-based AI coding agent for the terminal
- **Install:** `curl -fsSL https://opencode.ai/install | bash` or `npm install -g opencode-ai`
- **Key commands:**
- `opencode` — TUI mode
- `opencode run "prompt"` — non-interactive, returns output
- `opencode serve` — headless HTTP server (OpenAPI 3.1 spec)
- `opencode auth login` — configure LLM providers
- `opencode models` — list available models
- `opencode init` — generate `AGENTS.md` for a project
### OpenCode Server API (via `opencode serve`)
- Default: `http://localhost:4096`
- Auth: HTTP basic auth via `OPENCODE_SERVER_PASSWORD`
- Key endpoints:
- `GET /session` — list sessions
- `POST /session` — create session
- `POST /session/:id/message` — send message (returns `AssistantMessage`)
- `POST /session/:id/abort` — abort in-progress request
- `GET /event` — SSE event stream
### OpenCode Python SDK (`opencode-ai`)
- Install: `pip install opencode-ai`
- Key methods:
- `client.session.create()``Session`
- `client.session.chat(id, parts=[...])``AssistantMessage`
- `client.session.list()``Session[]`
- `client.session.abort(id)` → abort
- `client.app.get()` → app info
- `client.app.providers()` → available providers
---
## Quick Start
1. Install OpenCode: `curl -fsSL https://opencode.ai/install | bash`
2. Configure a provider: `opencode auth login`
3. Test standalone: `opencode run "Hello, what are you?"`
4. Configure `.env` (copy from `.env.example`)
5. Run Aetheel: `python main.py`
6. In Slack: send a message to the bot and get an AI response
---
## Next Steps
1. **Memory System** — Add conversation persistence (SQLite) so sessions survive restarts
2. **Heartbeat** — Proactive messages via cron/scheduler
3. **Skills** — Loadable skill modules (like OpenClaw's `skills/` directory)
4. **Multi-Channel** — Discord, Telegram adapters
5. **Streaming** — Use SSE events from `opencode serve` for real-time streaming responses