- Add `_SELECT_FIELD_HINTS` for select fields with predefined choices
(e.g., reasoning_effort: low/medium/high with hint text)
- Add `_select_with_back()` using prompt_toolkit for custom key bindings
- Support Escape and Left arrow keys to go back in menus
- Apply to field config, provider selection, and channel selection menus
- Add model_info.py module with litellm-based model lookup
- Provide autocomplete suggestions for model names
- Auto-fill context_window_tokens when model changes (only at default)
- Add "Get recommended value" option for manual context lookup
- Dynamically load provider keywords from registry (no hardcoding)
Resolves#2018
- Add CONTRIBUTING.md with detailed contribution guidelines
- Add branching strategy section to README.md explaining main/nightly branches
- Include maintainer information and development setup instructions
- test_channel_plugins: fix assertion logic for discoverable channels
- test_filesystem_tools: normalize path separators for Windows
- test_tool_validation: use python to generate output, avoid cmd line limits
- test_context_prompt_cache: Update test to reflect merged runtime
context and user message (commit ad99d5a merged them into one)
- Remove test_cron_commands.py: cron add CLI command was removed
in commit c05cb2e (unified scheduling via cron tool)
- test_context_prompt_cache: Update test to reflect merged runtime
context and user message (commit ad99d5a merged them into one)
- Remove test_cron_commands.py: cron add CLI command was removed
in commit c05cb2e (unified scheduling via cron tool)
- Add --workspace/-w flag to specify workspace directory
- Add --config/-c flag to specify config file path
- Move cron store to workspace directory for per-instance isolation
- Enable running multiple nanobot instances simultaneously
When allow_from is not configured, block all access by default
instead of allowing everyone. This prevents unauthorized access
when channels are enabled without explicit allow lists.
- Add handler for SIGTERM to prevent "Terminated" message on Linux
- Add handler for SIGHUP for terminal closure handling
- Ignore SIGPIPE to prevent silent process termination
- Change os._exit(0) to sys.exit(0) for proper cleanup
Fixes issue #1365
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
A refactoring in commit 132807a introduced a regression where the final
response was silently discarded whenever the message tool was used,
regardless of the target. This restored the original logic from PR #832
that only suppresses the final reply when the message tool sends to the
same (channel, chat_id) as the original message.
Changes:
- message.py: Replace _sent_in_turn: bool with _turn_sends: list[tuple]
to track actual send targets, add get_turn_sends() method
- loop.py: Check if (msg.channel, msg.chat_id) is in sent_targets before
suppressing final reply. Also move the "Response to" log after the
suppress check to avoid misleading logs.
- Add unit tests for the suppress logic
This ensures:
- Email sent via message tool → Feishu still gets confirmation
- Message tool sends to same Feishu chat → No duplicate (suppressed)
Previously, `autoReplyEnabled=false` would block ALL email sends,
including proactive emails triggered from other channels (e.g., asking
nanobot on Feishu to send an email).
Now `autoReplyEnabled` only controls automatic replies to incoming
emails, not proactive sends. This allows users to disable auto-replies
while still being able to ask nanobot to send emails on demand.
Changes:
- Check if recipient is in `_last_subject_by_chat` to determine if
it's a reply
- Only skip sending when it's a reply AND auto_reply_enabled is false
- Add test for proactive send with auto_reply_enabled=false
- Update existing test to verify reply behavior