diff --git a/README.md b/README.md index f20e21f..8c47f0f 100644 --- a/README.md +++ b/README.md @@ -841,6 +841,26 @@ nanobot cron remove +
+Heartbeat (Periodic Tasks) + +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. + +
+ ## 🐳 Docker > [!TIP] diff --git a/nanobot/cli/commands.py b/nanobot/cli/commands.py index c8948ee..5edebfa 100644 --- a/nanobot/cli/commands.py +++ b/nanobot/cli/commands.py @@ -199,84 +199,34 @@ def onboard(): def _create_workspace_templates(workspace: Path): - """Create default workspace template files.""" - templates = { - "AGENTS.md": """# Agent Instructions + """Create default workspace template files from bundled templates.""" + from importlib.resources import files as pkg_files -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.mkdir(exist_ok=True) + + memory_template = templates_dir / "memory" / "MEMORY.md" memory_file = memory_dir / "MEMORY.md" if not memory_file.exists(): - memory_file.write_text("""# Long-term Memory - -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") + memory_file.write_text(memory_template.read_text(encoding="utf-8"), encoding="utf-8") console.print(" [dim]Created memory/MEMORY.md[/dim]") - + history_file = memory_dir / "HISTORY.md" if not history_file.exists(): history_file.write_text("", encoding="utf-8") console.print(" [dim]Created memory/HISTORY.md[/dim]") - # Create skills directory for custom user skills - skills_dir = workspace / "skills" - skills_dir.mkdir(exist_ok=True) + (workspace / "skills").mkdir(exist_ok=True) def _make_provider(config: Config): diff --git a/nanobot/templates/AGENTS.md b/nanobot/templates/AGENTS.md new file mode 100644 index 0000000..155a0b2 --- /dev/null +++ b/nanobot/templates/AGENTS.md @@ -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. diff --git a/workspace/HEARTBEAT.md b/nanobot/templates/HEARTBEAT.md similarity index 100% rename from workspace/HEARTBEAT.md rename to nanobot/templates/HEARTBEAT.md diff --git a/workspace/SOUL.md b/nanobot/templates/SOUL.md similarity index 100% rename from workspace/SOUL.md rename to nanobot/templates/SOUL.md diff --git a/nanobot/templates/TOOLS.md b/nanobot/templates/TOOLS.md new file mode 100644 index 0000000..757edd2 --- /dev/null +++ b/nanobot/templates/TOOLS.md @@ -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 +``` diff --git a/workspace/USER.md b/nanobot/templates/USER.md similarity index 100% rename from workspace/USER.md rename to nanobot/templates/USER.md diff --git a/nanobot/templates/__init__.py b/nanobot/templates/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/workspace/memory/MEMORY.md b/nanobot/templates/memory/MEMORY.md similarity index 100% rename from workspace/memory/MEMORY.md rename to nanobot/templates/memory/MEMORY.md diff --git a/nanobot/templates/memory/__init__.py b/nanobot/templates/memory/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyproject.toml b/pyproject.toml index c337d02..cb58ec5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,10 +64,11 @@ packages = ["nanobot"] [tool.hatch.build.targets.wheel.sources] "nanobot" = "nanobot" -# Include non-Python files in skills +# Include non-Python files in skills and templates [tool.hatch.build] include = [ "nanobot/**/*.py", + "nanobot/templates/**/*.md", "nanobot/skills/**/*.md", "nanobot/skills/**/*.sh", ] diff --git a/workspace/AGENTS.md b/workspace/AGENTS.md deleted file mode 100644 index 69bd823..0000000 --- a/workspace/AGENTS.md +++ /dev/null @@ -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. diff --git a/workspace/TOOLS.md b/workspace/TOOLS.md deleted file mode 100644 index 0134a64..0000000 --- a/workspace/TOOLS.md +++ /dev/null @@ -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 # 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()`