5.9 KiB
CherryClaw Architecture
CherryClaw is an autonomous agent type in Cherry Studio, built on the Claude Agent SDK. Unlike standard claude-code agents, CherryClaw has an independent personality system, task-based scheduler, IM channel integration, and a set of self-management tools provided through an internal MCP server.
Architecture Overview
CherryClawService
├── PromptBuilder — Assembles full system prompt from workspace files
├── HeartbeatReader — Reads heartbeat file content (for pre-task prompt context)
├── ClawServer (MCP) — Built-in MCP server providing cron / notify / skills / memory tools
├── SchedulerService — 60s polling scheduler, queries DB for due tasks and executes
├── TaskService — Task CRUD + next run time calculation
└── ChannelManager — Channel adapter lifecycle management (Telegram, etc.)
Core Design Decisions
AgentServiceRegistry Pattern
SessionMessageService no longer hard-codes ClaudeCodeService. Instead, it uses AgentServiceRegistry to look up the corresponding service implementation by AgentType. CherryClaw delegates to claude-code for execution at runtime through the registry.
// src/main/services/agents/services/AgentServiceRegistry.ts
agentServiceRegistry.register('claude-code', new ClaudeCodeService())
agentServiceRegistry.register('cherry-claw', new CherryClawService())
Custom System Prompt (Replacing Claude Code Presets)
CherryClaw does not use Claude Code's preset system prompts. PromptBuilder assembles a complete custom prompt from workspace files, passed via the _systemPrompt field to ClaudeCodeService. When this field is present, it serves as the complete system prompt rather than the preset + append mode.
Disabling Inapplicable Built-in Tools
CherryClaw disables a set of SDK built-in tools unsuitable for autonomous operation via _disallowedTools:
| Disabled Tool | Reason |
|---|---|
CronCreate / CronDelete / CronList |
Replaced by internal MCP cron tools |
TodoWrite |
Not suitable for autonomous agents |
AskUserQuestion |
Autonomous agents should not ask users |
EnterPlanMode / ExitPlanMode |
Not suitable for autonomous agents |
EnterWorktree / NotebookEdit |
Not suitable for autonomous agents |
Invocation Flow
CherryClawService.invoke()
1. PromptBuilder.buildSystemPrompt(workspacePath)
→ Load system.md (optional override) + soul.md + user.md + memory/FACT.md
→ Assemble into complete system prompt
2. Create ClawServer instance (in-memory MCP server)
→ Inject as _internalMcpServers = { claw: { type: 'inmem', instance } }
3. Set _disallowedTools (disable inapplicable tools)
4. If agent has allowed_tools whitelist, append mcp__claw__* wildcard
5. Delegate to ClaudeCodeService.invoke()
→ Use _systemPrompt as complete replacement
→ Merge _internalMcpServers into SDK options.mcpServers
→ Claude SDK auto-discovers cron / notify / skills / memory tools
Memory System
CherryClaw uses an Anna-inspired three-file memory model, each file with an independent scope:
{workspace}/
system.md — Optional system prompt override (replaces default CherryClaw identity)
soul.md — Who you are: personality, tone, communication style
user.md — Who the user is: name, preferences, personal context
memory/
FACT.md — What you know: persistent project knowledge, technical decisions (6+ months)
JOURNAL.jsonl — Event log: one-off events, completed tasks, session notes (append-only)
Key rules:
- Each file has independent scope; no cross-file duplication
soul.mdanduser.mdare edited directly via Read/Edit toolsFACT.mdandJOURNAL.jsonlare managed viamemoryMCP tools- Agent updates autonomously without requesting user approval
- Filenames are case-insensitive
PromptBuilder Caching
PromptBuilder uses mtime-based caching for all file reads. Each read performs a single fs.stat check — if the file modification time hasn't changed, cached content is returned directly, without persistent file watchers.
Database
CherryClaw uses Drizzle ORM + better-sqlite3 (SQLite) for task data storage:
| Table | Purpose |
|---|---|
scheduled_tasks |
Scheduled tasks (name, prompt, schedule type, next run time, status) |
task_run_logs |
Task run logs (run time, duration, status, result/error) |
Both tables are associated with the agents table via foreign key cascades.
API Endpoints
| Method | Path | Description |
|---|---|---|
GET |
/v1/agents/:agentId/tasks |
List tasks |
POST |
/v1/agents/:agentId/tasks |
Create task |
GET |
/v1/agents/:agentId/tasks/:taskId |
Get task details |
PATCH |
/v1/agents/:agentId/tasks/:taskId |
Update task |
DELETE |
/v1/agents/:agentId/tasks/:taskId |
Delete task |
POST |
/v1/agents/:agentId/tasks/:taskId/run |
Manually trigger run |
GET |
/v1/agents/:agentId/tasks/:taskId/logs |
Get run logs |
Key Files
| File | Description |
|---|---|
src/main/services/agents/services/cherryclaw/index.ts |
CherryClawService entry point |
src/main/services/agents/services/cherryclaw/prompt.ts |
PromptBuilder system prompt assembly |
src/main/services/agents/services/cherryclaw/heartbeat.ts |
HeartbeatReader heartbeat file reading |
src/main/services/agents/services/AgentServiceRegistry.ts |
Agent service registry |
src/main/services/agents/services/TaskService.ts |
Task CRUD + scheduling calculation |
src/main/services/agents/services/SchedulerService.ts |
Polling scheduler |
src/main/ai/mcp/servers/claw.ts |
Claw MCP server |
src/main/services/agents/services/channels/ |
Channel abstraction layer |
src/main/services/agents/database/schema/tasks.schema.ts |
Task table schema |
