- Merged latest main (no conflicts)
- Added warning log when only one of proxy_username/proxy_password is set
- Added test_start_no_proxy_auth_when_only_password for coverage parity
Made-with: Cursor
- Add proxy, proxy_username, proxy_password fields to DiscordConfig
- Pass proxy and proxy_auth to discord.Client
- Add aiohttp.BasicAuth when credentials are provided
- Add tests for proxy configuration scenarios
Add allowed_env_keys config field to selectively forward host environment variables (e.g. GOPATH, JAVA_HOME) into the sandboxed subprocess environment, while keeping the default allow-list unchanged.
- Merged latest main (no conflicts)
- Added test_llm_error_not_appended_to_session_messages: verifies error
content stays out of session messages
- Added test_streamed_flag_not_set_on_llm_error: verifies _streamed is
not set when LLM returns an error, so ChannelManager delivers it
Made-with: Cursor
When the LLM returns an error (e.g. 429 quota exceeded, stream timeout),
streaming channels silently drop the error message because `_streamed=True`
is set in metadata even though no content was actually streamed.
This change:
- Skips setting `_streamed` when stop_reason is "error", so error messages
go through the normal channel.send() path and reach the user
- Stops appending error content to session history, preventing error
messages from polluting subsequent conversation context
- Exposes stop_reason from _run_agent_loop to enable the above check
- Use hmac.compare_digest for timing-safe static token comparison
- Add issued token capacity limit (_MAX_ISSUED_TOKENS=10000) with 429 response
- Use atomic pop in _take_issued_token_if_valid to eliminate TOCTOU window
- Enforce TLSv1.2 minimum version for SSL connections
- Extract _safe_send helper for consistent ConnectionClosed handling
- Move connection registration after ready send to prevent out-of-order delivery
- Add HTTP-level allow_from check and client_id truncation in process_request
- Make stop() idempotent with graceful shutdown error handling
- Normalize path via validator instead of leaving raw value
- Default websocket_requires_token to True for secure-by-default behavior
- Add integration tests and ws_test_client helper
- Refactor tests to use shared _ch factory and bus fixture
Resolved conflict in azure_openai_provider.py by keeping main's
Responses API implementation (role alternation not needed for the
Responses API input format).
Made-with: Cursor
The Enabled column in channels status and plugins list commands had a default green style that overrode the dim markup for disabled items. This caused no values to appear green instead of dimmed. Remove the default style to let cell-level markup control the display correctly.
Fix accidental line corruption in split_message() where 'break' was
merged with unrelated code during manual editing.
The actual fix: build_assistant_message() now returns content or ""
instead of content (which could be None), preventing providers like
MiMo V2 Omni from rejecting tool-call messages with missing text field.
Fixes#2519
nanobot's Windows exec environment was not forwarding ProgramFiles and related variables, so docker desktop start could not discover the desktop CLI plugin and reported unknown command. Forward the missing variables and add a regression test that covers the Windows env shape.
The /status command divided context_used by 1000 but context_total by
1024, producing inconsistent values. For example a 128000-token window
displayed as 125k instead of 128k. Tokens are not a binary unit, so
both should use 1000.
Keep importlib.metadata as the primary source for installed packages, but avoid PackageNotFoundError when nanobot is imported directly from a source tree.
Made-with: Cursor
Fixes#2856
Previously __version__ was hardcoded as '0.4.1' in __init__.py while
pyproject.toml declared version '0.1.5'. This caused nanobot gateway to
report version 0.4.1 on startup while pip showed 0.1.5.
Now __version__ reads from importlib.metadata.version('nanobot-ai'),
keeping pyproject.toml as the single source of truth.
Add reraise parameter to AgentHook so hooks can opt out of exception
swallowing in CompositeHook._for_each_hook_safe. _LoopHook sets
reraise=True to let its exceptions propagate. _LoopHookChain is removed
and replaced with CompositeHook([loop_hook] + extra_hooks).
Signed-off-by: Lingao Meng <menglingao@xiaomi.com>
Keep manual runs from flipping the scheduler's running flag, rebuild merged run history records from action logs, and avoid delaying sub-second jobs to a one-second floor. Add regression coverage for disabled/manual runs, merged history persistence, and sub-second timers.
Made-with: Cursor
Preserve path folding for quoted exec command paths with spaces so hint previews do not fall back to mid-path truncation. Add regression coverage for quoted Unix and Windows path cases.
Made-with: Cursor
1. exec tool hints previously used val[:40] blind character truncation,
cutting paths mid-segment. Now detects file paths via regex and
abbreviates them with abbreviate_path. Supports Windows, Unix
absolute, and ~/ home paths.
2. Deduplication now compares fully formatted hint strings instead of
tool names alone. Fixes ls /Desktop and ls /Downloads being
incorrectly merged as "ls /Desktop × 2".
Co-authored-by: xzq.xu <zhiqiang.xu@nodeskai.com>
Forward static location pins as [location: lat, lon] content so the
agent can respond to geo messages and pass coordinates to MCP tools.
ClosesHKUDS/nanobot#2909
ExecTool hardcoded bash, breaking exec on Windows. Now uses cmd.exe
via COMSPEC on Windows with a curated minimal env (PATH, SYSTEMROOT,
etc.) that excludes secrets. bwrap sandbox gracefully skips on Windows.
- Fix assertion in streaming dict fallback test (trailing space in data
not reflected in expected value).
- Add two regression tests proving that models with reasoning_content
(e.g. DeepSeek-R1) and standard models (no reasoning fields) are
completely unaffected by the reasoning fallback.
Made-with: Cursor
- Add reasoning_content fallback from reasoning in _parse dict branch
- Add content fallback from msg.reasoning in _parse SDK object branch
- Add reasoning_content fallback in _parse SDK object branch
- Add reasoning fallback in _parse_chunks dict branch
- Add reasoning fallback in _parse_chunks SDK object branch
This ensures StepFun Plan API works correctly in both streaming and
non-streaming modes, for both dict and SDK object response formats.
Add comprehensive tests for the StepFun Plan API compatibility fix:
- _parse dict branch: content and reasoning_content fallback to reasoning
- _parse SDK object branch: same fallback for pydantic response objects
- _parse_chunks dict branch: reasoning field handled in streaming mode
- _parse_chunks SDK branch: reasoning fallback for SDK delta objects
- Precedence tests: reasoning_content field takes priority over reasoning
Refs: fix(provider): support StepFun Plan API reasoning field fallback
StepFun Plan API returns response content in the 'reasoning' field when
the model is in thinking mode and 'content' is empty. OpenAICompatProvider
previously only checked 'content' and 'reasoning_content', missing this field.
This patch adds a fallback: if content is empty and 'reasoning' is present,
extract text from reasoning to populate content, ensuring StepFun models
(step-3.5-flash, step-3.5-flash-2603) work correctly with tool calls.
Co-authored-by: moranfong <moranfong@gmail.com>
- Drop *.pycs, *.pywz, *.pyzz — not real Python file extensions.
- Move nano.*.save from "Project-specific" to "Editors & IDEs" where
it belongs (nano editor backup files, not project artifacts).
Made-with: Cursor
- Propagate `description` from MCP prompt arguments into the JSON
Schema so LLMs can better understand prompt parameters.
- Align generic-exception error message with tool/resource wrappers
(drop redundant `{exc}` detail).
- Extend test fixture to mock `mcp.shared.exceptions.McpError`.
- Add tests for argument description forwarding and McpError handling.
Made-with: Cursor
Add MCPResourceWrapper and MCPPromptWrapper classes that expose MCP
server resources and prompts as nanobot tools. Resources are read-only
tools that fetch content by URI, and prompts are read-only tools that
return filled prompt templates with optional arguments.
- MCPResourceWrapper: reads resource content (text and binary) via URI
- MCPPromptWrapper: gets prompt templates with typed arguments
- Both handle timeouts, cancellation, and MCP SDK 1.x error types
- Resources and prompts are registered during server connection
- Gracefully handles servers that don't support resources/prompts