refactor: move workspace/ to nanobot/templates/ for packaging
This commit is contained in:
20
README.md
20
README.md
@@ -841,6 +841,26 @@ nanobot cron remove <job_id>
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>Heartbeat (Periodic Tasks)</b></summary>
|
||||||
|
|
||||||
|
The gateway wakes up every 30 minutes and checks `HEARTBEAT.md` in your workspace (`~/.nanobot/workspace/HEARTBEAT.md`). If the file has tasks, the agent executes them and delivers results to your most recently active chat channel.
|
||||||
|
|
||||||
|
**Setup:** edit `~/.nanobot/workspace/HEARTBEAT.md` (created automatically by `nanobot onboard`):
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Periodic Tasks
|
||||||
|
|
||||||
|
- [ ] Check weather forecast and send a summary
|
||||||
|
- [ ] Scan inbox for urgent emails
|
||||||
|
```
|
||||||
|
|
||||||
|
The agent can also manage this file itself — ask it to "add a periodic task" and it will update `HEARTBEAT.md` for you.
|
||||||
|
|
||||||
|
> **Note:** The gateway must be running (`nanobot gateway`) and you must have chatted with the bot at least once so it knows which channel to deliver to.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
## 🐳 Docker
|
## 🐳 Docker
|
||||||
|
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
|
|||||||
@@ -199,74 +199,26 @@ def onboard():
|
|||||||
|
|
||||||
|
|
||||||
def _create_workspace_templates(workspace: Path):
|
def _create_workspace_templates(workspace: Path):
|
||||||
"""Create default workspace template files."""
|
"""Create default workspace template files from bundled templates."""
|
||||||
templates = {
|
from importlib.resources import files as pkg_files
|
||||||
"AGENTS.md": """# Agent Instructions
|
|
||||||
|
|
||||||
You are a helpful AI assistant. Be concise, accurate, and friendly.
|
templates_dir = pkg_files("nanobot") / "templates"
|
||||||
|
|
||||||
## Guidelines
|
for item in templates_dir.iterdir():
|
||||||
|
if not item.name.endswith(".md"):
|
||||||
|
continue
|
||||||
|
dest = workspace / item.name
|
||||||
|
if not dest.exists():
|
||||||
|
dest.write_text(item.read_text(encoding="utf-8"), encoding="utf-8")
|
||||||
|
console.print(f" [dim]Created {item.name}[/dim]")
|
||||||
|
|
||||||
- Always explain what you're doing before taking actions
|
|
||||||
- Ask for clarification when the request is ambiguous
|
|
||||||
- Use tools to help accomplish tasks
|
|
||||||
- Remember important information in memory/MEMORY.md; past events are logged in memory/HISTORY.md
|
|
||||||
""",
|
|
||||||
"SOUL.md": """# Soul
|
|
||||||
|
|
||||||
I am nanobot, a lightweight AI assistant.
|
|
||||||
|
|
||||||
## Personality
|
|
||||||
|
|
||||||
- Helpful and friendly
|
|
||||||
- Concise and to the point
|
|
||||||
- Curious and eager to learn
|
|
||||||
|
|
||||||
## Values
|
|
||||||
|
|
||||||
- Accuracy over speed
|
|
||||||
- User privacy and safety
|
|
||||||
- Transparency in actions
|
|
||||||
""",
|
|
||||||
"USER.md": """# User
|
|
||||||
|
|
||||||
Information about the user goes here.
|
|
||||||
|
|
||||||
## Preferences
|
|
||||||
|
|
||||||
- Communication style: (casual/formal)
|
|
||||||
- Timezone: (your timezone)
|
|
||||||
- Language: (your preferred language)
|
|
||||||
""",
|
|
||||||
}
|
|
||||||
|
|
||||||
for filename, content in templates.items():
|
|
||||||
file_path = workspace / filename
|
|
||||||
if not file_path.exists():
|
|
||||||
file_path.write_text(content, encoding="utf-8")
|
|
||||||
console.print(f" [dim]Created {filename}[/dim]")
|
|
||||||
|
|
||||||
# Create memory directory and MEMORY.md
|
|
||||||
memory_dir = workspace / "memory"
|
memory_dir = workspace / "memory"
|
||||||
memory_dir.mkdir(exist_ok=True)
|
memory_dir.mkdir(exist_ok=True)
|
||||||
|
|
||||||
|
memory_template = templates_dir / "memory" / "MEMORY.md"
|
||||||
memory_file = memory_dir / "MEMORY.md"
|
memory_file = memory_dir / "MEMORY.md"
|
||||||
if not memory_file.exists():
|
if not memory_file.exists():
|
||||||
memory_file.write_text("""# Long-term Memory
|
memory_file.write_text(memory_template.read_text(encoding="utf-8"), encoding="utf-8")
|
||||||
|
|
||||||
This file stores important information that should persist across sessions.
|
|
||||||
|
|
||||||
## User Information
|
|
||||||
|
|
||||||
(Important facts about the user)
|
|
||||||
|
|
||||||
## Preferences
|
|
||||||
|
|
||||||
(User preferences learned over time)
|
|
||||||
|
|
||||||
## Important Notes
|
|
||||||
|
|
||||||
(Things to remember)
|
|
||||||
""", encoding="utf-8")
|
|
||||||
console.print(" [dim]Created memory/MEMORY.md[/dim]")
|
console.print(" [dim]Created memory/MEMORY.md[/dim]")
|
||||||
|
|
||||||
history_file = memory_dir / "HISTORY.md"
|
history_file = memory_dir / "HISTORY.md"
|
||||||
@@ -274,9 +226,7 @@ This file stores important information that should persist across sessions.
|
|||||||
history_file.write_text("", encoding="utf-8")
|
history_file.write_text("", encoding="utf-8")
|
||||||
console.print(" [dim]Created memory/HISTORY.md[/dim]")
|
console.print(" [dim]Created memory/HISTORY.md[/dim]")
|
||||||
|
|
||||||
# Create skills directory for custom user skills
|
(workspace / "skills").mkdir(exist_ok=True)
|
||||||
skills_dir = workspace / "skills"
|
|
||||||
skills_dir.mkdir(exist_ok=True)
|
|
||||||
|
|
||||||
|
|
||||||
def _make_provider(config: Config):
|
def _make_provider(config: Config):
|
||||||
|
|||||||
29
nanobot/templates/AGENTS.md
Normal file
29
nanobot/templates/AGENTS.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Agent Instructions
|
||||||
|
|
||||||
|
You are a helpful AI assistant. Be concise, accurate, and friendly.
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
|
||||||
|
- Always explain what you're doing before taking actions
|
||||||
|
- Ask for clarification when the request is ambiguous
|
||||||
|
- Remember important information in `memory/MEMORY.md`; past events are logged in `memory/HISTORY.md`
|
||||||
|
|
||||||
|
## Scheduled Reminders
|
||||||
|
|
||||||
|
When user asks for a reminder at a specific time, use `exec` to run:
|
||||||
|
```
|
||||||
|
nanobot cron add --name "reminder" --message "Your message" --at "YYYY-MM-DDTHH:MM:SS" --deliver --to "USER_ID" --channel "CHANNEL"
|
||||||
|
```
|
||||||
|
Get USER_ID and CHANNEL from the current session (e.g., `8281248569` and `telegram` from `telegram:8281248569`).
|
||||||
|
|
||||||
|
**Do NOT just write reminders to MEMORY.md** — that won't trigger actual notifications.
|
||||||
|
|
||||||
|
## Heartbeat Tasks
|
||||||
|
|
||||||
|
`HEARTBEAT.md` is checked every 30 minutes. Use file tools to manage periodic tasks:
|
||||||
|
|
||||||
|
- **Add**: `edit_file` to append new tasks
|
||||||
|
- **Remove**: `edit_file` to delete completed tasks
|
||||||
|
- **Rewrite**: `write_file` to replace all tasks
|
||||||
|
|
||||||
|
When the user asks for a recurring/periodic task, update `HEARTBEAT.md` instead of creating a one-time cron reminder.
|
||||||
36
nanobot/templates/TOOLS.md
Normal file
36
nanobot/templates/TOOLS.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# Tool Usage Notes
|
||||||
|
|
||||||
|
Tool signatures are provided automatically via function calling.
|
||||||
|
This file documents non-obvious constraints and usage patterns.
|
||||||
|
|
||||||
|
## exec — Safety Limits
|
||||||
|
|
||||||
|
- Commands have a configurable timeout (default 60s)
|
||||||
|
- Dangerous commands are blocked (rm -rf, format, dd, shutdown, etc.)
|
||||||
|
- Output is truncated at 10,000 characters
|
||||||
|
- `restrictToWorkspace` config can limit file access to the workspace
|
||||||
|
|
||||||
|
## Cron — Scheduled Reminders
|
||||||
|
|
||||||
|
Use `exec` to create scheduled reminders:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Recurring: every day at 9am
|
||||||
|
nanobot cron add --name "morning" --message "Good morning!" --cron "0 9 * * *"
|
||||||
|
|
||||||
|
# With timezone (--tz only works with --cron)
|
||||||
|
nanobot cron add --name "standup" --message "Standup time!" --cron "0 10 * * 1-5" --tz "Asia/Shanghai"
|
||||||
|
|
||||||
|
# Recurring: every 2 hours
|
||||||
|
nanobot cron add --name "water" --message "Drink water!" --every 7200
|
||||||
|
|
||||||
|
# One-time: specific ISO time
|
||||||
|
nanobot cron add --name "meeting" --message "Meeting starts now!" --at "2025-01-31T15:00:00"
|
||||||
|
|
||||||
|
# Deliver to a specific channel/user
|
||||||
|
nanobot cron add --name "reminder" --message "Check email" --at "2025-01-31T09:00:00" --deliver --to "USER_ID" --channel "CHANNEL"
|
||||||
|
|
||||||
|
# Manage jobs
|
||||||
|
nanobot cron list
|
||||||
|
nanobot cron remove <job_id>
|
||||||
|
```
|
||||||
0
nanobot/templates/__init__.py
Normal file
0
nanobot/templates/__init__.py
Normal file
0
nanobot/templates/memory/__init__.py
Normal file
0
nanobot/templates/memory/__init__.py
Normal file
@@ -64,10 +64,11 @@ packages = ["nanobot"]
|
|||||||
[tool.hatch.build.targets.wheel.sources]
|
[tool.hatch.build.targets.wheel.sources]
|
||||||
"nanobot" = "nanobot"
|
"nanobot" = "nanobot"
|
||||||
|
|
||||||
# Include non-Python files in skills
|
# Include non-Python files in skills and templates
|
||||||
[tool.hatch.build]
|
[tool.hatch.build]
|
||||||
include = [
|
include = [
|
||||||
"nanobot/**/*.py",
|
"nanobot/**/*.py",
|
||||||
|
"nanobot/templates/**/*.md",
|
||||||
"nanobot/skills/**/*.md",
|
"nanobot/skills/**/*.md",
|
||||||
"nanobot/skills/**/*.sh",
|
"nanobot/skills/**/*.sh",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
# Agent Instructions
|
|
||||||
|
|
||||||
You are a helpful AI assistant. Be concise, accurate, and friendly.
|
|
||||||
|
|
||||||
## Guidelines
|
|
||||||
|
|
||||||
- Always explain what you're doing before taking actions
|
|
||||||
- Ask for clarification when the request is ambiguous
|
|
||||||
- Use tools to help accomplish tasks
|
|
||||||
- Remember important information in your memory files
|
|
||||||
|
|
||||||
## Tools Available
|
|
||||||
|
|
||||||
You have access to:
|
|
||||||
- File operations (read, write, edit, list)
|
|
||||||
- Shell commands (exec)
|
|
||||||
- Web access (search, fetch)
|
|
||||||
- Messaging (message)
|
|
||||||
- Background tasks (spawn)
|
|
||||||
|
|
||||||
## Memory
|
|
||||||
|
|
||||||
- `memory/MEMORY.md` — long-term facts (preferences, context, relationships)
|
|
||||||
- `memory/HISTORY.md` — append-only event log, search with grep to recall past events
|
|
||||||
|
|
||||||
## Scheduled Reminders
|
|
||||||
|
|
||||||
When user asks for a reminder at a specific time, use `exec` to run:
|
|
||||||
```
|
|
||||||
nanobot cron add --name "reminder" --message "Your message" --at "YYYY-MM-DDTHH:MM:SS" --deliver --to "USER_ID" --channel "CHANNEL"
|
|
||||||
```
|
|
||||||
Get USER_ID and CHANNEL from the current session (e.g., `8281248569` and `telegram` from `telegram:8281248569`).
|
|
||||||
|
|
||||||
**Do NOT just write reminders to MEMORY.md** — that won't trigger actual notifications.
|
|
||||||
|
|
||||||
## Heartbeat Tasks
|
|
||||||
|
|
||||||
`HEARTBEAT.md` is checked every 30 minutes. You can manage periodic tasks by editing this file:
|
|
||||||
|
|
||||||
- **Add a task**: Use `edit_file` to append new tasks to `HEARTBEAT.md`
|
|
||||||
- **Remove a task**: Use `edit_file` to remove completed or obsolete tasks
|
|
||||||
- **Rewrite tasks**: Use `write_file` to completely rewrite the task list
|
|
||||||
|
|
||||||
Task format examples:
|
|
||||||
```
|
|
||||||
- [ ] Check calendar and remind of upcoming events
|
|
||||||
- [ ] Scan inbox for urgent emails
|
|
||||||
- [ ] Check weather forecast for today
|
|
||||||
```
|
|
||||||
|
|
||||||
When the user asks you to add a recurring/periodic task, update `HEARTBEAT.md` instead of creating a one-time reminder. Keep the file small to minimize token usage.
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
# Available Tools
|
|
||||||
|
|
||||||
This document describes the tools available to nanobot.
|
|
||||||
|
|
||||||
## File Operations
|
|
||||||
|
|
||||||
### read_file
|
|
||||||
Read the contents of a file.
|
|
||||||
```
|
|
||||||
read_file(path: str) -> str
|
|
||||||
```
|
|
||||||
|
|
||||||
### write_file
|
|
||||||
Write content to a file (creates parent directories if needed).
|
|
||||||
```
|
|
||||||
write_file(path: str, content: str) -> str
|
|
||||||
```
|
|
||||||
|
|
||||||
### edit_file
|
|
||||||
Edit a file by replacing specific text.
|
|
||||||
```
|
|
||||||
edit_file(path: str, old_text: str, new_text: str) -> str
|
|
||||||
```
|
|
||||||
|
|
||||||
### list_dir
|
|
||||||
List contents of a directory.
|
|
||||||
```
|
|
||||||
list_dir(path: str) -> str
|
|
||||||
```
|
|
||||||
|
|
||||||
## Shell Execution
|
|
||||||
|
|
||||||
### exec
|
|
||||||
Execute a shell command and return output.
|
|
||||||
```
|
|
||||||
exec(command: str, working_dir: str = None) -> str
|
|
||||||
```
|
|
||||||
|
|
||||||
**Safety Notes:**
|
|
||||||
- Commands have a configurable timeout (default 60s)
|
|
||||||
- Dangerous commands are blocked (rm -rf, format, dd, shutdown, etc.)
|
|
||||||
- Output is truncated at 10,000 characters
|
|
||||||
- Optional `restrictToWorkspace` config to limit paths
|
|
||||||
|
|
||||||
## Web Access
|
|
||||||
|
|
||||||
### web_search
|
|
||||||
Search the web using Brave Search API.
|
|
||||||
```
|
|
||||||
web_search(query: str, count: int = 5) -> str
|
|
||||||
```
|
|
||||||
|
|
||||||
Returns search results with titles, URLs, and snippets. Requires `tools.web.search.apiKey` in config.
|
|
||||||
|
|
||||||
### web_fetch
|
|
||||||
Fetch and extract main content from a URL.
|
|
||||||
```
|
|
||||||
web_fetch(url: str, extractMode: str = "markdown", maxChars: int = 50000) -> str
|
|
||||||
```
|
|
||||||
|
|
||||||
**Notes:**
|
|
||||||
- Content is extracted using readability
|
|
||||||
- Supports markdown or plain text extraction
|
|
||||||
- Output is truncated at 50,000 characters by default
|
|
||||||
|
|
||||||
## Communication
|
|
||||||
|
|
||||||
### message
|
|
||||||
Send a message to the user (used internally).
|
|
||||||
```
|
|
||||||
message(content: str, channel: str = None, chat_id: str = None) -> str
|
|
||||||
```
|
|
||||||
|
|
||||||
## Background Tasks
|
|
||||||
|
|
||||||
### spawn
|
|
||||||
Spawn a subagent to handle a task in the background.
|
|
||||||
```
|
|
||||||
spawn(task: str, label: str = None) -> str
|
|
||||||
```
|
|
||||||
|
|
||||||
Use for complex or time-consuming tasks that can run independently. The subagent will complete the task and report back when done.
|
|
||||||
|
|
||||||
## Scheduled Reminders (Cron)
|
|
||||||
|
|
||||||
Use the `exec` tool to create scheduled reminders with `nanobot cron add`:
|
|
||||||
|
|
||||||
### Set a recurring reminder
|
|
||||||
```bash
|
|
||||||
# Every day at 9am
|
|
||||||
nanobot cron add --name "morning" --message "Good morning! ☀️" --cron "0 9 * * *"
|
|
||||||
|
|
||||||
# Every 2 hours
|
|
||||||
nanobot cron add --name "water" --message "Drink water! 💧" --every 7200
|
|
||||||
```
|
|
||||||
|
|
||||||
### Set a one-time reminder
|
|
||||||
```bash
|
|
||||||
# At a specific time (ISO format)
|
|
||||||
nanobot cron add --name "meeting" --message "Meeting starts now!" --at "2025-01-31T15:00:00"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Manage reminders
|
|
||||||
```bash
|
|
||||||
nanobot cron list # List all jobs
|
|
||||||
nanobot cron remove <job_id> # Remove a job
|
|
||||||
```
|
|
||||||
|
|
||||||
## Heartbeat Task Management
|
|
||||||
|
|
||||||
The `HEARTBEAT.md` file in the workspace is checked every 30 minutes.
|
|
||||||
Use file operations to manage periodic tasks:
|
|
||||||
|
|
||||||
### Add a heartbeat task
|
|
||||||
```python
|
|
||||||
# Append a new task
|
|
||||||
edit_file(
|
|
||||||
path="HEARTBEAT.md",
|
|
||||||
old_text="## Example Tasks",
|
|
||||||
new_text="- [ ] New periodic task here\n\n## Example Tasks"
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Remove a heartbeat task
|
|
||||||
```python
|
|
||||||
# Remove a specific task
|
|
||||||
edit_file(
|
|
||||||
path="HEARTBEAT.md",
|
|
||||||
old_text="- [ ] Task to remove\n",
|
|
||||||
new_text=""
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Rewrite all tasks
|
|
||||||
```python
|
|
||||||
# Replace the entire file
|
|
||||||
write_file(
|
|
||||||
path="HEARTBEAT.md",
|
|
||||||
content="# Heartbeat Tasks\n\n- [ ] Task 1\n- [ ] Task 2\n"
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Adding Custom Tools
|
|
||||||
|
|
||||||
To add custom tools:
|
|
||||||
1. Create a class that extends `Tool` in `nanobot/agent/tools/`
|
|
||||||
2. Implement `name`, `description`, `parameters`, and `execute`
|
|
||||||
3. Register it in `AgentLoop._register_default_tools()`
|
|
||||||
Reference in New Issue
Block a user