docs: prepare 0.2.0 release notes
This commit is contained in:
39
CHANGELOG.md
39
CHANGELOG.md
@@ -1,8 +1,39 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
All notable changes to EJClaw will be documented in this file.
|
이 문서는 EJClaw의 주요 변경 사항을 기록합니다.
|
||||||
|
|
||||||
## [1.2.0](https://github.com/phj1081/EJClaw/compare/v1.1.6...v1.2.0)
|
## [0.2.0] - 2026-04-11
|
||||||
|
|
||||||
[BREAKING] WhatsApp removed from core, now a skill. Run `/add-whatsapp` to re-add (existing auth/groups preserved).
|
### Added
|
||||||
- **fix:** Prevent scheduled tasks from executing twice when container runtime exceeds poll interval (#138, #669)
|
|
||||||
|
- owner / reviewer / arbiter 기반 Tribunal 3-에이전트 흐름 정비
|
||||||
|
- MoA(Mixture of Agents) 기반 외부 모델 의견 수집
|
||||||
|
- `assign_room` 기반 room assignment 모델과 `room_settings` SSOT 정착
|
||||||
|
- 역할별 model / effort / fallback 설정
|
||||||
|
- reviewer host runtime read-only guard 및 host verification 경로
|
||||||
|
- Claude OAuth 멀티 토큰 로테이션
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- reviewer는 owner workspace를 direct mount로 읽는 현재 모델로 정리
|
||||||
|
- final delivery 경로를 단일화해 duplicate final 배달 문제를 줄임
|
||||||
|
- startup precondition을 전용 오류 / exit code 체계로 정리해 crash loop를 막음
|
||||||
|
- deploy 과정에 `migrate-room-registrations`를 포함해 startup strictness와 운영 절차를 맞춤
|
||||||
|
- Bun 기준 단일 quality gate(`bun run check`)로 format / typecheck / test / build를 통합
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- stale paired finalize retry가 이전 attempt의 side effect를 밖으로 새는 문제 수정
|
||||||
|
- reviewer / arbiter intermediate progress가 final 직전에 외부 채팅으로 flush되는 문제 수정
|
||||||
|
- owner final이 progress edit + 새 work item 메시지로 이중 배달되는 문제 수정
|
||||||
|
- owner workspace branch mismatch 시 안전한 auto-repair 또는 visible blocked 처리 추가
|
||||||
|
- stale reviewer workspace 레코드가 옛 경로를 유지해 reviewer가 다른 worktree를 보는 문제 수정
|
||||||
|
- legacy room migration 미실행 상태에서 서비스가 restart loop에 빠지는 문제 완화
|
||||||
|
|
||||||
|
## [0.1.0] - 2026-04-09
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Discord 기반 EJClaw 초기 공개 버전
|
||||||
|
- owner / reviewer / arbiter 역할 고정 봇 구조
|
||||||
|
- auto user notify, loop protection, Discord-independent paired communication 기반 정착
|
||||||
|
|||||||
278
README.md
278
README.md
@@ -1,220 +1,180 @@
|
|||||||
# EJClaw
|
# EJClaw
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
Tribunal multi-agent AI assistant (Owner + Reviewer + Arbiter) over Discord with autonomous paired review and Mixture of Agents.
|
EJClaw는 Discord 위에서 동작하는 Tribunal 멀티에이전트 개발 보조 시스템입니다.
|
||||||
|
사용자 요청은 owner가 받고, reviewer가 자동 리뷰를 수행하며, 필요할 때 arbiter가 교착을 정리합니다.
|
||||||
|
|
||||||
Originally derived from [qwibitai/nanoclaw](https://github.com/qwibitai/nanoclaw), now independently maintained as EJClaw.
|
원본은 [qwibitai/nanoclaw](https://github.com/qwibitai/nanoclaw)에서 출발했지만, 현재는 EJClaw의 Discord/paired-runtime 구조에 맞게 독립적으로 유지되고 있습니다.
|
||||||
Prompt design inspired by [Q00/ouroboros](https://github.com/Q00/ouroboros) and [garrytan/gstack](https://github.com/garrytan/gstack), adapted for EJClaw's Discord and dual-service workflow.
|
|
||||||
Tribunal arbiter system inspired by multi-agent consensus architectures.
|
|
||||||
|
|
||||||
## Overview
|
## 개요
|
||||||
|
|
||||||
A single unified service (`ejclaw`) runs a **Tribunal** of three roles while
|
- 단일 `ejclaw` 서비스가 owner / reviewer / arbiter 세 역할과 세 Discord 봇을 함께 관리합니다.
|
||||||
managing three Discord bots in one process:
|
- 사용자 진입점은 owner 하나이며, reviewer와 arbiter는 내부 역할로 동작합니다.
|
||||||
|
- room-level 설정은 `room_settings`를 기준으로 하며, `assign_room`이 공개 assignment 인터페이스입니다.
|
||||||
|
- reviewer는 owner의 현재 worktree를 direct mount로 읽고, role-scoped read-only 보호를 적용받습니다.
|
||||||
|
- paired runtime은 SQLite(WAL), stable owner worktree, turn/lease 추적, host verification으로 구성됩니다.
|
||||||
|
|
||||||
| Role | Purpose |
|
## 핵심 기능
|
||||||
|------|---------|
|
|
||||||
| **Owner** | Handles user requests, writes code |
|
|
||||||
| **Reviewer** | Critically reviews owner's work, verifies design direction |
|
|
||||||
| **Arbiter** | On-demand deadlock breaker between owner and reviewer |
|
|
||||||
|
|
||||||
The identity layer is role-fixed:
|
- Tribunal 3-에이전트 루프: owner / reviewer / arbiter
|
||||||
|
- Mixture of Agents(MoA): 외부 모델 의견을 arbiter 판단에 주입
|
||||||
|
- 역할별 agent type / model / effort 설정
|
||||||
|
- role-fixed Discord 봇 3개 체계
|
||||||
|
- reviewer host runtime + read-only guard
|
||||||
|
- 승인 후 변경 감지와 재리뷰
|
||||||
|
- Claude 장애 시 Codex로 넘기는 global failover
|
||||||
|
- Claude OAuth 멀티 토큰 로테이션
|
||||||
|
- `assign_room` 기반 명시적 room assignment
|
||||||
|
- Bun + SQLite 기반 빠른 런타임
|
||||||
|
|
||||||
- **Owner bot** — Handles the owner turn output slot.
|
## Tribunal 시스템
|
||||||
- **Reviewer bot** — Handles the reviewer turn output slot.
|
|
||||||
- **Arbiter bot** — Handles the arbiter turn output slot.
|
|
||||||
|
|
||||||
Each role's agent type and model are independently configurable via `.env`
|
| 역할 | 현재 기본값 | 설명 |
|
||||||
(`OWNER_AGENT_TYPE`, `REVIEWER_AGENT_TYPE`, `ARBITER_AGENT_TYPE`, `*_MODEL`).
|
| --- | --- | --- |
|
||||||
Three Discord bots provide the identity layer — which bot speaks is determined
|
| Owner | room별 `owner_agent_type` (기본 Codex) | 사용자 요청 처리, 코드 작성, 최종 응답 |
|
||||||
by the active role, not hardcoded.
|
| Reviewer | 전역 `REVIEWER_AGENT_TYPE` (기본 Claude Code) | owner 결과 비판적 리뷰, 회귀 검증 |
|
||||||
|
| Arbiter | 전역 `ARBITER_AGENT_TYPE` (옵션) | owner/reviewer 교착 시 판정 |
|
||||||
|
|
||||||
## Room Assignment Model
|
```text
|
||||||
|
사용자 메시지
|
||||||
Per-room routing now uses an explicit assignment model:
|
→ Owner 응답
|
||||||
|
→ Reviewer 자동 실행
|
||||||
- `room_settings` is the room-level source of truth (SSOT)
|
→ verdict:
|
||||||
- Each room stores:
|
DONE → Owner finalize → 완료
|
||||||
- `room_mode`: `single` or `tribunal`
|
DONE_WITH_CONCERNS → Owner 수정 → 재리뷰 루프
|
||||||
- `owner_agent_type`: `codex` or `claude-code`
|
|
||||||
- Public room assignment uses `assign_room`
|
|
||||||
- Legacy `register_group` public interfaces were removed
|
|
||||||
- `registered_groups` remains as a materialized capability/read-model layer and legacy fallback, not the authoritative room configuration
|
|
||||||
|
|
||||||
Operationally:
|
|
||||||
|
|
||||||
- `single` → one owner bot
|
|
||||||
- `tribunal` → per-room owner + globally configured reviewer + optional arbiter
|
|
||||||
|
|
||||||
This means tribunal is no longer inferred from “two bots registered on one room”; it is an explicit room setting.
|
|
||||||
|
|
||||||
## Tribunal Flow
|
|
||||||
|
|
||||||
```
|
|
||||||
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
|
BLOCKED/NEEDS_CONTEXT
|
||||||
├─ Arbiter enabled → Arbiter judges → PROCEED/REVISE/RESET/ESCALATE
|
├─ Arbiter enabled → Arbiter 판정
|
||||||
└─ Arbiter disabled → Escalate to user → @user ⚠️
|
└─ Arbiter disabled → 사용자로 에스컬레이션
|
||||||
→ Owner BLOCKED/NEEDS_CONTEXT → Arbiter (same path as reviewer)
|
→ 왕복이 누적되면 arbiter 자동 요청 가능
|
||||||
→ Deadlock (2+ round trips without progress)
|
|
||||||
→ Arbiter summoned → binding verdict → loop resumes
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Mixture of Agents (MoA)
|
### MoA
|
||||||
|
|
||||||
When enabled, the arbiter collects opinions from external models (Kimi, GLM, etc.) before rendering its verdict:
|
MoA가 켜져 있으면 arbiter가 판정하기 전에 Kimi, GLM 같은 외부 모델 의견을 병렬 수집하고, 그 결과를 arbiter 프롬프트에 주입합니다. 최종 판정은 여전히 EJClaw arbiter가 내립니다.
|
||||||
|
|
||||||
```
|
## 방 설정 모델
|
||||||
Deadlock detected → MoA reference queries (Kimi + GLM, parallel)
|
|
||||||
→ Opinions injected into arbiter's prompt
|
|
||||||
→ SDK arbiter (subscription-based) aggregates all perspectives
|
|
||||||
→ Final verdict: PROCEED / REVISE / RESET / ESCALATE
|
|
||||||
```
|
|
||||||
|
|
||||||
No extra SDK processes. External references use lightweight API calls (Anthropic-compatible).
|
현재 room 설정의 기준은 다음과 같습니다.
|
||||||
|
|
||||||
## Features
|
- `room_settings`: room-level SSOT
|
||||||
|
- `room_role_overrides`: owner / reviewer / arbiter 역할별 override
|
||||||
|
- `paired_projects`: canonical project root
|
||||||
|
- `paired_workspaces`: 실제 owner / reviewer 실행 workspace
|
||||||
|
- `registered_groups`: 완전히 제거되지는 않았지만, canonical source가 아니라 compatibility/read-model 성격으로 남아 있는 레이어
|
||||||
|
|
||||||
- **Tribunal 3-agent system** — Owner/reviewer/arbiter with on-demand deadlock resolution
|
운영적으로는:
|
||||||
- **Discord-independent communication** — Agent-to-agent data flows directly via DB, Discord is display-only
|
|
||||||
- **Mixture of Agents** — External model opinions (Kimi, GLM) enrich arbiter verdicts
|
|
||||||
- **Per-role model selection** — `OWNER_MODEL`, `REVIEWER_MODEL`, `ARBITER_MODEL` + effort + fallback toggle
|
|
||||||
- **Host reviewer with read-only guards** — Reviewer runs on host with role-scoped sandbox and guard policy
|
|
||||||
- **Global failover** — Account-level Claude failure → all channels switch to codex, auto-recovers
|
|
||||||
- **Post-approval change detection** — Re-triggers review if owner modifies code after approval
|
|
||||||
- **Auto user notification** — @mention on task completion (✅ done, ⚠️ escalated)
|
|
||||||
- **Loop protection** — Deadlock threshold, merge_ready oscillation guard, arbiter re-invocation limit
|
|
||||||
- **Voice transcription** — Groq Whisper (primary) / OpenAI Whisper (fallback)
|
|
||||||
- **Token rotation** — Multi-account Claude/Codex rotation on rate limits
|
|
||||||
- **Kimi usage dashboard** — Coding plan 5h/7d usage displayed alongside Claude/Codex
|
|
||||||
- **MCP integration** — Memento (persistent memory) + EJClaw host tools
|
|
||||||
- **Session persistence** — Separate sessions per role (owner/reviewer/arbiter)
|
|
||||||
- **Scheduled tasks** — Cron/interval/once via MCP tool
|
|
||||||
- **Mid-turn steering** — Inject follow-up messages while agent is working
|
|
||||||
- **Bun runtime** — Native SQLite (bun:sqlite), fast startup, no native addon builds
|
|
||||||
|
|
||||||
## Architecture
|
- `single` → owner만 실행
|
||||||
|
- `tribunal` → owner + reviewer + optional arbiter
|
||||||
|
|
||||||
```
|
중요한 점:
|
||||||
|
|
||||||
|
- reviewer는 더 이상 snapshot copy를 기본 실행 경로로 쓰지 않습니다.
|
||||||
|
- reviewer는 owner의 현재 workspace를 direct mount로 읽습니다.
|
||||||
|
- stale reviewer workspace 레코드가 남아 있어도 실행 직전 owner 현재 경로로 재동기화됩니다.
|
||||||
|
|
||||||
|
## 아키텍처
|
||||||
|
|
||||||
|
```text
|
||||||
Discord ──► SQLite (WAL) ──► GroupQueue ──┬──► Owner (host process)
|
Discord ──► SQLite (WAL) ──► GroupQueue ──┬──► Owner (host process)
|
||||||
│ │
|
│ │
|
||||||
│ ▼ (auto-trigger)
|
│ ▼
|
||||||
├──► Reviewer (host process, read-only guarded)
|
├──► Reviewer (host process, read-only)
|
||||||
│ │
|
│ │
|
||||||
│ Verdict routing
|
│ verdict routing
|
||||||
│ ├─ DONE → change detection → finalize or re-review
|
│ ├─ DONE → finalize
|
||||||
│ ├─ BLOCKED → Arbiter (if enabled) or User
|
│ ├─ feedback → owner loop
|
||||||
│ └─ Feedback → Owner (loop)
|
│ └─ BLOCKED → arbiter / user
|
||||||
│
|
│
|
||||||
├──► Arbiter (on-demand, fresh session each time)
|
├──► Arbiter (on-demand, fresh session)
|
||||||
│ │
|
│ │
|
||||||
│ ┌───┴─── MoA (if enabled) ───┐
|
│ ┌───┴─── MoA ───┐
|
||||||
│ │ Kimi API ──► opinion │
|
│ │ Kimi / GLM │
|
||||||
│ │ GLM API ──► opinion │
|
│ │ 의견 수집 │
|
||||||
│ │ → injected into prompt │
|
│ └───────────────┘
|
||||||
│ └────────────────────────────┘
|
|
||||||
│ │
|
|
||||||
│ PROCEED/REVISE/RESET/ESCALATE
|
|
||||||
│
|
│
|
||||||
IPC polling ◄── follow-up messages
|
IPC follow-up / host tools
|
||||||
│
|
│
|
||||||
┌────────── Router ──────────┐
|
┌────────── Router ──────────┐
|
||||||
▼ ▼
|
▼ ▼
|
||||||
paired_turn_outputs Discord (display only)
|
paired_turn_outputs Discord display
|
||||||
(agent ↔ agent data) (user observation, @mention)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Setup
|
## 시작하기
|
||||||
|
|
||||||
### Prerequisites
|
### 요구사항
|
||||||
|
|
||||||
- Linux (Ubuntu 22.04+) or macOS
|
- Linux(Ubuntu 22.04+) 또는 macOS
|
||||||
- [Bun](https://bun.sh/) 1.3+
|
- [Bun](https://bun.sh/) 1.3+
|
||||||
- [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code)
|
- Claude Code CLI
|
||||||
- [Codex CLI](https://github.com/openai/codex) (`npm install -g @openai/codex`)
|
- Codex CLI
|
||||||
- Discord bot tokens (3: owner, reviewer, arbiter)
|
- Discord 봇 토큰 3개(owner / reviewer / arbiter)
|
||||||
|
|
||||||
### Install
|
### 설치
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/phj1081/EJClaw.git
|
git clone https://github.com/phj1081/EJClaw.git
|
||||||
cd EJClaw
|
cd EJClaw
|
||||||
bun install
|
bun install
|
||||||
bun run build:runners
|
bun run build:all
|
||||||
bun run build
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Documentation
|
### 환경 설정
|
||||||
|
|
||||||
- [Architecture](docs/architecture.md) — Data flow, room model, verification execution, key files
|
- `.env.example`를 기준으로 `.env`를 작성합니다.
|
||||||
- [Configuration](docs/configuration.md) — Full `.env` reference, debugging paths
|
- 전체 키 설명은 [docs/configuration.md](docs/configuration.md)를 봅니다.
|
||||||
|
- 최소한 아래는 먼저 채워야 합니다.
|
||||||
### Environment
|
|
||||||
|
|
||||||
All configuration in a single `.env` file:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Discord bots (canonical role-fixed names)
|
DISCORD_OWNER_BOT_TOKEN=
|
||||||
DISCORD_OWNER_BOT_TOKEN= # Owner bot
|
DISCORD_REVIEWER_BOT_TOKEN=
|
||||||
DISCORD_REVIEWER_BOT_TOKEN= # Reviewer bot
|
DISCORD_ARBITER_BOT_TOKEN=
|
||||||
DISCORD_ARBITER_BOT_TOKEN= # Arbiter bot
|
CLAUDE_CODE_OAUTH_TOKENS=
|
||||||
|
OWNER_AGENT_TYPE=codex
|
||||||
# Old service-based token names are no longer accepted.
|
REVIEWER_AGENT_TYPE=claude-code
|
||||||
# Rename existing values to the canonical role-based keys above.
|
|
||||||
|
|
||||||
# Agent types
|
|
||||||
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
|
|
||||||
|
|
||||||
# API keys
|
|
||||||
CLAUDE_CODE_OAUTH_TOKEN= # Claude Code OAuth token
|
|
||||||
CLAUDE_CODE_OAUTH_TOKENS= # Comma-separated for multi-account rotation
|
|
||||||
GROQ_API_KEY= # Voice transcription (Groq Whisper)
|
|
||||||
|
|
||||||
# Mixture of Agents (MoA)
|
|
||||||
MOA_ENABLED=true
|
|
||||||
MOA_REF_MODELS=kimi,glm
|
|
||||||
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
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Deploy
|
### 실행
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### 배포
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun run deploy
|
bun run deploy
|
||||||
```
|
```
|
||||||
|
|
||||||
## Development
|
이 스크립트는 다음을 순서대로 수행합니다.
|
||||||
|
|
||||||
|
1. 최신 커밋 fast-forward pull
|
||||||
|
2. 전체 build
|
||||||
|
3. `migrate-room-registrations` 실행
|
||||||
|
4. `systemctl --user restart ejclaw`
|
||||||
|
|
||||||
|
## 개발
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun run build # Build main project
|
bun run build
|
||||||
bun run build:runners # Install + build both runners
|
bun run build:runners
|
||||||
bun run dev # Dev mode with hot reload
|
bun run test
|
||||||
bun test # Run tests
|
bun run typecheck
|
||||||
|
bun run check
|
||||||
```
|
```
|
||||||
|
|
||||||
## License
|
## 문서
|
||||||
|
|
||||||
MIT — Originally derived from [qwibitai/nanoclaw](https://github.com/qwibitai/nanoclaw)
|
- [docs/architecture.md](docs/architecture.md) — 데이터 모델, 실행 흐름, 주요 파일
|
||||||
|
- [docs/configuration.md](docs/configuration.md) — `.env` 키와 디버깅 경로
|
||||||
|
- [docs/legacy-compat-removal-spec.md](docs/legacy-compat-removal-spec.md) — 남아 있는 레거시 제거 계획
|
||||||
|
- [CHANGELOG.md](CHANGELOG.md) — 릴리즈 이력
|
||||||
|
|
||||||
|
## 라이선스
|
||||||
|
|
||||||
|
MIT
|
||||||
|
|||||||
@@ -1,107 +1,118 @@
|
|||||||
# Architecture
|
# 아키텍처
|
||||||
|
|
||||||
## Service Stack
|
## 서비스 구성
|
||||||
|
|
||||||
Single unified service (`ejclaw`) manages all three Discord bots in one process:
|
EJClaw는 단일 `ejclaw` 서비스가 세 Discord 봇과 paired runtime을 함께 운영하는 구조입니다.
|
||||||
|
|
||||||
- `ejclaw.service` — Single unified process
|
- `ejclaw.service`: 단일 unified process
|
||||||
- Discord bots: `DISCORD_OWNER_BOT_TOKEN` (owner), `DISCORD_REVIEWER_BOT_TOKEN` (reviewer), `DISCORD_ARBITER_BOT_TOKEN` (arbiter)
|
- Discord 봇:
|
||||||
- Paired review: owner ↔ reviewer (agent types configurable per role)
|
- `DISCORD_OWNER_BOT_TOKEN`
|
||||||
- Reviewer fallback: Claude exhaustion → codex-review auto-handoff
|
- `DISCORD_REVIEWER_BOT_TOKEN`
|
||||||
- Shared dirs: `store/`, `groups/`, `data/`
|
- `DISCORD_ARBITER_BOT_TOKEN`
|
||||||
- SQLite WAL mode + `busy_timeout=5000` for concurrent access
|
- 저장소:
|
||||||
|
- `store/`: SQLite DB
|
||||||
|
- `groups/`: room별 로그 / 메모리 / 설정
|
||||||
|
- `data/`: 세션, worktree, 런타임 보조 데이터
|
||||||
|
- SQLite는 WAL + `busy_timeout=5000` 기준으로 동작
|
||||||
|
|
||||||
## Data Flow
|
## 핵심 데이터 모델
|
||||||
|
|
||||||
```
|
| 구성 요소 | 역할 |
|
||||||
|
| --- | --- |
|
||||||
|
| `room_settings` | room-level SSOT |
|
||||||
|
| `room_role_overrides` | 역할별 agent type / agentConfig override |
|
||||||
|
| `paired_projects` | canonical project root (`canonical_work_dir`) |
|
||||||
|
| `paired_tasks` | paired runtime의 상태 머신 |
|
||||||
|
| `paired_workspaces` | owner / reviewer의 실제 실행 workspace 경로 |
|
||||||
|
| `registered_groups` | compatibility / materialized read-model 성격의 잔존 레이어 |
|
||||||
|
|
||||||
|
현재 기준에서 중요한 점:
|
||||||
|
|
||||||
|
- room 설정의 기준은 `room_settings`
|
||||||
|
- reviewer / arbiter는 room의 공개 진입점이 아니라 내부 역할
|
||||||
|
- `registered_groups`는 제거 진행 대상이지만 아직 완전히 사라진 것은 아님
|
||||||
|
|
||||||
|
## 실행 흐름
|
||||||
|
|
||||||
|
```text
|
||||||
Discord ──► SQLite (WAL) ──► GroupQueue ──┬──► Owner (host process)
|
Discord ──► SQLite (WAL) ──► GroupQueue ──┬──► Owner (host process)
|
||||||
│ │
|
│ │
|
||||||
│ ▼ (auto-trigger)
|
│ ▼
|
||||||
├──► Reviewer (host process, read-only guarded)
|
├──► Reviewer (host process, read-only)
|
||||||
│ │
|
│ │
|
||||||
│ Verdict routing
|
│ verdict routing
|
||||||
│ ├─ DONE → change detection → finalize or re-review
|
│ ├─ DONE → owner finalize
|
||||||
│ ├─ BLOCKED → Arbiter (if enabled) or User
|
│ ├─ feedback → owner loop
|
||||||
│ └─ Feedback → Owner (loop)
|
│ └─ BLOCKED → arbiter / user
|
||||||
│
|
│
|
||||||
├──► Arbiter (on-demand, fresh session each time)
|
├──► Arbiter (on-demand)
|
||||||
│ │
|
│ │
|
||||||
│ ┌───┴─── MoA (if enabled) ───┐
|
│ ┌───┴─── MoA ───┐
|
||||||
│ │ Ref model A ──► opinion │
|
│ │ Kimi / GLM │
|
||||||
│ │ Ref model B ──► opinion │
|
│ │ 의견 수집 │
|
||||||
│ │ → injected into prompt │
|
│ └───────────────┘
|
||||||
│ └────────────────────────────┘
|
|
||||||
│ │
|
|
||||||
│ PROCEED/REVISE/RESET/ESCALATE
|
|
||||||
│
|
│
|
||||||
IPC polling ◄── follow-up messages
|
IPC polling / host tools
|
||||||
│
|
│
|
||||||
┌────────── Router ──────────┐
|
┌────────── Router ──────────┐
|
||||||
▼ ▼
|
▼ ▼
|
||||||
paired_turn_outputs Discord (display only)
|
paired_turn_outputs Discord display
|
||||||
(agent ↔ agent data) (user observation, @mention)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Room Assignment Model
|
## Tribunal 역할 분리
|
||||||
|
|
||||||
Per-room routing uses an explicit assignment model:
|
| 역할 | 기본 선택 | 설명 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| owner | room별 `owner_agent_type` (기본 Codex) | 사용자 요청 처리, 코드 작성, 최종 응답 |
|
||||||
|
| reviewer | 전역 `REVIEWER_AGENT_TYPE` (기본 Claude Code) | owner 결과 검토, 회귀 검증 |
|
||||||
|
| arbiter | 전역 `ARBITER_AGENT_TYPE` (옵션) | owner / reviewer 교착 시 판정 |
|
||||||
|
|
||||||
- `room_settings` is the room-level source of truth (SSOT)
|
역할별 model / effort는 전역 env(`OWNER_*`, `REVIEWER_*`, `ARBITER_*`)로 정하고, room-level `agentConfig`는 provider별(`claudeModel`, `codexModel`) override만 제공합니다.
|
||||||
- Each room stores:
|
|
||||||
- `room_mode`: `single` or `tribunal`
|
|
||||||
- `owner_agent_type`: `codex` or `claude-code`
|
|
||||||
- Public room assignment uses `assign_room`
|
|
||||||
- `registered_groups` is a legacy projection being removed from setup/verify/runtime read paths
|
|
||||||
- Refactor target and migration sequence live in `docs/legacy-compat-removal-spec.md`
|
|
||||||
|
|
||||||
Operationally:
|
## Reviewer / Arbiter runtime
|
||||||
|
|
||||||
- `single` → one owner bot
|
- reviewer는 owner의 현재 workspace를 direct mount로 읽습니다
|
||||||
- `tribunal` → per-room owner + globally configured reviewer + optional arbiter
|
- snapshot copy는 현재 기본 실행 경로가 아닙니다
|
||||||
|
- reviewer workspace 레코드가 오래된 경로를 가리키면 실행 직전에 owner 현재 workspace로 재동기화합니다
|
||||||
|
- arbiter는 reviewer와 같은 read-only workspace를 쓰되, 세션 디렉토리는 매 호출마다 fresh하게 준비합니다
|
||||||
|
|
||||||
Tribunal is no longer inferred from "two bots registered on one room"; it is an explicit room setting.
|
즉 reviewer가 보는 실행 경로는:
|
||||||
|
|
||||||
## Tribunal 3-Agent System
|
1. `paired_projects.canonical_work_dir` 같은 canonical root와 별개로
|
||||||
|
2. `paired_workspaces.workspace_dir`에 저장된 실제 owner worktree
|
||||||
|
|
||||||
```
|
입니다.
|
||||||
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 (2+ round trips without progress)
|
|
||||||
→ Arbiter summoned → binding verdict → loop resumes
|
|
||||||
```
|
|
||||||
|
|
||||||
## Verification Execution
|
## 세션 / 프롬프트 구성
|
||||||
|
|
||||||
Reviewer and arbiter now run as host processes with role-scoped read-only
|
- owner는 stable worktree + stable session을 사용
|
||||||
guards and sandbox settings. Verification profiles run directly on the host with
|
- reviewer / arbiter는 read-only 세션 디렉토리를 매 실행 전에 다시 준비
|
||||||
restricted environment variables and snapshot checks:
|
- `prepareReadonlySessionEnvironment()`가 `CLAUDE.md`, `.codex/AGENTS.md`, 설정 파일을 매번 재생성
|
||||||
|
- 그래서 reviewer 관련 프롬프트 / workspace 변경은 기존 실행 중 프로세스에는 즉시 적용되지 않지만, **다음 reviewer 턴부터는 자동 반영**됩니다
|
||||||
|
|
||||||
- Fixed-profile direct execution (`test`, `typecheck`, `build`)
|
## 검증 / 운영 경로
|
||||||
- Snapshot comparison before/after execution
|
|
||||||
- Root-level runtime/build directories excluded from verification snapshot
|
|
||||||
- Role-scoped reviewer safeguards remain independent of verification execution
|
|
||||||
|
|
||||||
## Key Files
|
- 검증 명령은 `bun run check` 하나로 묶여 있음
|
||||||
|
- format
|
||||||
|
- typecheck
|
||||||
|
- test
|
||||||
|
- build
|
||||||
|
- reviewer / arbiter가 직접 로컬 빌드를 못 돌려도, host verification 경로로 `typecheck`, `test`, `build`를 수행할 수 있음
|
||||||
|
- startup precondition은 전용 오류로 올리고, `RestartPreventExitStatus=78`로 crash loop를 막음
|
||||||
|
- deploy는 `migrate-room-registrations`를 선행한 뒤 service restart를 수행
|
||||||
|
|
||||||
| File | Purpose |
|
## 주요 파일
|
||||||
| ------------------------- | ---------------------------------------------------------- |
|
|
||||||
| `src/index.ts` | Orchestrator: state, message loop, agent invocation |
|
| 파일 | 역할 |
|
||||||
| `src/agent-runner.ts` | Spawns agent processes, manages env/sessions/skills |
|
| --- | --- |
|
||||||
| `src/verification.ts` | Fixed-profile verification execution with snapshot checks |
|
| `src/index.ts` | 전체 오케스트레이션 진입점 |
|
||||||
| `src/channels/discord.ts` | Discord channel (8s typing refresh, Whisper transcription) |
|
| `src/message-runtime.ts` | 메시지 루프, paired flow 연결 |
|
||||||
| `src/ipc.ts` | IPC watcher and task processing |
|
| `src/message-turn-controller.ts` | progress / final delivery 제어 |
|
||||||
| `src/router.ts` | Message formatting and outbound routing |
|
| `src/paired-execution-context.ts` | owner / reviewer / arbiter 실행 준비 |
|
||||||
| `src/config.ts` | Trigger pattern, paths, intervals |
|
| `src/paired-workspace-manager.ts` | owner / reviewer workspace 관리 |
|
||||||
| `src/task-scheduler.ts` | Runs scheduled tasks |
|
| `src/agent-runner.ts` | host process spawn, env/session wiring |
|
||||||
| `src/db.ts` | SQLite operations |
|
| `src/db.ts` | 런타임 DB facade |
|
||||||
| `runners/agent-runner/` | Claude Code runner (Agent SDK) |
|
| `src/db/` | canonical room / paired state / migration 로직 |
|
||||||
| `runners/codex-runner/` | Codex runner (SDK, `codex exec` wrapper) |
|
| `runners/agent-runner/` | Claude Code runner |
|
||||||
| `groups/{name}/CLAUDE.md` | Per-group memory (isolated) |
|
| `runners/codex-runner/` | Codex runner |
|
||||||
|
| `setup/` | setup / verify / service rendering |
|
||||||
|
|||||||
@@ -1,83 +1,145 @@
|
|||||||
# Configuration
|
# 설정
|
||||||
|
|
||||||
All configuration in a single `.env` file.
|
EJClaw의 설정 기준은 `.env` 하나입니다.
|
||||||
|
프로젝트 루트의 [`../.env.example`](../.env.example)를 기본 예시로 보고, 이 문서는 현재 런타임에서 의미가 있는 키만 추려 설명합니다.
|
||||||
|
|
||||||
## Discord Bots
|
## 기본 원칙
|
||||||
|
|
||||||
|
- Discord 토큰은 role-fixed canonical 키만 사용합니다
|
||||||
|
- reviewer 기본 provider는 `claude-code`
|
||||||
|
- owner 기본 provider는 `codex`
|
||||||
|
- room-level override는 provider 단위(`claudeModel`, `codexModel`)만 지원합니다
|
||||||
|
- `CLAUDE_CODE_OAUTH_TOKENS`가 canonical이고, `CLAUDE_CODE_OAUTH_TOKEN`은 legacy fallback입니다
|
||||||
|
|
||||||
|
## Discord 봇
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
DISCORD_OWNER_BOT_TOKEN= # Owner bot
|
DISCORD_OWNER_BOT_TOKEN=
|
||||||
DISCORD_REVIEWER_BOT_TOKEN= # Reviewer bot
|
DISCORD_REVIEWER_BOT_TOKEN=
|
||||||
DISCORD_ARBITER_BOT_TOKEN= # Arbiter bot
|
DISCORD_ARBITER_BOT_TOKEN=
|
||||||
```
|
```
|
||||||
|
|
||||||
## Agent Types & Models
|
예전 service-based alias는 더 이상 허용되지 않습니다.
|
||||||
|
|
||||||
|
## 역할 / 모델 설정
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Agent types per role
|
# 역할별 provider
|
||||||
OWNER_AGENT_TYPE=codex # codex | claude-code
|
OWNER_AGENT_TYPE=codex
|
||||||
REVIEWER_AGENT_TYPE=claude-code # claude-code | codex
|
REVIEWER_AGENT_TYPE=claude-code
|
||||||
ARBITER_AGENT_TYPE=codex # codex | claude-code (optional, enables 3rd agent)
|
# ARBITER_AGENT_TYPE=claude-code
|
||||||
|
|
||||||
# Per-role model overrides
|
# provider 기본 모델
|
||||||
|
CLAUDE_MODEL=claude-opus-4-6
|
||||||
|
CLAUDE_EFFORT=high
|
||||||
|
CLAUDE_THINKING=adaptive
|
||||||
|
CODEX_MODEL=gpt-5.4
|
||||||
|
CODEX_EFFORT=xhigh
|
||||||
|
|
||||||
|
# 역할별 override
|
||||||
OWNER_MODEL=gpt-5.4
|
OWNER_MODEL=gpt-5.4
|
||||||
REVIEWER_MODEL=claude-opus-4-6
|
|
||||||
ARBITER_MODEL=gpt-5.4
|
|
||||||
|
|
||||||
# Per-role effort level
|
|
||||||
OWNER_EFFORT=xhigh
|
OWNER_EFFORT=xhigh
|
||||||
|
OWNER_FALLBACK_ENABLED=true
|
||||||
|
|
||||||
|
REVIEWER_MODEL=claude-opus-4-6
|
||||||
REVIEWER_EFFORT=high
|
REVIEWER_EFFORT=high
|
||||||
ARBITER_EFFORT=xhigh
|
REVIEWER_FALLBACK_ENABLED=true
|
||||||
|
|
||||||
# Per-role fallback toggle
|
ARBITER_MODEL=claude-sonnet-4-6
|
||||||
REVIEWER_FALLBACK_ENABLED=true # Auto-handoff to codex when Claude exhausted
|
ARBITER_EFFORT=high
|
||||||
ARBITER_FALLBACK_ENABLED=false
|
ARBITER_FALLBACK_ENABLED=true
|
||||||
|
|
||||||
# Response language
|
|
||||||
AGENT_LANGUAGE=Korean # Injected into all agent prompts
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Authentication
|
설명:
|
||||||
|
|
||||||
|
- reviewer / arbiter provider 선택은 **전역 설정**
|
||||||
|
- 특정 방에서만 reviewer를 Codex로 고르는 기능은 없음
|
||||||
|
- room-level `agentConfig`의 `claudeModel`, `codexModel`은 역할별이 아니라 provider별 override
|
||||||
|
- `ARBITER_AGENT_TYPE`은 옵션이며, 설정하지 않으면 arbiter는 비활성 상태입니다
|
||||||
|
|
||||||
|
## 인증
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
CLAUDE_CODE_OAUTH_TOKEN= # Claude Code OAuth token
|
# Claude Code OAuth
|
||||||
CLAUDE_CODE_OAUTH_TOKENS= # Comma-separated for multi-account rotation
|
CLAUDE_CODE_OAUTH_TOKENS=
|
||||||
OPENAI_API_KEY= # Codex API key (if not using OAuth)
|
CLAUDE_CODE_OAUTH_TOKEN=
|
||||||
|
|
||||||
|
# Claude host env (선택)
|
||||||
|
ANTHROPIC_API_KEY=
|
||||||
|
ANTHROPIC_AUTH_TOKEN=
|
||||||
|
ANTHROPIC_BASE_URL=
|
||||||
```
|
```
|
||||||
|
|
||||||
## Voice Transcription
|
설명:
|
||||||
|
|
||||||
|
- `CLAUDE_CODE_OAUTH_TOKENS`: canonical, 쉼표 구분
|
||||||
|
- `CLAUDE_CODE_OAUTH_TOKEN`: 단일 토큰 legacy fallback
|
||||||
|
- 실제 runner에는 현재 선택된 Claude 토큰 하나만 주입됩니다
|
||||||
|
|
||||||
|
Codex 쪽은 현재 **OAuth 세션 파일** 기준으로 동작합니다. `OPENAI_API_KEY`를 Codex child process에 넘겨서 빌링하는 구조는 사용하지 않습니다.
|
||||||
|
|
||||||
|
## 음성 전사
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
GROQ_API_KEY= # Groq whisper-large-v3-turbo (primary)
|
GROQ_API_KEY=
|
||||||
OPENAI_API_KEY= # OpenAI whisper-1 (fallback)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Mixture of Agents (MoA)
|
- 기본은 Groq Whisper
|
||||||
|
- 필요 시 OpenAI Whisper fallback을 별도 확장할 수 있지만, 현재 README / 문서 기준 최소 키는 `GROQ_API_KEY`입니다
|
||||||
|
|
||||||
|
## MoA
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
MOA_ENABLED=true
|
MOA_ENABLED=true
|
||||||
MOA_REF_MODELS=kimi,glm # Configurable reference model list
|
MOA_REF_MODELS=kimi,glm
|
||||||
|
|
||||||
# Per-model config (pattern: MOA_{NAME}_{SETTING})
|
|
||||||
MOA_KIMI_MODEL=kimi-k2.5
|
MOA_KIMI_MODEL=kimi-k2.5
|
||||||
MOA_KIMI_BASE_URL=https://api.kimi.com/coding
|
MOA_KIMI_BASE_URL=https://api.kimi.com/coding
|
||||||
MOA_KIMI_API_KEY=sk-kimi-xxx
|
MOA_KIMI_API_KEY=sk-kimi-xxx
|
||||||
MOA_KIMI_API_FORMAT=anthropic
|
MOA_KIMI_API_FORMAT=anthropic
|
||||||
|
|
||||||
MOA_GLM_MODEL=glm-5.1
|
MOA_GLM_MODEL=glm-4-plus
|
||||||
MOA_GLM_BASE_URL=https://open.bigmodel.cn/api/anthropic
|
MOA_GLM_BASE_URL=https://open.bigmodel.cn/api/paas/v4
|
||||||
MOA_GLM_API_KEY=xxx
|
MOA_GLM_API_KEY=xxx
|
||||||
MOA_GLM_API_FORMAT=anthropic
|
MOA_GLM_API_FORMAT=anthropic
|
||||||
```
|
```
|
||||||
|
|
||||||
## Debugging Paths
|
MoA는 arbiter 판정 전에 외부 모델 의견을 수집해 prompt에 주입합니다.
|
||||||
|
`API_FORMAT` 같은 항목은 `.env.example`의 최소 예시에 없는 운영 확장 키입니다.
|
||||||
|
|
||||||
| Item | Path |
|
## 운영 / 배포 관련 설정
|
||||||
|------|------|
|
|
||||||
| **DB** | `store/messages.db` (shared, WAL mode) |
|
```bash
|
||||||
| Service log | `journalctl --user -u ejclaw -f` or `logs/ejclaw.log` |
|
ASSISTANT_NAME=claude
|
||||||
| Per-group logs | `groups/{name}/logs/` |
|
STATUS_CHANNEL_ID=
|
||||||
| Claude sessions | `data/sessions/{name}/.claude/` |
|
SESSION_COMMAND_ALLOWED_SENDERS=
|
||||||
| Codex sessions | `data/sessions/{name}/.codex/` |
|
MAX_CONCURRENT_AGENTS=5
|
||||||
| Claude platform rules | `prompts/claude-platform.md` |
|
```
|
||||||
| Codex platform rules | `prompts/codex-platform.md` |
|
|
||||||
| Global memory | `groups/global/CLAUDE.md` |
|
- `ASSISTANT_NAME`은 owner trigger 기본 이름을 만듭니다
|
||||||
|
- paired room에서도 사용자 진입점은 owner가 기준입니다
|
||||||
|
- status dashboard와 session command는 선택 설정입니다
|
||||||
|
|
||||||
|
## 디버깅 경로
|
||||||
|
|
||||||
|
| 항목 | 경로 / 명령 |
|
||||||
|
| --- | --- |
|
||||||
|
| DB | `store/messages.db` |
|
||||||
|
| 서비스 로그 | `journalctl --user -u ejclaw -f` |
|
||||||
|
| room 로그 | `groups/{folder}/logs/` |
|
||||||
|
| owner/reviewer 세션 | `data/sessions/{folder}*` |
|
||||||
|
| owner worktree | `data/workspaces/{folder}/owner` |
|
||||||
|
| Claude 플랫폼 프롬프트 | `prompts/claude-platform.md` |
|
||||||
|
| reviewer 프롬프트 | `prompts/claude-paired-room.md` |
|
||||||
|
| arbiter 프롬프트 | `prompts/arbiter-paired-room.md` |
|
||||||
|
| Codex 플랫폼 프롬프트 | `prompts/codex-platform.md` |
|
||||||
|
| 글로벌 메모리 | `groups/global/CLAUDE.md` |
|
||||||
|
|
||||||
|
## 문서와 실제 코드의 우선순위
|
||||||
|
|
||||||
|
문서보다 실제 동작이 우선입니다. 동작 기준을 확인할 때는 아래를 먼저 봅니다.
|
||||||
|
|
||||||
|
1. `.env.example`
|
||||||
|
2. `src/config/load-config.ts`
|
||||||
|
3. `src/agent-runner-environment.ts`
|
||||||
|
4. `src/paired-execution-context.ts`
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "ejclaw",
|
"name": "ejclaw",
|
||||||
"version": "0.1.0",
|
"version": "0.2.0",
|
||||||
"description": "Personal Claude assistant. Lightweight, secure, customizable.",
|
"description": "Discord tribunal multi-agent assistant with owner, reviewer, and arbiter roles.",
|
||||||
"packageManager": "bun@1.3.11",
|
"packageManager": "bun@1.3.11",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
|||||||
Reference in New Issue
Block a user