Commit Graph

93 Commits

Author SHA1 Message Date
ejclaw
994e957767 Enable unsafe host paired runtime 2026-04-06 02:40:15 +09:00
ejclaw
446b194e2c Automate worktree dependency installs 2026-04-05 22:34:16 +09:00
Eyejoker
04802166a6 Fix reviewer canonical repo mounts 2026-04-04 22:01:08 +09:00
Eyejoker
e9c400424d Add reviewer host evidence and verification shims 2026-04-04 20:51:24 +09:00
Eyejoker
74ff662026 feat: mount owner session directory read-only in reviewer container
Allows reviewer to verify runtime state files (cron state, configs)
that the owner references by absolute host path. Mounted at the same
host path so absolute path references work inside the container.
2026-04-01 13:47:34 +09:00
Eyejoker
fad3ce3bdf fix: inject role prompts into AGENTS.md for codex container reviewer
prepareContainerSessionEnvironment now writes AGENTS.md (with platform
+ paired-room prompts) to a session-local .codex/ dir alongside auth
credentials from host. CODEX_HOME points to this dir so codex-runner
gets role-appropriate prompts instead of empty instructions.
2026-04-01 06:00:26 +09:00
Eyejoker
bfd781c552 fix: skip role model override on failover handoffs
When forcedAgentType is set (failover), the role's configured model
(e.g. claude-opus-4-6 for reviewer) was injected as CODEX_MODEL,
causing codex SDK to reject it. Now model overrides are only injected
for the primary agent type, not failover runs.
2026-04-01 05:31:02 +09:00
Eyejoker
9b71bc5878 debug: log container runner selection 2026-04-01 05:25:47 +09:00
Eyejoker
df6298f340 fix: exclude model env from container creation, inject per-exec only
Model/effort keys (CLAUDE_MODEL, CODEX_MODEL, etc.) are now excluded
from docker run creation args and only injected at docker exec time
matching the active agent type. Removes the empty-value override hack.
2026-04-01 05:22:20 +09:00
Eyejoker
a648a08bf0 fix: clear CLAUDE_MODEL env on codex container exec
Container-level env has CLAUDE_MODEL baked in from creation.
Explicitly clear it on docker exec when running codex agent
to prevent 'model not supported with ChatGPT account' errors.
2026-04-01 05:20:55 +09:00
Eyejoker
7c9050ba9f fix: only inject matching model env vars for container agent type
When codex runs in container, skip CLAUDE_MODEL/EFFORT to prevent
'claude-opus-4-6 not supported with ChatGPT account' errors.
2026-04-01 05:18:10 +09:00
Eyejoker
6e81bdc215 fix: mount codex OAuth credentials into reviewer container
Mount host's ~/.codex (read-only) into container so codex-runner
can authenticate via ChatGPT OAuth. Set CODEX_HOME env on exec.
2026-04-01 05:01:44 +09:00
Eyejoker
404cbd6b04 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
2026-04-01 04:50:05 +09:00
Eyejoker
47d532e869 fix: sync refreshed OAuth tokens to process.env for container exec
Token refresh updated internal array and .env file but not process.env,
so docker exec -e kept injecting the stale startup token. Now syncs
to process.env after refresh. Container kill on refresh is no longer
needed since docker exec picks up the latest token each turn.
2026-03-31 13:34: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
3e2954ebcf refactor: extract resolveEffectiveAgentType/resolveSessionFolder as SSOT, deduplicate CONTAINER_IMAGE 2026-03-30 23:44:49 +09:00
Eyejoker
e649432a40 feat: add per-role model selection via .env (OWNER/REVIEWER/ARBITER_MODEL, _EFFORT, _FALLBACK_ENABLED) 2026-03-30 23:40:36 +09:00
Eyejoker
58ed721d6e feat: auto-recreate reviewer containers on token rotation
When OAuth tokens rotate or refresh, all reviewer containers are
automatically removed. The next reviewer turn recreates them with
the latest token, preventing 401 auth failures.

Uses callback pattern to avoid circular dependencies:
- token-rotation.ts: onTokenRotated(cb)
- token-refresh.ts: onTokenRefreshed(cb)
- index.ts: registers recreateAllReviewerContainers as callback
2026-03-30 21:51:45 +09:00
Eyejoker
ad34062dbc feat: mount attachments directory in reviewer container
Discord file attachments are downloaded to data/attachments/ on the
host. Mount this directory read-only in the reviewer container at the
same absolute path so both owner and reviewer can access uploaded
files (WAV, images, etc.) using the same path.
2026-03-30 18:40:36 +09:00
Eyejoker
008154b57d fix: inject real OAuth token to reviewer container instead of proxy
The credential proxy cached the OAuth token at startup, but Anthropic
no longer supports raw OAuth Bearer tokens on the API. The Claude Code
SDK handles OAuth internally, so the proxy was breaking auth.

Now injects the real token both at container creation and at each
docker exec, so token rotation is picked up without recreating the
container.
2026-03-30 05:14:03 +09:00
Eyejoker
3281736390 feat: inject Sentry read-only token into reviewer container 2026-03-30 04:21:18 +09:00
Eyejoker
3015c3ea9a feat: mount parent .git for worktree resolution in reviewer container
Git worktrees reference the parent repo's .git directory via absolute
path. Without mounting it, all git commands fail inside the container.
Now reads the .git file, resolves the parent .git path, and mounts it
read-only at the same host path so references resolve naturally.
2026-03-30 03:28:09 +09:00
Eyejoker
484c0d7316 fix: mount reviewer session dir matching CLAUDE_CONFIG_DIR path
Container mounted data/sessions/{folder}/ but CLAUDE.md was written
to data/sessions/{folder}-reviewer/.claude/ by paired-execution-context.
The reviewer never saw its prompts (verdict protocol, paired room rules).

Now mounts {folder}-reviewer/ to match, so the CLAUDE.md with platform
and paired room prompts is visible inside the container.
2026-03-30 01:50:29 +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
a215b2471a chore: formatter cleanup and test updates from bot cherry-pick 2026-03-30 00:57:03 +09:00
Eyejoker
6cb0b38ba3 refactor: persistent reviewer containers with docker exec per turn
Instead of docker run --rm per turn (~300ms overhead), containers are
now created once per channel with docker run -d and reused across turns.
Each turn runs via docker exec inside the warm container.

- ensurePersistentContainer: creates or reuses container per channel
- stopReviewerContainer: explicit stop for /clear or shutdown
- Container stays alive with sleep infinity, turns exec node runner
- cleanupOrphans handles stale containers on restart
2026-03-30 00:45:42 +09:00
Eyejoker
60c3dd5fb9 fix: await onOutput chain in container runner to prevent duplicate delivery
Container runner was calling onOutput with fire-and-forget (void),
so work items were still in 'produced' status when the drain loop
checked after container exit. This caused the same message to be
delivered through both reviewer and owner channels.

Now chains onOutput calls and waits for completion before resolving,
matching the host-process runner's outputChain pattern.
2026-03-30 00:05:42 +09:00
Eyejoker
6036fd988b fix: remap host paths to container mount points for EJCLAW_WORK_DIR and CLAUDE_CONFIG_DIR
Host paths passed via envOverrides don't exist inside the container,
causing ENOENT when the SDK tries to spawn with a non-existent CWD.
Now remaps EJCLAW_WORK_DIR to /workspace/project and CLAUDE_CONFIG_DIR
to /home/node/.claude (matching the container mount points).
2026-03-29 23:52:32 +09:00
Eyejoker
c28fba0478 feat: re-review on post-approval changes, container improvements
- Compare git HEAD at reviewer approval vs owner finalize to detect
  new code changes after DONE verdict. Re-trigger reviewer if changed,
  complete if unchanged (commit-only is fine).
- Record approved HEAD in source_ref when reviewer says DONE
- Add pnpm store mount, pre-flight checks, idle timeout for containers
- Clean up orphaned containers on shutdown
2026-03-29 23:00:56 +09:00
Eyejoker
b965a7e43d feat: add pnpm store mount, pre-flight checks, and idle timeout for reviewer containers
- Detect pnpm projects and mount global store read-only so hardlinks resolve
- npm/yarn/bun need no extra mounts (node_modules is self-contained)
- Pre-flight check: verify Docker running + image exists (cached after first call)
- Add activity-based idle timeout (reset on stdout/stderr like host runner)
- Clean up orphaned containers on shutdown (not just startup)
2026-03-29 22:46:20 +09:00
Eyejoker
fcb854e9de fix: move tmpfs to /tmp instead of inside read-only mount 2026-03-29 19:16:23 +09:00
Eyejoker
d01f98bd61 feat: unify 3 bot services into single process
- Add UNIFIED_MODE flag (default on, disable with UNIFIED_MODE=0)
- Register all 3 Discord bots in one process (claude, codex, review)
- Load all registered groups regardless of agent_type (codex/owner priority)
- Start credential proxy and container cleanup at unified startup
- shouldServiceProcessChat returns true in unified mode
- Add findChannelByName for role-based response routing
- Backward compatible: UNIFIED_MODE=0 restores per-service behavior
2026-03-29 18:37:17 +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
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
Eyejoker
35277cea0a feat: codex app-server integration, token sync fix, typing fix
- Rewrite codex-runner to use `codex app-server` JSON-RPC instead of
  `codex exec`. Supports streaming (item/agentMessage/delta), session
  persistence (thread/start, thread/resume), and mid-turn message
  injection (turn/steer with IPC polling during execution).
- Fix token refresh not syncing to per-group session directories,
  causing 401 errors on running agents.
- Fix typing indicator staying on when codex returns null result
  by always clearing typing on any streaming callback.
- Update README and CLAUDE.md to reflect dual-service architecture,
  host process mode, and codex app-server integration.
2026-03-13 16:03:08 +09:00
Eyejoker
0515edc93d feat: OAuth auto-refresh, Codex session resume, MCP injection, and secret sanitization
- Add token-refresh module that auto-renews Claude OAuth tokens 30min before expiry
- Fix Codex session loss on service restart by resuming previous sessions
- Inject nanoclaw MCP server into Codex config.toml for send_message/schedule_task
- Sanitize ANTHROPIC_API_KEY and CLAUDE_CODE_OAUTH_TOKEN from Codex subprocess env
2026-03-12 02:47:24 +09:00
Eyejoker
3fd9ce18b5 feat: sync skills to per-group Codex session dirs 2026-03-11 20:06:38 +09:00
Eyejoker
03c1a67649 fix: sync instructions.md to per-group Codex session dirs 2026-03-11 19:01:09 +09:00
Eyejoker
aed051fb96 fix: sync ~/.claude/.credentials.json to per-group session dirs
Without this, per-group credentials become stale when the home
credentials are refreshed elsewhere, causing 401 auth errors.
2026-03-11 16:49:09 +09:00
Eyejoker
b0ef22e702 fix: use CODEX_HOME instead of CODEX_CONFIG_DIR for per-group session isolation
CODEX_CONFIG_DIR is not recognized by Codex CLI. CODEX_HOME is the correct
env var that controls the root directory for sessions, state, and config.
Without this, all groups sharing the same workDir would have their sessions
collide, causing resume --last to pick up wrong group's session.
2026-03-11 05:07:34 +09:00
Eyejoker
7c49bdf6ce feat: per-group model/effort overrides via containerConfig 2026-03-11 04:31:41 +09:00
Eyejoker
8de978ccf6 feat: sync project workDir commands and skills into agent sessions 2026-03-11 02:59:34 +09:00
Eyejoker
aec2cd27af feat: sync global ~/.claude/commands/ into agent sessions 2026-03-11 02:34:20 +09:00
Eyejoker
de0b166b5d feat: sync user's global ~/.claude/skills/ into agent sessions 2026-03-11 02:32:06 +09:00
Eyejoker
021014cc56 feat: add CODEX_MODEL and CODEX_EFFORT env var support for codex-runner 2026-03-11 02:25:20 +09:00
Eyejoker
cb51ef36f9 feat: add per-group workDir for agent working directory override
Agents can now run in a specified project directory instead of the group
folder. The group's CLAUDE.md is still loaded via additionalDirectories.
2026-03-11 02:02:51 +09:00
Eyejoker
57cafc19f8 refactor: dual-instance setup, remove dead container code
- Make STORE_DIR, DATA_DIR, GROUPS_DIR configurable via env vars
  (NANOCLAW_STORE_DIR, NANOCLAW_DATA_DIR, NANOCLAW_GROUPS_DIR)
  for running two instances from one codebase
- Remove broken 'both' agent type sequential loop from processGroupMessages
- Delete dead code: container-runtime, mount-security, credential-proxy,
  Dockerfiles, and related config constants (~1,085 lines removed)
- Add codex instance launchd plist template
2026-03-11 00:50:03 +09:00
Eyejoker
bbe8354bfd fix: add npm-global to PATH for codex CLI, clear typing after response
- Enrich spawned process PATH with ~/.npm-global/bin so codex CLI is
  findable when running under launchd
- Clear typing indicator in streaming callback after sending response,
  instead of waiting for runner process to fully exit
2026-03-10 21:46:36 +09:00
Eyejoker
46f560ed67 fix: load agentType from DB, fix host process PATH and session resume
- Read/write agent_type column in getAllRegisteredGroups/setRegisteredGroup
  (was missing, causing all groups to fall back to claude-code)
- Strip CLAUDECODE env var to prevent nested session detection
- Add node binary path to spawned process PATH for launchd compatibility
- Clear stale container-era sessions that reference /workspace/group
2026-03-10 21:25:28 +09:00
Eyejoker
08dd468692 feat: remove container layer, run agents as direct host processes
- Rewrite container-runner.ts to spawn node processes directly instead
  of Docker/Apple Container
- Runner paths configurable via env vars (NANOCLAW_GROUP_DIR, etc.)
  with container-path defaults for backwards compat
- Pass real credentials directly (no credential proxy needed)
- Remove ensureContainerSystemRunning() and startCredentialProxy()
  from startup
- Add build:runners script for building agent-runner and codex-runner
- Fix TS strict mode errors in agent-runner ipc-mcp-stdio.ts
2026-03-10 21:05:21 +09:00