From d295eb7aaf74067902ab82c2ea232d7577aa115d Mon Sep 17 00:00:00 2001 From: Eyejoker Date: Fri, 13 Mar 2026 19:56:19 +0900 Subject: [PATCH] docs: update README to reflect current codebase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - container-runner.ts → agent-runner.ts - Add missing files: group-queue, group-folder, router, sender-allowlist, session-commands, channels/registry - Add image handling, skill sync, auto-continue, GroupQueue sections - Add 'both' agent type, data/attachments directory - Add npm test to development commands --- README.md | 78 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index bb26da2..7037ae3 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ This fork runs two independent NanoClaw instances as systemd services: - **nanoclaw** (Claude Code) — powered by Claude Agent SDK, trigger `@claude` - **nanoclaw-codex** (Codex) — powered by Codex app-server JSON-RPC, trigger `@codex` -Each service has its own store, data, and groups directories. Discord channels can be registered with either or both bots. +Each service has its own store, data, and groups directories. Discord channels can be registered with either agent, or both (`both` agent type for shared channels). ## Key Differences from Upstream @@ -29,42 +29,54 @@ Each service has its own store, data, and groups directories. Discord channels c | Session management | Container-based | Per-group `CLAUDE_CONFIG_DIR` / `CODEX_HOME` | | Token management | Manual | Auto-refresh with session sync | | Channel | Multi-channel (WhatsApp, Telegram, etc.) | Discord-focused | +| Image support | N/A | Bidirectional (receive as multimodal input, send as attachments) | +| Skill sync | Per-container | SSOT from `~/.claude/skills/` to all agent sessions | | Deployment | Single service | Dual systemd services | ## Architecture ``` -Discord ──► SQLite ──► Polling Loop ──┬──► Claude Agent SDK (host process) - └──► Codex App-Server (JSON-RPC stdio) - ├── thread/start, thread/resume - ├── turn/start (streaming) - ├── turn/steer (mid-turn injection) - └── Auto-approval (bypass sandbox) +Discord ──► SQLite ──► GroupQueue ──┬──► Claude Agent SDK (host process) + └──► Codex App-Server (JSON-RPC stdio) + ├── thread/start, thread/resume + ├── turn/start (streaming, multimodal) + ├── turn/steer (mid-turn injection) + ├── Auto-approval (bypass sandbox) + └── Auto-continue (text-only turn retry) ``` ### Directory Layout ``` nanoclaw/ -├── src/ # Core source +├── src/ │ ├── index.ts # Orchestrator: state, message loop, agent invocation -│ ├── container-runner.ts # Spawns agent processes, manages env/sessions +│ ├── agent-runner.ts # Spawns agent processes, manages env/sessions/skills +│ ├── group-queue.ts # Per-group concurrency, priority queue, idle preemption +│ ├── group-folder.ts # Group directory resolution and management +│ ├── router.ts # Outbound message formatting and routing +│ ├── sender-allowlist.ts # Security: sender-based access control +│ ├── session-commands.ts # Session commands (/compact) │ ├── token-refresh.ts # OAuth auto-refresh + session directory sync -│ ├── channels/discord.ts # Discord channel implementation -│ ├── db.ts # SQLite operations -│ ├── ipc.ts # IPC watcher and task processing │ ├── task-scheduler.ts # Scheduled tasks (cron/interval/once) -│ └── config.ts # Paths, intervals, trigger patterns +│ ├── ipc.ts # IPC watcher and task processing +│ ├── db.ts # SQLite operations +│ ├── config.ts # Paths, intervals, trigger patterns +│ └── channels/ +│ ├── registry.ts # Channel self-registration system +│ └── discord.ts # Discord: mentions, images, typing, file attachments ├── container/ -│ ├── agent-runner/ # Claude Code runner (Agent SDK) -│ ├── codex-runner/ # Codex runner (app-server JSON-RPC) +│ ├── agent-runner/ # Claude Code runner (Agent SDK, multimodal input) +│ ├── codex-runner/ # Codex runner (app-server JSON-RPC, auto-continue) │ └── skills/ # Shared agent skills (browser, etc.) ├── store/ # Claude Code service DB ├── store-codex/ # Codex service DB -├── data/sessions/ # Per-group Claude sessions (.claude/) +├── data/ +│ ├── sessions/ # Per-group Claude sessions (.claude/) +│ └── attachments/ # Downloaded Discord image attachments ├── data-codex/sessions/ # Per-group Codex sessions (.codex/) -├── groups/ # Per-group memory (Claude Code) -├── groups-codex/ # Per-group memory (Codex) +├── groups/ # Per-group memory and workspace (Claude Code) +├── groups-codex/ # Per-group memory and workspace (Codex) └── logs/ # Service logs ``` @@ -76,8 +88,25 @@ The Codex runner (`container/codex-runner/`) communicates with `codex app-server - **Streaming**: `item/agentMessage/delta` notifications for real-time text - **Mid-turn steering**: IPC messages injected via `turn/steer` during execution - **Auto-approval**: `approvalPolicy: "never"` + `sandbox: "danger-full-access"` +- **Auto-continue**: Detects text-only turns (no tool execution) and automatically retries up to 5 times to nudge the agent into actually executing tasks +- **Multimodal input**: Image attachments converted to `localImage` input blocks in `turn/start` - **Per-group config**: Model, effort, MCP servers configured per channel +### Image Handling + +Bidirectional image support through Discord: + +- **Receiving** (user → agent): Discord image attachments are downloaded to `data/attachments/`, then passed as base64 `ImageBlockParam` content blocks (Claude Code) or `localImage` input blocks (Codex) +- **Sending** (agent → user): Markdown image links `[name.png](/path)` in agent responses are automatically parsed and sent as Discord file attachments. Non-image file links are converted to readable filenames (`BuildPanel.tsx:320`) + +### Skill Sync + +Skills are managed from a single source of truth (`~/.claude/skills/` on the server) and automatically synced to all agent session directories at process start: + +- Claude Code sessions: `~/.claude/skills/` + project `container/skills/` +- Codex sessions: Same sources, synced to per-group `.codex/` directories +- Skills auto-register as slash commands (`/name`) in Claude Code and `$name` in Codex + ### OAuth Token Auto-Refresh `src/token-refresh.ts` handles Claude Code OAuth token lifecycle: @@ -87,6 +116,16 @@ The Codex runner (`container/codex-runner/`) communicates with `codex app-server - Syncs refreshed credentials to all per-group session directories - Solves the known headless environment token expiry issue +### GroupQueue + +`src/group-queue.ts` manages agent execution with: + +- **Per-group serialization**: Only one agent process per group at a time +- **Global concurrency limit**: Configurable max concurrent agents across all groups +- **Task priority**: Scheduled tasks drain before message processing +- **Idle preemption**: Idle agents are terminated when higher-priority tasks arrive +- **Exponential backoff**: Retries with backoff on processing failure + ## Setup ### Prerequisites @@ -132,7 +171,7 @@ Channels are registered in each service's SQLite database (`registered_groups` t - **jid**: Discord channel ID (`dc:`) - **folder**: Group folder name (matches Discord channel name) - **trigger_pattern**: Regex for bot activation (`@claude` or `@codex`) -- **agent_type**: `claude-code` or `codex` +- **agent_type**: `claude-code`, `codex`, or `both` (shared channel) - **work_dir**: Working directory for the agent - **container_config**: JSON config (e.g., `{"codexEffort":"high"}`) @@ -143,6 +182,7 @@ npm run build # Build main project cd container/agent-runner && npm run build # Build Claude runner cd container/codex-runner && npm run build # Build Codex runner npm run dev # Dev mode with hot reload +npm test # Run tests ``` ## License