Eyejoker 008154b57d fix: inject real OAuth token to reviewer container instead of proxy
The credential proxy cached the OAuth token at startup, but Anthropic
no longer supports raw OAuth Bearer tokens on the API. The Claude Code
SDK handles OAuth internally, so the proxy was breaking auth.

Now injects the real token both at container creation and at each
docker exec, so token rotation is picked up without recreating the
container.
2026-03-30 05:14:03 +09:00
2026-03-29 07:33:13 +09:00
2026-02-03 17:14:17 +02:00
2026-03-19 03:31:41 +09:00
2026-03-25 03:57:52 +09:00

EJClaw

Claude Agent SDK Codex SDK Node Discord

Dual-agent AI assistant (Claude Code + Codex) over Discord with autonomous paired review.

Originally derived from qwibitai/nanoclaw, now maintained as EJClaw for personal production use. Prompt design inspired by Q00/ouroboros, adapted for EJClaw's Discord and dual-service workflow.

Overview

A single unified service (ejclaw) manages three Discord bots in one process:

  • Codex-main (@codex) — Owner agent. Handles user requests, writes code.
  • Claude (@claude) — Reviewer agent. Critically reviews owner's work, verifies design direction.
  • Codex-review (@codex-review) — Fallback reviewer. Takes over when Claude hits rate limits (429).

Owner and reviewer agent types are configurable via OWNER_AGENT_TYPE and REVIEWER_AGENT_TYPE in .env.

Paired Review

The core workflow is an autonomous owner-reviewer ping-pong:

User message
  → Owner responds (implementation, answer, etc.)
    → Reviewer auto-triggered (critical review, design check)
      → Verdict:
          DONE              → Owner gets final turn to finalize → Task completed
          DONE_WITH_CONCERNS → Owner addresses feedback → Reviewer re-reviews
          BLOCKED            → Escalate to user (needs decision)
          NEEDS_CONTEXT      → Escalate to user (missing information)

The system stops autonomously when the reviewer approves or escalates. No manual intervention needed for the happy path.

Features

  • Paired review — Autonomous owner/reviewer ping-pong with verdict-based control
  • Container-isolated reviewer — Reviewer runs in persistent Docker container with read-only source mount (kernel-level write protection)
  • Post-approval change detection — Re-triggers review if owner modifies code after reviewer DONE (git tree hash comparison)
  • Configurable agent types — Owner and reviewer roles independently set to claude-code or codex
  • Reviewer fallback — Claude 429/exhaustion → automatic handoff to codex-review
  • Credential proxy — API keys never exposed to containers, injected via HTTP proxy
  • Voice transcription — Groq Whisper (primary) / OpenAI Whisper (fallback), shared file cache with dedup
  • Bidirectional images — Receive Discord attachments as multimodal input, send screenshots back
  • Token rotation — Claude 429 / usage exhaustion → automatic multi-account rotation
  • MCP integration — Memento (persistent memory) + EJClaw host tools (send_message, schedule_task, watch_ci, etc.)
  • Browser automationgstack browse skill, headless Chromium daemon
  • Priority queue — Per-group serialization, global concurrency limit
  • Session persistence — Resume conversations across restarts (separate sessions per role)
  • Scheduled tasks — Cron/interval/once via MCP tool
  • Mid-turn steering — Inject follow-up messages while agent is working

Architecture

Discord ──► SQLite (WAL) ──► GroupQueue ──┬──► Owner (host process)
                                          │       │
                                          │       ▼ (auto-trigger)
                                          └──► Reviewer (Docker container, :ro mount)
                                          │       │
                                          │   Verdict routing
                                          │       ├─ DONE → change detection → Owner finalizes or re-review
                                          │       ├─ BLOCKED/NEEDS_CONTEXT → User
                                          │       └─ Feedback → Owner (loop)
                                          │
                                     IPC polling ◄── follow-up messages (mid-turn steering)
                                          │
                                     Router ──► Discord (text, images, files)

Owner (host process):
  ├── Stable per-channel worktree (session persists across tasks)
  ├── MCP tools, Bash skills, per-group memory
  └── Full read-write access

Reviewer (persistent Docker container):
  ├── Owner workspace mounted read-only (kernel-level protection)
  ├── Credential proxy (API keys never in container)
  ├── pnpm store mounted read-only (if applicable)
  └── tmpfs for test caches (vitest, jest, npm)

Setup

Prerequisites

  • Linux (Ubuntu 22.04+) or macOS
  • Node.js 20+ (24 recommended, fnm for version management)
  • Docker (required for reviewer container isolation)
  • Claude Code CLI
  • Codex CLI (npm install -g @openai/codex)
  • Discord bot tokens (3: Claude, Codex-main, Codex-review)

Install

git clone https://github.com/phj1081/EJClaw.git
cd EJClaw
npm install
npm run build:runners
npm run build

Environment

All configuration in a single .env file:

# Discord bots (3 tokens for 3 bots)
DISCORD_BOT_TOKEN=               # Claude bot
DISCORD_CODEX_BOT_TOKEN=         # Codex-main bot (owner)
DISCORD_REVIEW_BOT_TOKEN=        # Codex-review bot (fallback reviewer)

# Agent type configuration
OWNER_AGENT_TYPE=codex            # codex | claude-code
REVIEWER_AGENT_TYPE=claude-code   # claude-code | codex

# API keys
CLAUDE_CODE_OAUTH_TOKEN=          # Claude Code OAuth token
CLAUDE_CODE_OAUTH_TOKENS=         # Comma-separated tokens for multi-account rotation
OPENAI_API_KEY=                   # For Codex
GROQ_API_KEY=                     # Voice transcription (Groq Whisper)

# Bot names
ASSISTANT_NAME=claude             # Claude bot trigger name
CODEX_ASSISTANT_NAME=codex        # Codex bot trigger name

Authentication

Multi-account OAuth token rotation is supported via CLAUDE_CODE_OAUTH_TOKENS (comma-separated). When one account hits a rate limit, the system automatically rotates to the next.

Token auto-refresh runs on the Claude service only, refreshing access tokens 30 minutes before expiry using rotating refresh tokens from ~/.claude/.credentials.json (account 0) and ~/.claude-accounts/{n}/.credentials.json (account 1+). Generate tokens with claude setup-token (account 0) or CLAUDE_CONFIG_DIR=~/.claude-accounts/{n} claude setup-token (account 1+).

Systemd Service (Linux)

Single unified service:

systemctl --user restart ejclaw    # Restart
systemctl --user status ejclaw     # Check status
systemctl --user enable ejclaw     # Enable on boot
journalctl --user -u ejclaw -f     # Follow logs

Deploy

Build on server, not locally:

ssh clone-ej@100.64.185.108 'cd ~/EJClaw && git pull && npm run build && npm run build:runners && systemctl --user restart ejclaw'

Development

npm run build                # Build main project
npm run build:runners        # Install + build both runners
npm run dev                  # Dev mode with hot reload
npm test                     # Run tests

License

MIT — Originally derived from qwibitai/nanoclaw

Description
EJClaw — GitHub main base + local owner modifications (claude-bot)
Readme MIT 16 MiB
Languages
TypeScript 96.8%
CSS 2.5%
Kotlin 0.4%
JavaScript 0.1%
Shell 0.1%