Commit Graph

48 Commits

Author SHA1 Message Date
ejclaw
2b8f55deb6 runtime: centralize unsafe paired mode checks 2026-04-11 08:20:05 +09:00
ejclaw
497e6dec74 refactor: decompose runtime coordination and runner internals (PR6) 2026-04-10 22:48:49 +09:00
ejclaw
8b9bebd29e refactor: complete config unification and split db helpers 2026-04-08 19:05:19 +09:00
ejclaw
4650abbd10 Rename readonly reviewer runtime remnants 2026-04-06 17:27:16 +09:00
ejclaw
aaa9eeb50a Remove paired reviewer container execution path 2026-04-06 07:56:24 +09:00
ejclaw
d75d26c23d Stabilize paired reviewer recovery and owner follow-up 2026-04-06 07:49:26 +09:00
ejclaw
994e957767 Enable unsafe host paired runtime 2026-04-06 02:40:15 +09:00
Eyejoker
e9c400424d Add reviewer host evidence and verification shims 2026-04-04 20:51:24 +09:00
Eyejoker
59cf931b82 fix: copy arbiter AGENTS.md to reviewer session for container mount
Container's /home/node/.claude always mounts the reviewer session dir.
When arbiter runs codex in the same container, CODEX_HOME reads from
reviewer session's .codex/. Copy arbiter AGENTS.md there so codex gets
arbiter prompts instead of reviewer prompts.
2026-04-01 13:34:46 +09:00
Eyejoker
35ba7cb5ba chore: replace all node/npm/tsx references with bun across codebase
- Source: setup/service.ts, runners.ts, agent-runner.ts error messages
- Scripts: restart-stack.sh, run-migrations.ts → bun
- Bootstrap: setup.sh check_node→check_bun, npm install→bun install
- Docs: CLAUDE.md, README.md, all SKILL.md files
- Tests: service.test.ts, restart-stack.test.ts expectations updated
2026-03-31 00:10:23 +09:00
Eyejoker
0112f5a2d6 feat: migrate runtime from Node.js to Bun
- 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)
2026-03-30 23:58:54 +09:00
Eyejoker
4a5edaceb6 refactor: remove SERVICE_AGENT_TYPE legacy constant
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
2026-03-30 23:10:48 +09:00
Eyejoker
a043f2b79a feat: implement MAGI 3-agent arbiter system for deadlock resolution
Introduces a third agent role (arbiter) that is summoned on-demand
when owner and reviewer reach a deadlock (same verdict 3+ rounds).

Architecture:
- 3 Discord bots: owner (codex), reviewer (claude), arbiter (claude/codex)
- Arbiter is NOT always-on — only invoked when deadlock detected
- Arbiter renders binding verdict: PROCEED/REVISE/RESET/ESCALATE
- Non-escalate verdicts reset round_trip_count and resume ping-pong
- Backward compatible: ARBITER_AGENT_TYPE unset = existing 2-agent mode

Changes across 13 source files + 7 test files:
- types.ts: PairedRoomRole += 'arbiter', new statuses, ArbiterVerdict type
- config.ts: ARBITER_AGENT_TYPE, ARBITER_SERVICE_ID, ARBITER_DEADLOCK_THRESHOLD
- db.ts: schema migration (arbiter columns in channel_owner + paired_tasks)
- service-routing.ts: arbiter_service_id in lease
- room-role-context.ts: arbiter role detection
- paired-execution-context.ts: deadlock->arbiter, verdict handling
- message-runtime.ts: arbiter turn routing, cursor, sender labeling
- message-agent-executor.ts: arbiter mode, failover
- agent-runner.ts + environment.ts: arbiter container mode
- platform-prompts.ts: arbiter prompt loading

New files:
- prompts/arbiter-paired-room.md: arbiter system prompt
- src/arbiter-context.ts: builds conversation context for arbiter judgment
2026-03-30 22:20:42 +09:00
Eyejoker
d024b763f0 fix: prepare CLAUDE.md and roomRoleContext for container reviewer
Container reviewer was running without prompts because the container
path in agent-runner.ts skipped prepareGroupEnvironment() entirely.
This meant no CLAUDE.md (platform + paired room prompts + memory
briefing) and no roomRoleContext were passed to the container.

- Add prepareContainerSessionEnvironment() to write CLAUDE.md, sync
  skills, and ensure settings.json for the reviewer session directory
- Pass roomRoleContext through ReviewerContainerInput so the runner
  can prepend the [ROOM_ROLE] header
- Add roomRoleContext field to ReviewerContainerInput interface
2026-03-30 01:12:47 +09:00
Eyejoker
42079d5796 refactor: remove host-mode reviewer, container-only
Reviewer always runs in Docker container. No more snapshot file copy —
reviewer mounts the owner workspace directly as read-only. Removes
REVIEWER_CONTAINER_ENABLED toggle and host-mode fallback code.
2026-03-30 00:39:50 +09:00
Eyejoker
b33cf6c058 debug: forward runner query lifecycle logs to INFO for diagnosis 2026-03-29 21:29:06 +09:00
Eyejoker
16d20fe627 feat: paired review system overhaul — unified service, configurable agents, silent output removal
- 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
2026-03-29 20:50:32 +09:00
Eyejoker
fc9f2867b9 refactor: simplify paired review system and add reviewer container isolation
Phase 1 — Strip over-engineering (-3,665 lines):
- DB tables: 7 → 3 (remove executions, approvals, artifacts, events)
- Task statuses: 11 → 5 (active, review_ready, in_review, merge_ready, completed)
- Remove plan governance, event sourcing, gate/verdict, freshness guards
- paired-execution-context: 980 → 299 lines
- Session commands: remove /risk, /plan, /approve-plan, /request-plan-changes

Phase 2 — Container isolation for reviewers:
- Add container-runtime.ts (Docker abstraction)
- Add credential-proxy.ts (API key injection without container exposure)
- Add container-runner.ts (reviewer-specific read-only mount + tmpfs)
- Add container/Dockerfile + agent-runner (Chromium, Claude Code, Codex)
- agent-runner.ts: auto-route reviewer execution to container mode
2026-03-29 18:24:56 +09:00
Eyejoker
79a8a2639e feat: add structured room role metadata 2026-03-28 19:30:32 +09:00
Eyejoker
fe6314108c feat: add structured silent output contract 2026-03-28 05:56:58 +09:00
Eyejoker
ba9f6871b6 feat: add codex review failover and suppress output hardening 2026-03-28 05:40:38 +09:00
Eyejoker
d1c693fb17 fix: treat signal-killed agents as success when output was delivered
Two bugs fixed:

1. agent-runner: process close handler only checked `code !== 0`,
   but signal kills (SIGTERM/SIGKILL from post-close cleanup) set
   code=null which was misclassified as error even after successful
   output delivery. Now checks `code === null && signal` and resolves
   as success when hadStreamingOutput is true.

2. message-agent-executor: wrappedOnOutput persisted newSessionId
   before checking for poisoned session, allowing a stale session to
   be re-saved after clearSession. Reordered to check poison first
   and guard persist with !resetSessionRequested.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 21:25:30 +09:00
Eyejoker
5ea3439c5f refactor: extract shared utilities, protocol constants, and retry loop
Phase 3: provider-retry.ts — shared Claude rotation loop (SSOT)
  - retryClaudeWithRotation extracted from message-agent-executor + task-scheduler
  - ~200 lines of duplicated retry logic removed

Phase 4: types.ts — AgentOutputPhase + VisiblePhase unified
Phase 5: types.ts — AgentConfig extended with claudeThinking/claudeThinkingBudget

Utilities (utils.ts):
  - getErrorMessage: 14 occurrences of instanceof Error pattern → 1 function
  - readJsonFile/writeJsonFile: 19 occurrences of JSON+fs pattern → 2 functions
  - fetchWithTimeout: 3 occurrences of AbortController pattern → 1 function
  - formatElapsedKorean: deduplicated from task-watch-status + message-turn-controller

Protocol (agent-protocol.ts):
  - OUTPUT_START/END_MARKER centralized (runners keep local copies with SSOT reference)
  - IMAGE_TAG_RE, IPC constants documented

Runner: show "대화 요약 중..." progress message during auto-compact

Net: -137 lines, 354/354 tests passing
2026-03-25 04:59:49 +09:00
Eyejoker
0724914a43 merge: integrate remote token rotation + memory pipeline
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>
2026-03-24 03:53:15 +09:00
Eyejoker
3467e245d5 feat: auto memory pipeline — host-driven Memento recall/reflect
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>
2026-03-24 03:48:36 +09:00
Eyejoker
21a6be84b4 feat: multi-agent progress tracking with per-subagent display
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.
2026-03-23 19:46:08 +09:00
Eyejoker
8c98ad9faf fix: intermediate text as standalone messages, preserve progress
Add 'intermediate' phase for assistant text between tool calls.
These are sent as separate Discord messages without finalizing
or disrupting the active progress message.
2026-03-23 19:29:16 +09:00
Eyejoker
ac11cb7df1 feat: show tool activity sub-lines in progress messages
Display subagent tool descriptions as sub-lines under the main
progress text, updating in-place via Discord message edit.
Shows up to 5 recent tool activities (e.g., "Reading index.ts").
2026-03-23 18:31:20 +09:00
Eyejoker
0438d9113b fix: clean up progress tracking, fix subagent summary field handling
- Use system/task_progress subtype (not top-level type) for subagent events
- Extract intermediate assistant text between tool calls as progress
- Only show SDK-generated summaries for subagent progress (skip noisy per-tool updates)
- Truncate progress messages to Discord 2000 char limit
- Remove verbose debug logging from production
2026-03-23 17:59:49 +09:00
Eyejoker
d28b6151a8 chore: rebrand runtime and docs to EJClaw 2026-03-23 08:09:15 +09:00
Eyejoker
d3833eb20d style: format agent runner imports 2026-03-23 07:31:26 +09:00
Eyejoker
f42234bc65 refactor: split runner env and slim dashboard facade 2026-03-23 07:30:55 +09:00
Eyejoker
9def49f7fb fix: isolate watcher task runtimes 2026-03-23 07:16:07 +09:00
Eyejoker
867a6a80ef style: format extracted helpers 2026-03-23 07:00:22 +09:00
Eyejoker
a2db8f6c0e refactor: extract dashboard and task helpers 2026-03-23 06:59:59 +09:00
Eyejoker
83d7bf3d51 feat: merge per-group codex config overlay 2026-03-23 04:20:55 +09:00
Eyejoker
badb900a23 style: format files after pre-commit prettier run
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 00:11:44 +09:00
Eyejoker
b647502a10 feat: add seq cursor, work_items, provider fallback, and delivery reliability
Major reliability improvements to the message processing pipeline:

- Add messages.seq monotonic cursor replacing timestamp-based cursors,
  preventing message loss from timestamp collisions with LIMIT queries
- Add work_items table separating agent production from delivery,
  enabling delivery retry without re-running the agent
- Propagate Discord send failures instead of silently swallowing them
- Add Claude 429 → Kimi K2.5 automatic provider fallback with cooldown
- Fix follow-up turn state reset in live index.ts path (not just
  message-runtime.ts) to prevent final output from being lost
- Add restart context tracking for graceful restart announcements
- Lazy migration from timestamp cursors to seq cursors for existing data

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 00:11:32 +09:00
Eyejoker
dd15585941 fix: pass actual chat JID to agent runner instead of group folder
watch_ci tasks in perf channel were silently failing because
NANOCLAW_CHAT_JID was set to the group folder name (e.g. "eyejokerdb-4")
instead of the actual Discord JID. The scheduler rejected the task with
"target group not registered".

Also adds a fallback in ipc.ts to resolve folder names to registered
JIDs when a folder name is passed instead of a JID.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 14:27:00 +09:00
Eyejoker
9b741be902 fix: resolve session race condition and follow-up turn output loss
Two bugs fixed:

1. agent-runner error path race condition: when agent process exited
   with error, the promise resolved immediately without waiting for
   outputChain, allowing a late wrappedOnOutput to re-persist a stale
   session ID after clearSession had already run.

2. follow-up turn state not resetting: when a follow-up IPC message
   joined an active run, turn-level flags (finalOutputSentToUser,
   sawNonProgressOutput) from the first turn prevented the last
   progress from being promoted to a final message.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 12:29:00 +09:00
Eyejoker
767ab20d76 Restrict Codex task scheduling to CI watchers 2026-03-20 04:09:36 +09:00
Eyejoker
f281a2ee52 style: apply formatter to runtime files 2026-03-20 03:42:22 +09:00
Eyejoker
a293a701f4 fix: restore regressions from discord-only refactor 2026-03-20 03:41:53 +09:00
Eyejoker
bb0628e8f4 refactor: remove legacy container and non-discord remnants 2026-03-20 01:07:46 +09:00
Eyejoker
35356dd8a7 refactor: remove token-refresh, use env-based auth only
Delete token-refresh.ts and credentials.json sync. Auth is now
via CLAUDE_CODE_OAUTH_TOKEN in .env (1-year setup-token).
No more auto-refresh loop or credentials.json dependency.
2026-03-14 19:09:40 +09:00
Eyejoker
09a089ccaf refactor: unify skill sync, remove commands directory
- Remove commands/ sync from agent-runner (skills/ is SSOT for both agents)
- Remove ~/.codex/skills/ as source (use ~/.claude/skills/ only)
- Codex uses $skill prefix, Claude Code uses /skill — both read from skills/
- Minor formatting fixes in config.ts, group-queue.test.ts, index.ts
2026-03-13 17:03:28 +09:00
Eyejoker
922ec60517 fix: sync Claude Code skills/commands to Codex sessions
Codex sessions now also receive skills and commands from ~/.claude/
in addition to ~/.codex/, so both agents share the same tool set
(patch, linear, sentry, coolify, exa, playwright, sprite, etc.)
2026-03-13 16:45:36 +09:00
Eyejoker
bd7f4408ae refactor: rename container references to agent across codebase
- container-runner.ts → agent-runner.ts
- ContainerInput/Output → AgentInput/Output
- ContainerConfig → AgentConfig
- runContainerAgent → runAgentProcess
- CONTAINER_TIMEOUT → AGENT_TIMEOUT (with env fallback)
- MAX_CONCURRENT_CONTAINERS → MAX_CONCURRENT_AGENTS
- containerName → processName, isTaskContainer → isTaskProcess
- DB column container_config kept as-is (backwards compat)
2026-03-13 16:18:36 +09:00