docs: move architecture and configuration details to docs/
- docs/architecture.md — data flow, room model, tribunal flow, container isolation, key files - docs/configuration.md — full .env reference, debugging paths - README links to docs/
This commit is contained in:
@@ -64,6 +64,11 @@ cp .env.example .env # Configure tokens and settings
|
||||
bun run deploy # Or: bun run dev
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Architecture](docs/architecture.md) — Data flow, room model, container isolation, key files
|
||||
- [Configuration](docs/configuration.md) — Full `.env` reference, debugging paths
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
|
||||
106
docs/architecture.md
Normal file
106
docs/architecture.md
Normal file
@@ -0,0 +1,106 @@
|
||||
# Architecture
|
||||
|
||||
## Service Stack
|
||||
|
||||
Single unified service (`ejclaw`) manages all three Discord bots in one process:
|
||||
|
||||
- `ejclaw.service` — Single unified process
|
||||
- Discord bots: `DISCORD_BOT_TOKEN` (Claude), `DISCORD_CODEX_BOT_TOKEN` (Codex-main), `DISCORD_REVIEW_BOT_TOKEN` (Codex-review)
|
||||
- Paired review: owner ↔ reviewer (agent types configurable per role)
|
||||
- Reviewer fallback: Claude exhaustion → codex-review auto-handoff
|
||||
- Shared dirs: `store/`, `groups/`, `data/`
|
||||
- SQLite WAL mode + `busy_timeout=5000` for concurrent access
|
||||
|
||||
## Data Flow
|
||||
|
||||
```
|
||||
Discord ──► SQLite (WAL) ──► GroupQueue ──┬──► Owner (host process)
|
||||
│ │
|
||||
│ ▼ (auto-trigger)
|
||||
├──► Reviewer (Docker container, :ro mount)
|
||||
│ │
|
||||
│ Verdict routing
|
||||
│ ├─ DONE → change detection → finalize or re-review
|
||||
│ ├─ BLOCKED → Arbiter (if enabled) or User
|
||||
│ └─ Feedback → Owner (loop)
|
||||
│
|
||||
├──► Arbiter (on-demand, fresh session each time)
|
||||
│ │
|
||||
│ ┌───┴─── MoA (if enabled) ───┐
|
||||
│ │ Ref model A ──► opinion │
|
||||
│ │ Ref model B ──► opinion │
|
||||
│ │ → injected into prompt │
|
||||
│ └────────────────────────────┘
|
||||
│ │
|
||||
│ PROCEED/REVISE/RESET/ESCALATE
|
||||
│
|
||||
IPC polling ◄── follow-up messages
|
||||
│
|
||||
┌────────── Router ──────────┐
|
||||
▼ ▼
|
||||
paired_turn_outputs Discord (display only)
|
||||
(agent ↔ agent data) (user observation, @mention)
|
||||
```
|
||||
|
||||
## Room Assignment Model
|
||||
|
||||
Per-room routing uses an explicit assignment model:
|
||||
|
||||
- `room_settings` is the room-level source of truth (SSOT)
|
||||
- Each room stores:
|
||||
- `room_mode`: `single` or `tribunal`
|
||||
- `owner_agent_type`: `codex` or `claude-code`
|
||||
- Public room assignment uses `assign_room`
|
||||
- `registered_groups` remains as a materialized capability/read-model layer
|
||||
|
||||
Operationally:
|
||||
|
||||
- `single` → one owner bot
|
||||
- `tribunal` → per-room owner + globally configured reviewer + optional arbiter
|
||||
|
||||
Tribunal is no longer inferred from "two bots registered on one room"; it is an explicit room setting.
|
||||
|
||||
## Tribunal 3-Agent System
|
||||
|
||||
```
|
||||
User message
|
||||
→ Owner responds (implementation, answer, etc.)
|
||||
→ Reviewer auto-triggered (critical review, design check)
|
||||
→ Verdict:
|
||||
DONE → Owner finalizes → Task completed → @user ✅
|
||||
DONE_WITH_CONCERNS → Owner addresses feedback → loop
|
||||
BLOCKED/NEEDS_CONTEXT
|
||||
├─ Arbiter enabled → Arbiter judges → PROCEED/REVISE/RESET/ESCALATE
|
||||
└─ Arbiter disabled → Escalate to user → @user ⚠️
|
||||
→ Owner BLOCKED/NEEDS_CONTEXT → Arbiter (same path as reviewer)
|
||||
→ Deadlock (3+ round trips without progress)
|
||||
→ Arbiter summoned → binding verdict → loop resumes
|
||||
```
|
||||
|
||||
## Container Isolation
|
||||
|
||||
Reviewer and arbiter run inside a Docker container with:
|
||||
|
||||
- Read-only source mount (kernel-level write protection)
|
||||
- Both Claude (agent-runner) and Codex (codex-runner) supported
|
||||
- Runner selected at `docker exec` time based on agent type
|
||||
- tmpfs overlays for test runner caches
|
||||
- IPC via filesystem for follow-up messages
|
||||
- Credentials injected per-exec (never baked into container)
|
||||
|
||||
## Key Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `src/index.ts` | Orchestrator: state, message loop, agent invocation |
|
||||
| `src/agent-runner.ts` | Spawns agent processes, manages env/sessions/skills |
|
||||
| `src/container-runner.ts` | Docker container execution for reviewer/arbiter |
|
||||
| `src/channels/discord.ts` | Discord channel (8s typing refresh, Whisper transcription) |
|
||||
| `src/ipc.ts` | IPC watcher and task processing |
|
||||
| `src/router.ts` | Message formatting and outbound routing |
|
||||
| `src/config.ts` | Trigger pattern, paths, intervals |
|
||||
| `src/task-scheduler.ts` | Runs scheduled tasks |
|
||||
| `src/db.ts` | SQLite operations |
|
||||
| `runners/agent-runner/` | Claude Code runner (Agent SDK) |
|
||||
| `runners/codex-runner/` | Codex runner (SDK, `codex exec` wrapper) |
|
||||
| `groups/{name}/CLAUDE.md` | Per-group memory (isolated) |
|
||||
83
docs/configuration.md
Normal file
83
docs/configuration.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# Configuration
|
||||
|
||||
All configuration in a single `.env` file.
|
||||
|
||||
## Discord Bots
|
||||
|
||||
```bash
|
||||
DISCORD_BOT_TOKEN= # Claude bot
|
||||
DISCORD_CODEX_BOT_TOKEN= # Codex-main bot (owner)
|
||||
DISCORD_REVIEW_BOT_TOKEN= # Codex-review bot (arbiter)
|
||||
```
|
||||
|
||||
## Agent Types & Models
|
||||
|
||||
```bash
|
||||
# Agent types per role
|
||||
OWNER_AGENT_TYPE=codex # codex | claude-code
|
||||
REVIEWER_AGENT_TYPE=claude-code # claude-code | codex
|
||||
ARBITER_AGENT_TYPE=codex # codex | claude-code (optional, enables 3rd agent)
|
||||
|
||||
# Per-role model overrides
|
||||
OWNER_MODEL=gpt-5.4
|
||||
REVIEWER_MODEL=claude-opus-4-6
|
||||
ARBITER_MODEL=gpt-5.4
|
||||
|
||||
# Per-role effort level
|
||||
OWNER_EFFORT=xhigh
|
||||
REVIEWER_EFFORT=high
|
||||
ARBITER_EFFORT=xhigh
|
||||
|
||||
# Per-role fallback toggle
|
||||
REVIEWER_FALLBACK_ENABLED=true # Auto-handoff to codex when Claude exhausted
|
||||
ARBITER_FALLBACK_ENABLED=false
|
||||
|
||||
# Response language
|
||||
AGENT_LANGUAGE=Korean # Injected into all agent prompts
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
```bash
|
||||
CLAUDE_CODE_OAUTH_TOKEN= # Claude Code OAuth token
|
||||
CLAUDE_CODE_OAUTH_TOKENS= # Comma-separated for multi-account rotation
|
||||
OPENAI_API_KEY= # Codex API key (if not using OAuth)
|
||||
```
|
||||
|
||||
## Voice Transcription
|
||||
|
||||
```bash
|
||||
GROQ_API_KEY= # Groq whisper-large-v3-turbo (primary)
|
||||
OPENAI_API_KEY= # OpenAI whisper-1 (fallback)
|
||||
```
|
||||
|
||||
## Mixture of Agents (MoA)
|
||||
|
||||
```bash
|
||||
MOA_ENABLED=true
|
||||
MOA_REF_MODELS=kimi,glm # Configurable reference model list
|
||||
|
||||
# Per-model config (pattern: MOA_{NAME}_{SETTING})
|
||||
MOA_KIMI_MODEL=kimi-k2.5
|
||||
MOA_KIMI_BASE_URL=https://api.kimi.com/coding
|
||||
MOA_KIMI_API_KEY=sk-kimi-xxx
|
||||
MOA_KIMI_API_FORMAT=anthropic
|
||||
|
||||
MOA_GLM_MODEL=glm-5.1
|
||||
MOA_GLM_BASE_URL=https://open.bigmodel.cn/api/anthropic
|
||||
MOA_GLM_API_KEY=xxx
|
||||
MOA_GLM_API_FORMAT=anthropic
|
||||
```
|
||||
|
||||
## Debugging Paths
|
||||
|
||||
| Item | Path |
|
||||
|------|------|
|
||||
| **DB** | `store/messages.db` (shared, WAL mode) |
|
||||
| Service log | `journalctl --user -u ejclaw -f` or `logs/ejclaw.log` |
|
||||
| Per-group logs | `groups/{name}/logs/` |
|
||||
| Claude sessions | `data/sessions/{name}/.claude/` |
|
||||
| Codex sessions | `data/sessions/{name}/.codex/` |
|
||||
| Claude platform rules | `prompts/claude-platform.md` |
|
||||
| Codex platform rules | `prompts/codex-platform.md` |
|
||||
| Global memory | `groups/global/CLAUDE.md` |
|
||||
Reference in New Issue
Block a user