feat: support codex runner in reviewer container
- Build both agent-runner and codex-runner into container image - Select runner path based on group.agentType at docker exec time - Update README to role-centric overview, generic MoA references - Add MoA reference opinions guidance to arbiter prompt
This commit is contained in:
26
README.md
26
README.md
@@ -14,13 +14,15 @@ Tribunal arbiter system inspired by multi-agent consensus architectures.
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
A single unified service (`ejclaw`) manages three Discord bots in one process:
|
A single unified service (`ejclaw`) runs a **Tribunal** of three roles:
|
||||||
|
|
||||||
- **Codex-main** (`@codex`) — Owner agent. Handles user requests, writes code.
|
| Role | Purpose | Default |
|
||||||
- **Claude** (`@claude`) — Reviewer agent. Critically reviews owner's work, verifies design direction.
|
|------|---------|---------|
|
||||||
- **Codex-review** (`@codex-review`) — Arbiter agent. Summoned on-demand to break deadlocks between owner and reviewer.
|
| **Owner** | Handles user requests, writes code | Codex (`@codex`) |
|
||||||
|
| **Reviewer** | Critically reviews owner's work, verifies design direction | Claude Code (`@claude`) |
|
||||||
|
| **Arbiter** | On-demand deadlock breaker between owner and reviewer | Codex (`@codex-review`) |
|
||||||
|
|
||||||
All agent types and models are independently configurable per role via `.env`.
|
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 by the active role, not hardcoded.
|
||||||
|
|
||||||
## Room Assignment Model
|
## Room Assignment Model
|
||||||
|
|
||||||
@@ -60,22 +62,22 @@ User message
|
|||||||
|
|
||||||
### Mixture of Agents (MoA)
|
### Mixture of Agents (MoA)
|
||||||
|
|
||||||
When enabled, the arbiter collects opinions from external models (Kimi, GLM, etc.) before rendering its verdict:
|
When enabled, the arbiter collects opinions from configurable external reference models before rendering its verdict:
|
||||||
|
|
||||||
```
|
```
|
||||||
Deadlock detected → MoA reference queries (Kimi + GLM, parallel)
|
Deadlock detected → MoA reference queries (parallel, configurable via MOA_REF_MODELS)
|
||||||
→ Opinions injected into arbiter's prompt
|
→ Opinions injected into arbiter's prompt
|
||||||
→ SDK arbiter (subscription-based) aggregates all perspectives
|
→ Arbiter aggregates all perspectives
|
||||||
→ Final verdict: PROCEED / REVISE / RESET / ESCALATE
|
→ Final verdict: PROCEED / REVISE / RESET / ESCALATE
|
||||||
```
|
```
|
||||||
|
|
||||||
No extra SDK processes. External references use lightweight API calls (Anthropic-compatible).
|
No extra SDK processes. External references use lightweight OpenAI/Anthropic-compatible API calls.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- **Tribunal 3-agent system** — Owner/reviewer/arbiter with on-demand deadlock resolution
|
- **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
|
- **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
|
- **Mixture of Agents** — Configurable external reference models enrich arbiter verdicts
|
||||||
- **Per-role model selection** — `OWNER_MODEL`, `REVIEWER_MODEL`, `ARBITER_MODEL` + effort + fallback toggle
|
- **Per-role model selection** — `OWNER_MODEL`, `REVIEWER_MODEL`, `ARBITER_MODEL` + effort + fallback toggle
|
||||||
- **Container-isolated reviewer** — Persistent Docker container with read-only source mount
|
- **Container-isolated reviewer** — Persistent Docker container with read-only source mount
|
||||||
- **Global failover** — Account-level Claude failure → all channels switch to codex, auto-recovers
|
- **Global failover** — Account-level Claude failure → all channels switch to codex, auto-recovers
|
||||||
@@ -107,8 +109,8 @@ Discord ──► SQLite (WAL) ──► GroupQueue ──┬──► Owner (ho
|
|||||||
├──► Arbiter (on-demand, fresh session each time)
|
├──► Arbiter (on-demand, fresh session each time)
|
||||||
│ │
|
│ │
|
||||||
│ ┌───┴─── MoA (if enabled) ───┐
|
│ ┌───┴─── MoA (if enabled) ───┐
|
||||||
│ │ Kimi API ──► opinion │
|
│ │ Ref model A ──► opinion │
|
||||||
│ │ GLM API ──► opinion │
|
│ │ Ref model B ──► opinion │
|
||||||
│ │ → injected into prompt │
|
│ │ → injected into prompt │
|
||||||
│ └────────────────────────────┘
|
│ └────────────────────────────┘
|
||||||
│ │
|
│ │
|
||||||
|
|||||||
@@ -52,18 +52,24 @@ RUN curl -fsSL https://bun.sh/install | bash
|
|||||||
# Create app directory
|
# Create app directory
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Copy host's agent-runner (same code, same SDK version)
|
# Copy agent-runner (Claude Code)
|
||||||
COPY runners/agent-runner/package*.json ./
|
COPY runners/agent-runner/package*.json ./agent/
|
||||||
RUN bun install
|
RUN bun install --cwd ./agent
|
||||||
COPY runners/agent-runner/ ./
|
COPY runners/agent-runner/ ./agent/
|
||||||
RUN bun run build
|
RUN bun run --cwd ./agent build
|
||||||
|
|
||||||
|
# Copy codex-runner
|
||||||
|
COPY runners/codex-runner/package*.json ./codex/
|
||||||
|
RUN bun install --cwd ./codex
|
||||||
|
COPY runners/codex-runner/ ./codex/
|
||||||
|
RUN bun run --cwd ./codex build
|
||||||
|
|
||||||
# Create workspace directories
|
# Create workspace directories
|
||||||
RUN mkdir -p /workspace/project /workspace/group /workspace/global \
|
RUN mkdir -p /workspace/project /workspace/group /workspace/global \
|
||||||
/workspace/ipc/messages /workspace/ipc/input
|
/workspace/ipc/messages /workspace/ipc/input
|
||||||
|
|
||||||
# Create entrypoint script
|
# Entrypoint (fallback — persistent containers use docker exec with explicit runner path)
|
||||||
RUN printf '#!/bin/bash\nset -e\ncat > /tmp/input.json\nbun /app/dist/index.js < /tmp/input.json\n' \
|
RUN printf '#!/bin/bash\nset -e\ncat > /tmp/input.json\nbun /app/agent/dist/index.js < /tmp/input.json\n' \
|
||||||
> /app/entrypoint.sh && chmod +x /app/entrypoint.sh
|
> /app/entrypoint.sh && chmod +x /app/entrypoint.sh
|
||||||
|
|
||||||
# Set ownership to node user (non-root)
|
# Set ownership to node user (non-root)
|
||||||
|
|||||||
@@ -22,6 +22,15 @@ You have been summoned because the owner and reviewer reached a deadlock after m
|
|||||||
- The situation cannot be resolved without user input, regardless of technical agreement
|
- The situation cannot be resolved without user input, regardless of technical agreement
|
||||||
- The same NEEDS_CONTEXT or BLOCKED is repeated after a prior PROCEED — this means your PROCEED did not resolve the issue
|
- The same NEEDS_CONTEXT or BLOCKED is repeated after a prior PROCEED — this means your PROCEED did not resolve the issue
|
||||||
|
|
||||||
|
## MoA (Mixture of Agents) Reference Opinions
|
||||||
|
|
||||||
|
You may receive reference opinions from external models appended to your prompt. When present:
|
||||||
|
|
||||||
|
- **Cite them explicitly** in your verdict — e.g., "Reference model A agrees that...", "Reference model B raises a concern about..."
|
||||||
|
- **Cross-reference** their opinions against the owner/reviewer conversation and code evidence
|
||||||
|
- **Resolve conflicts** — if reference opinions disagree with each other or with owner/reviewer, state which view you adopt and why
|
||||||
|
- Do NOT blindly follow reference opinions — they are inputs to your judgment, not authorities
|
||||||
|
|
||||||
## Rules
|
## Rules
|
||||||
|
|
||||||
- Base your verdict on evidence (code, test output, logs), not on who said what first
|
- Base your verdict on evidence (code, test output, logs), not on who said what first
|
||||||
|
|||||||
@@ -484,7 +484,11 @@ export async function runReviewerContainer(args: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
execArgs.push(containerName, 'bun', '/app/dist/index.js');
|
const isCodexAgent = (group.agentType || 'claude-code') === 'codex';
|
||||||
|
const runnerPath = isCodexAgent
|
||||||
|
? '/app/codex/dist/index.js'
|
||||||
|
: '/app/agent/dist/index.js';
|
||||||
|
execArgs.push(containerName, 'bun', runnerPath);
|
||||||
|
|
||||||
return new Promise<AgentOutput>((resolve) => {
|
return new Promise<AgentOutput>((resolve) => {
|
||||||
const proc = spawn(CONTAINER_RUNTIME_BIN, execArgs, {
|
const proc = spawn(CONTAINER_RUNTIME_BIN, execArgs, {
|
||||||
|
|||||||
Reference in New Issue
Block a user