From 32cef2df777f5f8c1997cb18e169b103149f6bc4 Mon Sep 17 00:00:00 2001 From: tanyar09 Date: Thu, 5 Mar 2026 15:15:25 -0500 Subject: [PATCH] Update cron tool documentation and context improvements - Update cron tool and skill documentation - Update TOOLS.md with email tool documentation - Context builder improvements --- nanobot/agent/context.py | 4 +++- nanobot/agent/tools/cron.py | 28 ++++++++++++++++++++++------ nanobot/skills/cron/SKILL.md | 9 +++++++++ workspace/TOOLS.md | 20 ++++++++++++++++++++ 4 files changed, 54 insertions(+), 7 deletions(-) diff --git a/nanobot/agent/context.py b/nanobot/agent/context.py index ada2d65..3e89065 100644 --- a/nanobot/agent/context.py +++ b/nanobot/agent/context.py @@ -118,7 +118,9 @@ For simple acknowledgments like "Thanks", "OK", "You're welcome", "Got it", etc. Always be helpful, accurate, and concise. Before calling tools, briefly tell the user what you're about to do (one short sentence in the user's language). When remembering something important, write to {workspace_path}/memory/MEMORY.md -To recall past events, grep {workspace_path}/memory/HISTORY.md""" +To recall past events, grep {workspace_path}/memory/HISTORY.md + +IMPORTANT: For email queries (latest email, email sender, inbox, etc.), ALWAYS use the read_emails tool. NEVER use exec() with mail/tail/awk commands or read_file() on /var/mail - those will not work. The read_emails tool is the only way to access emails.""" def _load_bootstrap_files(self) -> str: """Load all bootstrap files from workspace.""" diff --git a/nanobot/agent/tools/cron.py b/nanobot/agent/tools/cron.py index 1dcbdd2..6c3fbd6 100644 --- a/nanobot/agent/tools/cron.py +++ b/nanobot/agent/tools/cron.py @@ -26,7 +26,19 @@ class CronTool(Tool): @property def description(self) -> str: - return "Schedule reminders and recurring tasks. REQUIRED: Always include 'action' parameter ('add', 'list', or 'remove'). For reminders, use action='add' with message and timing (in_seconds, at, every_seconds, or cron_expr)." + return """Schedule reminders and recurring tasks. REQUIRED: Always include 'action' parameter ('add', 'list', or 'remove'). + +For 'add' action: +- MUST include 'message' parameter - extract the reminder/task text from user's request +- Examples: 'remind me to call mama' → message='call mama' + +For timing patterns: +- 'remind me in X seconds' → in_seconds=X (DO NOT use 'at') +- 'every X seconds' (forever) → every_seconds=X +- 'every X seconds for Y seconds' → EVERY_SECONDS=X AND IN_SECONDS=Y (creates multiple reminders, DO NOT use 'at') +- 'at specific time' → at='ISO datetime' (only when user specifies exact time) + +CRITICAL: For 'every X seconds for Y seconds', you MUST use both every_seconds AND in_seconds together. DO NOT use 'at' parameter for this pattern.""" @property def parameters(self) -> dict[str, Any]: @@ -40,11 +52,11 @@ class CronTool(Tool): }, "message": { "type": "string", - "description": "Reminder message (for add)" + "description": "REQUIRED for 'add' action: The reminder message to send. Extract this from the user's request. Examples: 'Remind me to call mama' → message='call mama', 'Remind me every hour to drink water' → message='drink water', 'Schedule a task to check email' → message='check email'. Always extract the actual task/reminder text, not the full user request." }, "every_seconds": { "type": "integer", - "description": "Interval in seconds (for recurring tasks)" + "description": "Interval in seconds (for recurring tasks). For 'every X seconds for Y seconds', use BOTH every_seconds AND in_seconds together to create multiple reminders." }, "cron_expr": { "type": "string", @@ -56,11 +68,11 @@ class CronTool(Tool): }, "at": { "type": "string", - "description": "ISO datetime string for one-time execution. Format: YYYY-MM-DDTHH:MM:SS (e.g. '2026-03-03T12:19:30'). You MUST calculate this from the current time shown in your system prompt plus the requested seconds/minutes, then format as ISO string." + "description": "ISO datetime string for one-time execution at a SPECIFIC time. Format: YYYY-MM-DDTHH:MM:SS (e.g. '2026-03-03T12:19:30'). ONLY use this when user specifies an exact time like 'at 3pm' or 'at 2026-03-03 14:30'. DO NOT use 'at' for 'every X seconds for Y seconds' - use every_seconds + in_seconds instead." }, "in_seconds": { "type": "integer", - "description": "Alternative to 'at': Schedule reminder in N seconds from now. Use this instead of calculating 'at' manually. Example: in_seconds=25 for 'remind me in 25 seconds'." + "description": "Schedule reminder in N seconds from now, OR duration for recurring reminders. Use this instead of calculating 'at' manually. Examples: 'remind me in 25 seconds' → in_seconds=25. For 'every 10 seconds for the next minute' → every_seconds=10 AND in_seconds=60 (creates 6 reminders)." }, "reminder": { "type": "boolean", @@ -111,7 +123,11 @@ class CronTool(Tool): reminder: bool = False, ) -> str: if not message: - return "Error: message is required for add" + return "Error: message is required for 'add' action. You must extract the reminder/task text from the user's request. Example: if user says 'remind me to call mama', use message='call mama'. If user says 'remind me every hour to drink water', use message='drink water'." + + # Detect common mistake: using 'at' with 'every_seconds' when 'in_seconds' should be used + if every_seconds is not None and at is not None and in_seconds is None: + return f"Error: You used 'at' with 'every_seconds', but for 'every X seconds for Y seconds' pattern, you MUST use 'in_seconds' instead of 'at'. Example: 'every 10 seconds for the next minute' → every_seconds=10 AND in_seconds=60 (NOT 'at'). The 'in_seconds' parameter specifies the duration, and the tool will create multiple reminders automatically." # Use defaults for CLI mode if context not set channel = self._channel or "cli" diff --git a/nanobot/skills/cron/SKILL.md b/nanobot/skills/cron/SKILL.md index d494f66..2ac8fea 100644 --- a/nanobot/skills/cron/SKILL.md +++ b/nanobot/skills/cron/SKILL.md @@ -15,6 +15,11 @@ Use the `cron` tool to schedule reminders or recurring tasks. ## Examples +**IMPORTANT**: Always extract the message from the user's request: +- User: "remind me to call mama" → `message="call mama"` +- User: "remind me every hour to drink water" → `message="drink water"` +- User: "remind me every 10 seconds for the next minute to call mama" → `message="call mama"` + Fixed reminder: ``` cron(action="add", message="Time to take a break!", every_seconds=1200) @@ -50,6 +55,8 @@ cron(action="remove", job_id="abc123") | remind me in 1 hour | **in_seconds: 3600** (1 hour = 3600 seconds) | | every 20 minutes | every_seconds: 1200 | | every hour | every_seconds: 3600 | +| **every 10 seconds for the next minute** | **every_seconds: 10 AND in_seconds: 60** (creates 6 reminders: at 0s, 10s, 20s, 30s, 40s, 50s) | +| **every 5 seconds for 30 seconds** | **every_seconds: 5 AND in_seconds: 30** (creates 6 reminders) | | every day at 8am | cron_expr: "0 8 * * *" | | weekdays at 5pm | cron_expr: "0 17 * * 1-5" | | 9am Vancouver time daily | cron_expr: "0 9 * * *", tz: "America/Vancouver" | @@ -61,6 +68,8 @@ cron(action="remove", job_id="abc123") - "remind me in 25 seconds" → `cron(action="add", message="...", in_seconds=25)` - "remind me in 5 minutes" → `cron(action="add", message="...", in_seconds=300)` (5 * 60 = 300) - "remind me in 1 hour" → `cron(action="add", message="...", in_seconds=3600)` (60 * 60 = 3600) +- **"remind me every 10 seconds for the next minute"** → `cron(action="add", message="...", every_seconds=10, in_seconds=60)` (creates 6 reminders) +- **"every 5 seconds for 30 seconds"** → `cron(action="add", message="...", every_seconds=5, in_seconds=30)` (creates 6 reminders) The `in_seconds` parameter automatically computes the correct future datetime - you don't need to calculate it yourself! diff --git a/workspace/TOOLS.md b/workspace/TOOLS.md index 926e030..0b84111 100644 --- a/workspace/TOOLS.md +++ b/workspace/TOOLS.md @@ -71,6 +71,26 @@ web_fetch(url: str, extractMode: str = "markdown", maxChars: int = 50000) -> str - Supports markdown or plain text extraction - Output is truncated at 50,000 characters by default +## Email + +### read_emails +Read emails from your configured email account via IMAP. **ALWAYS use this tool for email queries - NEVER use exec with mail commands.** +``` +read_emails(limit: int = 10, unread_only: bool = False, mark_seen: bool = False) -> str +``` + +**CRITICAL:** For ANY question about emails (latest email, email sender, email content, etc.), you MUST use this tool. Do NOT use `exec` with `mail` command or read memory files for email information. This tool connects directly to IMAP and fetches CURRENT, real-time email data. + +**Parameters:** +- `limit`: Maximum number of emails to return (1-50, default: 10) +- `unread_only`: If true, only return unread emails (default: false) +- `mark_seen`: If true, mark emails as read after fetching (default: false) + +**Examples:** +- `read_emails(limit=1)` - Get the latest email +- `read_emails(unread_only=true)` - Get all unread emails +- `read_emails(limit=5, mark_seen=false)` - Get last 5 emails without marking as read + ## Communication ### message