Compare commits
2 Commits
e9dd09a036
...
2d4b136c00
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d4b136c00 | ||
|
|
7197099db5 |
14
README.md
14
README.md
@@ -11,6 +11,8 @@ EJClaw는 Discord 위에서 동작하는 Tribunal 멀티에이전트 개발 보
|
||||
|
||||
원본은 [qwibitai/nanoclaw](https://github.com/qwibitai/nanoclaw)에서 출발했지만, 현재는 EJClaw의 Discord/paired-runtime 구조에 맞게 독립적으로 유지되고 있습니다.
|
||||
|
||||
이 Gitea 저장소는 GitHub `main`을 베이스로 삼고, 운영 환경에서 검증된 로컬 변경(아래 "운영 적용 사항")을 현재 코드 구조에 맞게 재구현해 얹은 배포본입니다.
|
||||
|
||||
## 개요
|
||||
|
||||
- 단일 `ejclaw` 서비스가 owner / reviewer / arbiter 세 역할과 세 Discord 봇을 함께 관리합니다.
|
||||
@@ -32,6 +34,16 @@ EJClaw는 Discord 위에서 동작하는 Tribunal 멀티에이전트 개발 보
|
||||
- `assign_room` 기반 명시적 room assignment
|
||||
- Bun + SQLite 기반 빠른 런타임
|
||||
|
||||
## 운영 적용 사항
|
||||
|
||||
GitHub `main` 베이스 위에 운영 중 검증된 변경을 현재 코드 구조에 맞춰 재구현해 반영했습니다.
|
||||
|
||||
- Discord 출력 마크다운 보존: 일반 문장의 `**bold**`, `~~strike~~`, 백틱 등 emphasis 마커를 렌더링 대신 원문 그대로 표시(백슬래시 이스케이프). 코드블록과 구조화 출력(EJClaw JSON, 링크→인라인 코드)은 의도대로 보존하고, 이스케이프는 Discord 전송 직전 한 번만 적용됩니다.
|
||||
- 2000자 분할 시 코드블록 보존: 코드 펜스(triple backtick)를 인식해 청크 경계에서 코드블록이 깨지지 않도록 분할(서러게이트 페어 안전 처리 포함).
|
||||
- 사용량 윈도우 정렬 프라이머: Codex/Claude 초기화 시점을 맞추기 위한 정렬 신호를 조건 없이 발사.
|
||||
- 리뷰어 무응답 방지: 리뷰어가 verdict 없이 연속 실패할 때 재시도 상한(기본 2회)으로 핑퐁 루프를 끊고 arbiter/사용자로 에스컬레이션.
|
||||
- 대시보드: 등록된 방 별칭을 Discord 채널명과 분리해 보존.
|
||||
|
||||
## Tribunal 시스템
|
||||
|
||||
| 역할 | 현재 기본값 | 설명 |
|
||||
@@ -123,7 +135,7 @@ Discord ──► SQLite (WAL) ──► GroupQueue ──┬──► Owner (ho
|
||||
### 설치
|
||||
|
||||
```bash
|
||||
git clone https://github.com/phj1081/EJClaw.git
|
||||
git clone https://git.tkrmagid.kr/claude-bot/EJClaw.git
|
||||
cd EJClaw
|
||||
bun install
|
||||
bun run build:all
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import path from 'path';
|
||||
|
||||
import { normalizeAgentOutput } from '../agent-protocol.js';
|
||||
import { neutralizeStrayMarkdown, sanitizeForOutbound } from '../router.js';
|
||||
import type { OutboundAttachment } from '../types.js';
|
||||
|
||||
const LOCAL_MARKDOWN_LINK_RE = /\[[^\]\n]*\]\((\/[^)\n]+)\)/g;
|
||||
@@ -46,7 +47,17 @@ export function prepareDiscordOutbound(
|
||||
const structuredOutput =
|
||||
normalized.output?.visibility === 'public' ? normalized.output : null;
|
||||
const outboundText = structuredOutput?.text ?? normalized.result ?? text;
|
||||
const cleanText = sanitizeLocalMarkdownLinks(outboundText);
|
||||
// Redact/strip before escaping so secret patterns (which contain `_`) still
|
||||
// match, then escape stray Discord markdown in free-form agent prose so
|
||||
// emphasis markers (**bold**, ~~strike~~, …) render literally. Author-
|
||||
// controlled structured envelopes are intentional, so their text is left
|
||||
// untouched. Escaping runs before sanitizeLocalMarkdownLinks so the inline
|
||||
// code it deliberately produces is preserved.
|
||||
const safeText = sanitizeForOutbound(outboundText);
|
||||
const renderedText = structuredOutput
|
||||
? safeText
|
||||
: neutralizeStrayMarkdown(safeText);
|
||||
const cleanText = sanitizeLocalMarkdownLinks(renderedText);
|
||||
const attachments =
|
||||
optionAttachments && optionAttachments.length > 0
|
||||
? optionAttachments
|
||||
|
||||
@@ -15,7 +15,7 @@ import { CACHE_DIR } from '../config.js';
|
||||
import { getEnv } from '../env.js';
|
||||
import { logger } from '../logger.js';
|
||||
import { validateOutboundAttachments } from '../outbound-attachments.js';
|
||||
import { formatOutbound } from '../router.js';
|
||||
import { sanitizeForOutbound } from '../router.js';
|
||||
import { hasReviewerLease } from '../service-routing.js';
|
||||
import type {
|
||||
DeleteRecentMessagesByContentOptions,
|
||||
@@ -528,7 +528,9 @@ export class DiscordChannel implements Channel {
|
||||
for (const [name, id] of Object.entries(mentionMap)) {
|
||||
cleaned = cleaned.replace(new RegExp(`@${name}`, 'g'), `<@${id}>`);
|
||||
}
|
||||
cleaned = formatOutbound(cleaned);
|
||||
// Markdown escaping already happened in prepareDiscordOutbound (before
|
||||
// the deliberate link→inline-code step). Here we only strip/redact.
|
||||
cleaned = sanitizeForOutbound(cleaned);
|
||||
|
||||
// Discord has a 2000 character limit per message and 10 attachments per message
|
||||
const MAX_LENGTH = 2000;
|
||||
|
||||
Reference in New Issue
Block a user