Component Development · Part 1 of 3 — How it works
Component System
A component is a Python class extending BaseCell. Drop it into components/ and within 3 seconds it is scanned, loaded and wrapped as a tool the LLM can call directly — no registration, no config edits, no restart.
From file to tool
Each layer has its own job. A failure at any step is recorded as a load error and visible via component.info().
components/foo.py
│ discover_components() scan the dir, extract BaseCell subclasses via AST
│ _instantiate_component() dynamic import + instantiate + on_load()
│ register_cell() write into the in-memory registry _cell_registry
│ register_to_config() append to settings.yaml enabled_components
│ CellToolAdapter wrap as BaseTool (with JSON Schema)
│ ComponentToolRegistry thread-safe registry, version++
↓
AgentLoop calls get_all_tools() every turn → the LLM sees foo
↓
execute_with_context() → _cmd_xxx() → Dict returnedThree registries
The same component shows up in three places, each with a different purpose.
| Layer | Location | Role |
|---|---|---|
| In-memory registry | _cell_registry | Live component instances; hot reload mutates this directly |
| Config manifest | settings.yaml | Persisted enabled list, maintained by the system — manual edits get overwritten |
| Tool registry | ComponentToolRegistry | Thread-safe tool view with a version counter; AgentLoop re-reads it every turn |
Core features
| Feature | Description |
|---|---|
| Auto discovery | Scans the directory and extracts BaseCell subclasses via AST — no decorators, no registration code |
| Hot-plug | ComponentWatcher compares file mtimes every 3s; add / modify / delete triggers an incremental reload |
| Tool wrapping | CellToolAdapter turns the component into a BaseTool with a generated function-calling definition |
| Sandbox isolation | Runs in a subprocess by default so a crash or infinite loop cannot take down the main process; 120s timeout |
| No-sandbox whitelist | Trusted components can run in-process; changes trigger an immediate hot reload |
| Reserved names | shell / memory / file are system tools and cannot be shadowed by a component |
How the tool definition is built
Nothing is hand-written — it is all derived from your code. This is the one place where component style matters.
- The signature defines parameter names, types and required fields (no default = required)
- The docstring Args: section supplies descriptions; missing entries fall back to the parameter name
- Annotations int/float/bool/list/dict map to integer/number/boolean/array/object
- A class docstring over 400 chars is truncated and auto-appended with the command list and a help hint
# Single command: params are flat, no command field
def _cmd_ping(self) -> Dict: ...
# Multiple commands: command becomes an enum and all other params merge into one level,
# so do not reuse a param name with a different type across commands
def _cmd_get(self, key: str) -> Dict: ...
def _cmd_set(self, key: str, value: str) -> Dict: ...Command inference fallback
The LLM occasionally forgets the command field. The adapter scores every subcommand by parameter overlap (required params weigh more) and runs the best match; an explicit command is always honoured.
Reserved tool names
These three belong to built-in tools. A component registering the same name is rejected — keep cell_name clear of them.
shell— ShellToolmemory— MemoryToolfile— FileTool
Sandbox and whitelist
Every non-whitelisted component runs in a subprocess. The process is reused across calls but recycled when idle.
| Parameter | Value |
|---|---|
| Per-command timeout | 120 s |
| Idle process recycle | 300 s |
| Heartbeat interval | 30 s |
| Custom whitelist file | config/sandbox.yaml |
Built-in no-sandbox components (hard-coded, cannot be removed): component, scheduler, skill_manager, skill_installer, sub_agent, web_fetch, web_search, qq_files, telegram_files, feishu_files, weixin_files.
Whitelist operations (list, add, remove) are covered in Part 2; this part only explains the model.
Built-in components
Shipped with the release. The source lives in components/ — read it and edit it directly.
| Component | Function |
|---|---|
component | Generate, list, reload components; sandbox whitelist; cache cleanup |
scheduler | Scheduled tasks: interval / daily / weekly |
skill_manager / skill_installer | Skill package index, install and uninstall |
sub_agent | Parallel sub-agent orchestration with independent tools and persona |
web_fetch / web_search | Headless browser fetch and multi-engine search |
qq_files / telegram_files / feishu_files / weixin_files | File transfer over each IM channel |
Troubleshooting
| Symptom | Fix |
|---|---|
| Component missing from the tool list | Check component.info(name) for the load error — usually an import failure or not inheriting BaseCell |
| Code changes have no effect | Top-level .py edits reload automatically; package-internal edits are not watched — call component.reload() |
