Improve cron job execution context to ensure proper message delivery and
session history recording.
Changes:
- Add [绯荤粺瀹氭椂浠诲姟] prefix to cron reminder messages to clearly mark
them as system-driven, not user queries
- Use user role for cron reminder messages (required by some LLM APIs)
- Properly handle MessageTool to avoid duplicate message delivery
- Correctly save turn history with proper skip count
- Ensure Runtime Context is included in the message list
This ensures that:
1. Cron jobs execute with proper context
2. Messages are correctly delivered to users
3. Session history accurately records cron job interactions
4. The LLM understands these are system-driven reminders, not user queries
When nanobot is run as a systemd service with ProtectSystem=strict,
the process cwd defaults to the read-only root filesystem (/). botpy's
default Client configuration includes a TimedRotatingFileHandler that
writes 'botpy.log' to os.getcwd(), which raises [Errno 30] Read-only
file system.
Pass ext_handlers=False when constructing the botpy Client subclass to
suppress the file handler. nanobot already routes all log output through
loguru, so botpy's file handler is redundant.
Fixes#1343
Co-Authored-By: Claude <noreply@anthropic.com>
Part 1: Make system prompt static
- Move Current Time from system prompt to user message prefix
- System prompt now only changes when config/skills change, not every minute
- Timestamp injected as [YYYY-MM-DD HH:MM (Day) (TZ)] prefix on each user message
Part 2: Add second cache_control breakpoint
- Existing: system message breakpoint (caches static system prompt)
- New: second-to-last message breakpoint (caches conversation history prefix)
- Refactored _apply_cache_control with shared _mark() helper
Before: 0% cache hit rate (system prompt changed every minute)
After: ~90% savings on cached input tokens for multi-turn conversations
Closes#981
- Remove trailing whitespace and normalize blank lines
- Unify string quotes and line breaks for long lines
- Sort imports alphabetically across modules
Prevent infinite loops by tracking processed message IDs in WhatsApp
channel. The bridge may send duplicate messages which caused the bot
to respond repeatedly with the same generic message.
Changes:
- Add _processed_message_ids deque (max 2000) to track seen messages
- Skip processing if message_id was already processed
- Align WhatsApp dedup with other channels (Feishu, Email, Mochat, QQ)
This fixes the issue where WhatsApp gets stuck in a loop sending
identical responses repeatedly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>