Pinned Note
The architectural ideas behind this project were learned from and inspired by the open-source project HKUDS/nanobot.
SolonClawis not a direct port of nanobot. It is a localized implementation built aroundSolon + Solon AI + file-based workspace + multi-channel runtime. Always treat the current repository code and tests as the source of truth.
SolonClaw is a lightweight Agent service built on Solon 3.9.5. It unifies model execution, conversation history, child-task orchestration, workspace tools, scheduled jobs, a local debug UI, and DingTalk integration under one runtime.
- 🤖 Unified Agent runtime
- 💬 Shared runtime for Debug Web and DingTalk
- 🧠 Workspace-driven prompt assembly and memory files
- 🛠️ Built-in tools for file IO, notifications, job management, and CLI terminal skills
- 🧩 Child task spawning with continuation back to the parent conversation
- ⏰ Persistent scheduled jobs restored on startup
- 📁 File-based runtime storage for runs, conversations, dedup, routes, and media
Inbound Message
-> ChannelAdapter / Debug Web / System Job
-> AgentRuntimeService
-> RuntimeStoreService
-> ConversationScheduler (per sessionKey)
-> SolonAiConversationAgent
-> ChatModel
-> Workspace Tools
-> Runtime Tools
-> Job Tools
-> CLI Skills (@skills)
-> OutboundEnvelope
-> ChannelRegistry
-> DingTalk / Debug Web
Main modules:
agent/runtimeagent/storeagent/workspaceagent/toolagent/jobchannel/dingtalkweb
Built-in tools currently include:
read_filewrite_fileedit_filenotify_userspawn_tasklist_child_runsget_run_statusget_child_summarylist_jobsget_jobadd_jobremove_jobstart_jobstop_job
Behavioral notes:
- File access is restricted to the configured workspace.
- Command execution now goes through CLI
TerminalSkillviabash. TerminalSkillsandbox mode is enabled by default and can be controlled withsolonclaw.agent.tools.sandboxMode.- Child runs use independent session keys and can be aggregated by
batchKey. - Scheduled jobs are bound to the latest external reply route.
- Heartbeat checks read
HEARTBEAT.mdand trigger a silent internal run.
sandboxMode rules:
true: CLI abilities such asbash,ls,read,grep, andglobonly allow workspace-relative paths,~/, and logical pool paths like@skillstrue: absolute paths are blocked, and relative traversal outside the workspace is blockedtrue: logical pool paths such as@skillsremain readable/executable but are still read-onlyfalse: absolute-path access is allowed and the CLI becomes more open
Default workspace root:
./workspace
Typical structure:
workspace/
AGENTS.md
SOUL.md
IDENTITY.md
USER.md
TOOLS.md
HEARTBEAT.md
MEMORY.md
memory/
skills/
jobs.json
runtime/
runs/
conversations/
dedup/
meta/
media/
Current DingTalk integration:
- Inbound:
DingTalkStreamTopics.BOT_MESSAGE_TOPIC - Outbound: official bot OpenAPI
- Group send:
orgGroupSend - Private send:
batchSendOTO - Reply format: markdown text
Current behavior:
- Group and private chats use isolated session keys.
- Replies always rely on
ReplyTarget. - Attachments are currently degraded into text-only fallback.
- Empty allowlists mean allow by default; once configured, only matched entries are accepted.
Requirements:
- JDK
8 - Maven
3.9+ - Ollama is recommended for local development
Compile and test:
mvn -q -DskipTests compile
mvn -q testRun:
java -jar target/solonclaw.jarDevelopment mode:
java -jar target/solonclaw.jar --env=devDefault port:
12345
Open the local debug page:
If you want ready-to-run commands that first set variables and then launch the jar, use one of the examples below.
PowerShell:
$env:APP_JAR="target/solonclaw.jar"
$env:APP_ENV="dev"
$env:APP_PORT="12345"
$env:APP_WORKSPACE="./workspace"
$env:APP_XMS="256m"
$env:APP_XMX="512m"
java `
"-Xms$env:APP_XMS" `
"-Xmx$env:APP_XMX" `
"-Dserver.port=$env:APP_PORT" `
"-Dsolonclaw.workspace=$env:APP_WORKSPACE" `
-jar $env:APP_JAR `
--env=$env:APP_ENVBash:
export APP_JAR="target/solonclaw.jar"
export APP_ENV="dev"
export APP_PORT="12345"
export APP_WORKSPACE="./workspace"
export JAVA_OPTS="-Xms256m -Xmx512m"
java ${JAVA_OPTS} \
-Dserver.port="${APP_PORT}" \
-Dsolonclaw.workspace="${APP_WORKSPACE}" \
-jar "${APP_JAR}" \
--env="${APP_ENV}"Production example:
export APP_JAR="target/solonclaw.jar"
export APP_ENV="prod"
export APP_PORT="12345"
export APP_WORKSPACE="./workspace"
export JAVA_OPTS="-Xms512m -Xmx1024m"
java ${JAVA_OPTS} \
-Dserver.port="${APP_PORT}" \
-Dsolonclaw.workspace="${APP_WORKSPACE}" \
-jar "${APP_JAR}" \
--env="${APP_ENV}"Main config:
src/main/resources/app.yml
Dev config example:
src/main/resources/app-dev.yml
External config example:
scripts/config.example.yml
Current important settings:
solonclaw.workspace=./workspacesolonclaw.agent.scheduler.maxConcurrentPerConversation=4solonclaw.agent.scheduler.ackWhenBusy=falsesolonclaw.agent.tools.sandboxMode=truesolonclaw.agent.heartbeat.enabled=truesolonclaw.agent.heartbeat.intervalSeconds=1800solonclaw.channels.dingtalk.*
The repository now includes:
Dockerfiledocker-compose.yml.dockerignore
docker build -t solonclaw:latest .Create these in the project root:
config.ymlworkspace/
Use config.yml for production secrets, model config, and DingTalk config. Use workspace/ for runtime data, memory files, skills, and persisted jobs.
You can start from:
scripts/config.example.yml
docker run -d \
--name solonclaw \
-p 12345:12345 \
-e JAVA_OPTS="-Xms256m -Xmx512m" \
-e APP_ARGS="--env=prod" \
-v "$(pwd)/workspace:/app/workspace" \
-v "$(pwd)/config.yml:/app/config.yml:ro" \
solonclaw:latestPowerShell:
docker run -d `
--name solonclaw `
-p 12345:12345 `
-e JAVA_OPTS="-Xms256m -Xmx512m" `
-e APP_ARGS="--env=prod" `
-v "${PWD}/workspace:/app/workspace" `
-v "${PWD}/config.yml:/app/config.yml:ro" `
solonclaw:latestNotes:
- The container working directory is
/app. APP_ARGS="--env=prod"activates theprodprofile and loads/app/config.yml.- Mounting
workspace/keeps runtime data outside the container lifecycle.
docker compose up -d --buildStop:
docker compose downLogs:
docker compose logs -f solonclawThe provided compose file already:
- exposes port
12345 - mounts
./workspaceto/app/workspace - mounts
./config.ymlto/app/config.yml - starts the app with
--env=prod
You can modify these environment variables in docker-compose.yml:
JAVA_OPTSAPP_ARGS
Example:
environment:
JAVA_OPTS: "-Xms512m -Xmx1024m"
APP_ARGS: "--env=prod"- Always mount a persistent
workspacedirectory in production. - Do not bake real secrets into the image.
- If you use Ollama outside the container, make sure the container can reach that endpoint.
- If you use DingTalk, provide
clientId,clientSecret, androbotCodeinconfig.yml.
The current test suite covers:
- Solon startup and
ChatModelwiring - Workspace template bootstrap and prompt assembly
- Workspace tool path boundaries
- Runtime file persistence
- Per-conversation concurrency and busy acknowledgements
- Child run spawning, continuation, aggregation, and batch filtering
- Proactive notifications
- Silent heartbeat execution
- DingTalk inbound mapping and markdown payload generation
- Persistent job storage
- Add new channels through
ChannelAdapterandChannelRegistry - Never guess reply routes outside
ReplyTarget - Maintain conversation history only through
RuntimeStoreService - Prefer Solon lifecycle hooks for long-running resources
- Add new project config under
SolonClawProperties - Reuse the existing Debug Web entrypoint for local debugging
- Prefer Lombok-managed accessors for entity classes, DTOs, event payloads, result objects, and config carrier objects; use
@Datawhere it is clearly appropriate - Prefer Lombok-managed no-arg constructors for these data carriers, typically via
@NoArgsConstructor - Classes that need persistence, serialized transport, caching, or stable data-carrier semantics should implement
Serializablewhen appropriate - Avoid adding inner classes when possible; config carrier objects should preferably be extracted into standalone classes
Every Pull Request is recommended to include:
BackgroundChangesImpactValidationRisk and Rollback
Recommended template:
## Background
- Why this change is needed
## Changes
- What was changed in this PR
## Impact
- Which modules, APIs, configs, or deployment paths are affected
## Validation
- What tests were run
- What manual verification was performed
## Risk and Rollback
- What could go wrong
- How to roll back if neededAdditional expectations:
- Keep one PR focused on one kind of change whenever possible.
- PR titles and commit messages are encouraged to use bilingual Chinese/English wording in this repository.
- If behavior or configuration changes, update the related docs in the same PR.
Commit messages should follow the Conventional Commits style:
<type>(<scope>): <subject>
Notes:
- There must be one space after the colon
: scopeis optional and describes the affected area, such as a module name, directory name, or a responsibility likeruntime,workspace, orwebsubjectis required and should briefly describe the change; bilingual Chinese/English wording is recommended in this repository
type is required and may be one of:
feat: new featurefix: bug fixdocs: documentation changesstyle: formatting-only changes that do not affect runtime behaviorrefactor: refactoring or optimization that is neither a feature nor a bug fixperf: performance optimizationtest: test additions or adjustmentschore: build process or auxiliary tooling changesrevert: revertbuild: packaging/build changes
Recommended examples:
feat(agent): 增加了子任务聚合能力 (Add child-run aggregation)
docs(readme): 补充了提交信息格式说明 (Add commit message format notes)
fix(runtime): 修复了 continuation 丢失问题 (Fix continuation dispatch issue)
Additional conventions:
- Split commits by responsibility whenever possible, preferably into small units such as prompt/context, runtime governance, config defaults/comments, and tests
- Try to keep each commit focused on one kind of change instead of mixing unrelated work
AI-assisted code and documentation authoring is allowed in this project.
But the following rules apply:
- AI can assist implementation, but it does not replace developer responsibility.
- All AI-generated or AI-assisted code must be reviewed by a human developer.
- All changes pending merge must be manually tested and verified by a developer.
- Using AI is never a reason to skip review, testing, or regression checks on critical flows.
Recommended human verification should match the risk level of the change, such as:
- local compilation
- unit or integration tests
- manual verification of critical behavior
- configuration and deployment checks
In short:
- AI-written code is allowed
- merging without developer manual testing and verification is not allowed
For the repository-specific collaboration guide, read: