- Replace better-sqlite3 with bun:sqlite (native, no native addon build)
- Change all spawn('node') to spawn('bun') for agent processes
- Update package.json scripts: node→bun, tsx→bun, npm→bun
- Add bun-types for tsc compatibility
- Add bun:sqlite→better-sqlite3 shim for vitest (tests run on Node.js)
- Update Dockerfile: install bun alongside Node.js (CLIs need Node)
- Update setup/platform.ts: getNodePath() resolves bun binary
- Remove better-sqlite3 from production dependencies (devDep only for tests)
prependRoomRoleHeader was called BEFORE /compact detection, turning
"/compact" into "[PAIRED ROOM: owner]\n/compact" which failed the
exact match check. Now session commands are detected first.
- Remove UNIFIED_MODE legacy: single service manages all 3 Discord bots
- Add OWNER_AGENT_TYPE / REVIEWER_AGENT_TYPE env vars for configurable agent selection
- Fix Discord channel routing: reviewer output goes through correct bot (discord/discord-review)
- Fix channel name assignment: explicit names prevent agentTypeFilter collision
- Remove silent output suppression system: harness-level protections replace prompt workarounds
- Add reviewer approval detection: DONE marker on first line stops ping-pong
- Include user's original message in reviewer prompt for context
- Fix round_trip_count auto-reset on new user message
- Fix session ID conflict: reviewer doesn't reuse owner's session
- Fix pending progress text flush in runner on close sentinel
- Promote buffered intermediate text to final result when result event has no text
- Remove legacy files: .env.codex.example, .env.codex-review.example, migrate-unify.cjs
- Update CLAUDE.md: server-side build deployment, unified architecture docs
When /stop sends SIGTERM, the runner now calls AbortController.abort()
on the Claude SDK query, allowing graceful cleanup of in-flight API
requests before the process exits. Falls back to SIGKILL after 5s.
Prevent duplicate CI completion notifications in paired rooms by checking
for existing active watchers with the same channel+provider+metadata before
creating a new one. Remove Task ID from watcher prompt construction to
avoid router secret redaction (task- IDs contain sk- pattern), passing
EJCLAW_RUNTIME_TASK_ID via env var instead and making cancel_task
auto-resolve when task_id is omitted.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace LLM-per-tick polling with direct `gh api` calls for GitHub
Actions watchers. New `ci_provider` discriminator column routes tasks
to either the host-driven path (zero LLM tokens) or the existing
generic LLM path. GitHub watchers poll at 15s intervals (min 10s)
via `checkGitHubActionsRun()` in the scheduler, with completion
messages sent directly to chat on terminal state.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Watcher tasks get task-scoped IPC dirs (data/ipc/{group}/tasks/{taskId}/),
but the MCP server used HOST_IPC_DIR to write IPC files. Without
EJCLAW_HOST_IPC_DIR, it fell back to the task-scoped path, writing
cancel_task/send_message files to nested dirs the IPC watcher never scans.
This caused 11 zombie watchers stuck in infinite loops — self-cancel
calls silently failed with ~4000 stranded IPC files accumulating.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Merge remote main (token rotation, usage API, dashboard fixes)
with local memory pipeline changes. Smart quote encoding fixed.
Test alignment pending.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add automatic memory integration so EJClaw host directly calls Memento
MCP for recall at session start and reflect on compact, removing
reliance on agent voluntary tool use.
Stage 1: New sessions get room memory briefing injected into system
prompt (CLAUDE.md / AGENTS.md) via host-side MCP client.
Stage 2: PreCompact hook automatically calls reflect + remember to
persist session summaries as room-memory fragments.
Also restores missing source files (token-rotation, codex-token-rotation,
claude-usage exports) to fix full build from source.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Track lastIntermediateText to prevent duplicate Discord delivery
when same text arrives as both intermediate and final
- Filter task_progress descriptions >80 chars (AI summaries)
- Mark completed subagents with ✅ instead of removing
- Update claude-agent-sdk 0.2.76→0.2.81, codex 0.115→0.116
Track each subagent separately via agentId from SDK task events.
Single subagent shows detailed view with activity sub-lines,
multiple subagents show compact one-line-each format.
Add 'intermediate' phase for assistant text between tool calls.
These are sent as separate Discord messages without finalizing
or disrupting the active progress message.
Intermediate assistant text (e.g., "서브에이전트 4개 병렬로 띄웠어요")
is now emitted as regular messages (no phase), not progress heading
updates. Progress heading is only set by task_started (🔄) events.