From 8de2f8d58845a13aa7c8df290a9da37705fd4160 Mon Sep 17 00:00:00 2001 From: haosenwang1018 Date: Tue, 24 Feb 2026 04:21:55 +0800 Subject: [PATCH 1/9] fix: preserve reasoning_content in message sanitization for thinking models _sanitize_messages strips all non-standard keys from messages, including reasoning_content. Thinking-enabled models like Moonshot Kimi k2.5 require reasoning_content to be present in assistant tool call messages when thinking mode is on, causing a BadRequestError (#1014). Add reasoning_content to _ALLOWED_MSG_KEYS so it passes through sanitization when present. Fixes #1014 --- nanobot/providers/litellm_provider.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nanobot/providers/litellm_provider.py b/nanobot/providers/litellm_provider.py index 7402a2b..0918954 100644 --- a/nanobot/providers/litellm_provider.py +++ b/nanobot/providers/litellm_provider.py @@ -12,8 +12,9 @@ from nanobot.providers.base import LLMProvider, LLMResponse, ToolCallRequest from nanobot.providers.registry import find_by_model, find_gateway -# Standard OpenAI chat-completion message keys; extras (e.g. reasoning_content) are stripped for strict providers. -_ALLOWED_MSG_KEYS = frozenset({"role", "content", "tool_calls", "tool_call_id", "name"}) +# Standard OpenAI chat-completion message keys plus reasoning_content for +# thinking-enabled models (Kimi k2.5, DeepSeek-R1, etc.). +_ALLOWED_MSG_KEYS = frozenset({"role", "content", "tool_calls", "tool_call_id", "name", "reasoning_content"}) class LiteLLMProvider(LLMProvider): From abcce1e1db3282651a916f5de9193bb4025ff559 Mon Sep 17 00:00:00 2001 From: aiguozhi123456 Date: Tue, 24 Feb 2026 03:18:23 +0000 Subject: [PATCH 2/9] feat(exec): add path_append config to extend PATH for subprocess --- nanobot/agent/tools/shell.py | 7 +++++++ nanobot/config/schema.py | 1 + 2 files changed, 8 insertions(+) diff --git a/nanobot/agent/tools/shell.py b/nanobot/agent/tools/shell.py index e3592a7..c11fa2d 100644 --- a/nanobot/agent/tools/shell.py +++ b/nanobot/agent/tools/shell.py @@ -19,6 +19,7 @@ class ExecTool(Tool): deny_patterns: list[str] | None = None, allow_patterns: list[str] | None = None, restrict_to_workspace: bool = False, + path_append: str = "/usr/sbin:/usr/local/sbin", ): self.timeout = timeout self.working_dir = working_dir @@ -35,6 +36,7 @@ class ExecTool(Tool): ] self.allow_patterns = allow_patterns or [] self.restrict_to_workspace = restrict_to_workspace + self.path_append = path_append @property def name(self) -> str: @@ -67,12 +69,17 @@ class ExecTool(Tool): if guard_error: return guard_error + env = os.environ.copy() + if self.path_append: + env["PATH"] = env.get("PATH", "") + ":" + self.path_append + try: process = await asyncio.create_subprocess_shell( command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, cwd=cwd, + env=env, ) try: diff --git a/nanobot/config/schema.py b/nanobot/config/schema.py index fe8dd83..dd856fe 100644 --- a/nanobot/config/schema.py +++ b/nanobot/config/schema.py @@ -252,6 +252,7 @@ class ExecToolConfig(Base): """Shell exec tool configuration.""" timeout: int = 60 + path_append: str = "/usr/sbin:/usr/local/sbin" class MCPServerConfig(Base): From 7be278517e8706f61bc2bc3c17b2b01fc4fbff5b Mon Sep 17 00:00:00 2001 From: aiguozhi123456 Date: Tue, 24 Feb 2026 12:13:52 +0000 Subject: [PATCH 3/9] fix(exec): use empty default and os.pathsep for cross-platform --- nanobot/agent/tools/shell.py | 4 ++-- nanobot/config/schema.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/nanobot/agent/tools/shell.py b/nanobot/agent/tools/shell.py index c11fa2d..c3810b2 100644 --- a/nanobot/agent/tools/shell.py +++ b/nanobot/agent/tools/shell.py @@ -19,7 +19,7 @@ class ExecTool(Tool): deny_patterns: list[str] | None = None, allow_patterns: list[str] | None = None, restrict_to_workspace: bool = False, - path_append: str = "/usr/sbin:/usr/local/sbin", + path_append: str = "", ): self.timeout = timeout self.working_dir = working_dir @@ -71,7 +71,7 @@ class ExecTool(Tool): env = os.environ.copy() if self.path_append: - env["PATH"] = env.get("PATH", "") + ":" + self.path_append + env["PATH"] = env.get("PATH", "") + os.pathsep + self.path_append try: process = await asyncio.create_subprocess_shell( diff --git a/nanobot/config/schema.py b/nanobot/config/schema.py index dd856fe..4543ae0 100644 --- a/nanobot/config/schema.py +++ b/nanobot/config/schema.py @@ -252,7 +252,7 @@ class ExecToolConfig(Base): """Shell exec tool configuration.""" timeout: int = 60 - path_append: str = "/usr/sbin:/usr/local/sbin" + path_append: str = "" class MCPServerConfig(Base): From 07ae82583bae300593aa779e0c2a172e2a3c98b3 Mon Sep 17 00:00:00 2001 From: aiguozhi123456 Date: Tue, 24 Feb 2026 12:31:18 +0000 Subject: [PATCH 4/9] fix: pass path_append from config to ExecTool --- nanobot/agent/loop.py | 1 + nanobot/agent/subagent.py | 1 + 2 files changed, 2 insertions(+) diff --git a/nanobot/agent/loop.py b/nanobot/agent/loop.py index 8be8e51..c5e2a00 100644 --- a/nanobot/agent/loop.py +++ b/nanobot/agent/loop.py @@ -110,6 +110,7 @@ class AgentLoop: working_dir=str(self.workspace), timeout=self.exec_config.timeout, restrict_to_workspace=self.restrict_to_workspace, + path_append=self.exec_config.path_append, )) self.tools.register(WebSearchTool(api_key=self.brave_api_key)) self.tools.register(WebFetchTool()) diff --git a/nanobot/agent/subagent.py b/nanobot/agent/subagent.py index d87c61a..7269dee 100644 --- a/nanobot/agent/subagent.py +++ b/nanobot/agent/subagent.py @@ -111,6 +111,7 @@ class SubagentManager: working_dir=str(self.workspace), timeout=self.exec_config.timeout, restrict_to_workspace=self.restrict_to_workspace, + path_append=self.exec_config.path_append, )) tools.register(WebSearchTool(api_key=self.brave_api_key)) tools.register(WebFetchTool()) From 637c200dee803af55ae606ed41baf4433aa47b4a Mon Sep 17 00:00:00 2001 From: Re-bin Date: Tue, 24 Feb 2026 16:34:22 +0000 Subject: [PATCH 5/9] docs: update v0.1.4.post2 release news --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d2483e4..36c4ada 100644 --- a/README.md +++ b/README.md @@ -16,10 +16,13 @@ ⚡️ Delivers core agent functionality in just **~4,000** lines of code — **99% smaller** than Clawdbot's 430k+ lines. -📏 Real-time line count: **3,955 lines** (run `bash core_agent_lines.sh` to verify anytime) +📏 Real-time line count: **3,966 lines** (run `bash core_agent_lines.sh` to verify anytime) ## 📢 News +- **2026-02-24** 🚀 Released **v0.1.4.post2** — a reliability-focused release with a redesigned heartbeat, prompt cache optimization, and hardened provider & channel stability. See [release notes](https://github.com/HKUDS/nanobot/releases/tag/v0.1.4.post2) for details. +- **2026-02-23** 🔧 Heartbeat now uses virtual tool calls for silent idle. Prompt prefix stabilized for cache reuse. Slack mrkdwn fixes. +- **2026-02-22** 🛡️ Slack thread-isolated sessions, Discord typing loop fix, and agent reliability improvements across the board. - **2026-02-21** 🎉 Released **v0.1.4.post1** — new providers, media support across channels, and major stability improvements. See [release notes](https://github.com/HKUDS/nanobot/releases/tag/v0.1.4.post1) for details. - **2026-02-20** 🐦 Feishu now receives multimodal files from users. More reliable memory under the hood. - **2026-02-19** ✨ Slack now sends files, Discord splits long messages, and subagents work in CLI mode. From a3963bfba3cf5494437ced5ddb86f4062b707f5f Mon Sep 17 00:00:00 2001 From: Re-bin Date: Tue, 24 Feb 2026 16:35:50 +0000 Subject: [PATCH 6/9] docs: update v0.1.4.post2 release news --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 36c4ada..baf6b98 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,8 @@ ## 📢 News - **2026-02-24** 🚀 Released **v0.1.4.post2** — a reliability-focused release with a redesigned heartbeat, prompt cache optimization, and hardened provider & channel stability. See [release notes](https://github.com/HKUDS/nanobot/releases/tag/v0.1.4.post2) for details. -- **2026-02-23** 🔧 Heartbeat now uses virtual tool calls for silent idle. Prompt prefix stabilized for cache reuse. Slack mrkdwn fixes. -- **2026-02-22** 🛡️ Slack thread-isolated sessions, Discord typing loop fix, and agent reliability improvements across the board. +- **2026-02-23** 🔧 Virtual tool-call heartbeat, prompt cache optimization, Slack mrkdwn fixes. +- **2026-02-22** 🛡️ Slack thread isolation, Discord typing fix, agent reliability improvements. - **2026-02-21** 🎉 Released **v0.1.4.post1** — new providers, media support across channels, and major stability improvements. See [release notes](https://github.com/HKUDS/nanobot/releases/tag/v0.1.4.post1) for details. - **2026-02-20** 🐦 Feishu now receives multimodal files from users. More reliable memory under the hood. - **2026-02-19** ✨ Slack now sends files, Discord splits long messages, and subagents work in CLI mode. From e959b13926680b8dc63e3af7c62f05db3534dbe2 Mon Sep 17 00:00:00 2001 From: aiguozhi123456 Date: Wed, 25 Feb 2026 01:49:56 +0000 Subject: [PATCH 7/9] docs: add pathAppend option to exec config docs --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 148c8f4..c3904bd 100644 --- a/README.md +++ b/README.md @@ -804,6 +804,7 @@ MCP tools are automatically discovered and registered on startup. The LLM can us | Option | Default | Description | |--------|---------|-------------| | `tools.restrictToWorkspace` | `false` | When `true`, restricts **all** agent tools (shell, file read/write/edit, list) to the workspace directory. Prevents path traversal and out-of-scope access. | +| `tools.exec.pathAppend` | `""` | Additional paths to append to `PATH` when executing shell commands. Useful for commands in non-standard locations (e.g., `/usr/sbin` for `ufw`). Use `":"` as separator (e.g., `"/usr/sbin:/usr/local/sbin"`). | | `channels.*.allowFrom` | `[]` (allow all) | Whitelist of user IDs. Empty = allow everyone; non-empty = only listed users can interact. | From a50a2c68686128a2dd1f395514501a392988e7e4 Mon Sep 17 00:00:00 2001 From: aiguozhi123456 Date: Wed, 25 Feb 2026 01:53:04 +0000 Subject: [PATCH 8/9] fix(docs): clarify platform-specific path separator --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c3904bd..278b114 100644 --- a/README.md +++ b/README.md @@ -804,7 +804,7 @@ MCP tools are automatically discovered and registered on startup. The LLM can us | Option | Default | Description | |--------|---------|-------------| | `tools.restrictToWorkspace` | `false` | When `true`, restricts **all** agent tools (shell, file read/write/edit, list) to the workspace directory. Prevents path traversal and out-of-scope access. | -| `tools.exec.pathAppend` | `""` | Additional paths to append to `PATH` when executing shell commands. Useful for commands in non-standard locations (e.g., `/usr/sbin` for `ufw`). Use `":"` as separator (e.g., `"/usr/sbin:/usr/local/sbin"`). | +| `tools.exec.pathAppend` | `""` | Additional paths to append to `PATH` when executing shell commands. Useful for commands in non-standard locations (e.g., `/usr/sbin` for `ufw`). Use the platform-specific separator (`:` on Linux/macOS, `;` on Windows). | | `channels.*.allowFrom` | `[]` (allow all) | Whitelist of user IDs. Empty = allow everyone; non-empty = only listed users can interact. | From 9eca7f339e0bce588877c5fe788c5208c1795828 Mon Sep 17 00:00:00 2001 From: Re-bin Date: Wed, 25 Feb 2026 15:57:50 +0000 Subject: [PATCH 9/9] docs: shorten pathAppend description in config table --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index faa2f29..ad81dd6 100644 --- a/README.md +++ b/README.md @@ -807,7 +807,7 @@ MCP tools are automatically discovered and registered on startup. The LLM can us | Option | Default | Description | |--------|---------|-------------| | `tools.restrictToWorkspace` | `false` | When `true`, restricts **all** agent tools (shell, file read/write/edit, list) to the workspace directory. Prevents path traversal and out-of-scope access. | -| `tools.exec.pathAppend` | `""` | Additional paths to append to `PATH` when executing shell commands. Useful for commands in non-standard locations (e.g., `/usr/sbin` for `ufw`). Use the platform-specific separator (`:` on Linux/macOS, `;` on Windows). | +| `tools.exec.pathAppend` | `""` | Extra directories to append to `PATH` when running shell commands (e.g. `/usr/sbin` for `ufw`). | | `channels.*.allowFrom` | `[]` (allow all) | Whitelist of user IDs. Empty = allow everyone; non-empty = only listed users can interact. |