When the Codex SDK returns 'interrupted' (API-side timeout/disconnect),
retry the turn once before reporting failure. Prevents unnecessary
"요청을 완료하지 못했습니다" messages for transient issues.
Remove 9 inherited skills (Qodo, update-ejclaw, add-* addons,
update-skills) and restart-ejclaw-stack.sh that came from the
nanoclaw fork and are no longer used.
- resolveSessionFolder now always returns unique folder for arbiter
(prevents sharing session with owner when both use same agent type)
- Arbiter sessionId forced to undefined to prevent resume attempts
- Owner finalize with concerns now checks deadlock threshold before
looping back — prevents merge_ready ↔ active infinite oscillation
- Arbiter verdict resets round_trip to threshold-1 instead of 0,
giving agents one round before re-triggering arbiter
- New paired_turn_outputs table stores agent output directly in DB
- Owner/reviewer prompts assembled from DB instead of Discord messages
- Arbiter context includes human messages + turn outputs
- Falls back to Discord messages for pre-migration tasks
- 50k char storage limit with truncation warning
- /stop now resets the active paired task to completed so the next
user message routes to the owner instead of the stuck reviewer
- Owner completion with sawOutput=false (e.g. interrupted by /stop)
no longer auto-triggers the reviewer — treated as interrupted
- Clear pendingProgressText when intermediate updates arrive on an
existing progress message, preventing flushPendingProgress from
overwriting latestProgressText with stale buffered content
- Dashboard memory: use /proc/meminfo MemAvailable instead of os.freemem()
to exclude Linux buffer/cache from reported usage
- Add build:container npm script for reviewer Docker image rebuild
- Update deploy commands in CLAUDE.md and README.md to include build:container
Kimi coding plan and GLM both use Anthropic-compatible API, not OpenAI.
queryModel now dispatches to /v1/messages or /chat/completions based on
apiFormat config per reference model.
Recovered kimi-usage.ts from bot session logs — original was deleted
during March 27 fallback cleanup. Shows 5h/7d usage bars in dashboard
alongside Claude and Codex, using sk-kimi-* coding plan API key.
- Failover is now global (account-level, not per-channel)
- Dashboard shows role model config (Owner/Reviewer/Arbiter + MoA refs)
- Dashboard shows failover status when active
- Auto-clear failover on successful Claude rotation
- Remove per-channel lease writes from failover path
Instead of spawning separate processes or using OpenRouter, MoA now:
- Queries external API models (Kimi, GLM) in parallel for opinions
- Injects opinions into the SDK arbiter's prompt
- The existing subscription-based arbiter aggregates all perspectives
No extra SDK processes, no OpenRouter dependency. Per-model config via
MOA_REF_MODELS + MOA_{NAME}_MODEL/BASE_URL/API_KEY env vars.
When MOA_ENABLED=true, arbiter queries multiple LLM models in parallel
via OpenAI-compatible API, then an aggregator model synthesizes the
final verdict from all opinions. Falls back to single-agent arbiter
when MoA is disabled.
Config: MOA_BASE_URL, MOA_API_KEY, MOA_REFERENCE_MODELS, MOA_AGGREGATOR_MODEL
- 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)
SERVICE_AGENT_TYPE was always 'claude-code' in the unified service,
making it a misleading constant. Each group already has its own
agentType field.
- Removed SERVICE_AGENT_TYPE from config.ts
- Session functions now accept agentType parameter (default: 'claude-code')
- Task scheduler uses task-level agent_type for token rotation decisions
- All fallback defaults changed to 'claude-code' literal
- Logging uses 'unified' instead of the misleading type
Eliminated duplicate task status checks in message-agent-executor.ts
and message-runtime.ts. All status→role decisions now flow through
the single resolveActiveRole() function.
resolveCursorKey and resolveChannel both duplicated the same
taskStatus→role switch. Now resolveActiveRole() is the single
mapping, and both functions derive from it.
Replace all manual channel selection (useReviewerChannel,
useArbiterChannel, isReviewerWorkItem, isArbiterWorkItem, 3-way
ternary chains) with a single resolveChannel(taskStatus) function
that maps task status to the correct Discord bot channel.
Replace all manual cursor key construction (hardcoded chatJid:reviewer,
chatJid:arbiter, pairedCursorKey, 3-way ternary) with a single
resolveCursorKey(chatJid, taskStatus) function.
- Removed pairedCursorKey import (no longer needed)
- Removed loopIsReviewerTurn, loopIsArbiterTurn intermediate variables
- All cursor key logic now flows through one function that maps
task status to the correct role-specific key