From bd8d3ad5b6db273e582fb0864927716f5f8a20e2 Mon Sep 17 00:00:00 2001 From: chengyongru Date: Fri, 7 Aug 2026 17:07:41 +0800 Subject: [PATCH] fix(channels): preserve global progress defaults --- nanobot/channels/base.py | 8 +++++ nanobot/channels/manager.py | 12 ++----- nanobot/channels/weixin/runtime.py | 3 ++ .../test_channel_manager_delta_coalescing.py | 33 +++++++++++++++++++ 4 files changed, 47 insertions(+), 9 deletions(-) diff --git a/nanobot/channels/base.py b/nanobot/channels/base.py index 9d52f00f7..1fdaf6f4f 100644 --- a/nanobot/channels/base.py +++ b/nanobot/channels/base.py @@ -101,6 +101,14 @@ class BaseChannel(ABC): """ pass + def progress_transport_defaults(self) -> tuple[bool, bool] | None: + """Return channel-owned defaults for progress and tool-hint messages. + + ``None`` keeps the global channel policy. Channels should override this + only when their transport requires different defaults. + """ + return None + def should_retry_send_error(self, error: Exception) -> bool: """Return whether the channel manager may retry a failed delivery. diff --git a/nanobot/channels/manager.py b/nanobot/channels/manager.py index e0c4f3866..06e3bd3d8 100644 --- a/nanobot/channels/manager.py +++ b/nanobot/channels/manager.py @@ -187,15 +187,9 @@ class ChannelManager: channel = cls(section, self.bus, **kwargs) if runtime_name and runtime_name != channel.name: channel.name = runtime_name - # Channel-owned config models may deliberately choose safer transport - # defaults than the global channel policy (for example, a quota-limited - # platform can disable progress messages). Preserve those defaults - # while still letting an explicit per-channel value win below. - progress_default = getattr( - channel.config, "send_progress", self.config.channels.send_progress, - ) - tool_hints_default = getattr( - channel.config, "send_tool_hints", self.config.channels.send_tool_hints, + progress_default, tool_hints_default = channel.progress_transport_defaults() or ( + self.config.channels.send_progress, + self.config.channels.send_tool_hints, ) channel.send_progress = self._resolve_bool_override( section, "send_progress", progress_default, diff --git a/nanobot/channels/weixin/runtime.py b/nanobot/channels/weixin/runtime.py index 0cf0a234c..d2b24fa7a 100644 --- a/nanobot/channels/weixin/runtime.py +++ b/nanobot/channels/weixin/runtime.py @@ -339,6 +339,9 @@ class WeixinChannel(BaseChannel): self._reply_run_ids: dict[str, str] = {} self._reply_progress_counts: dict[str, int] = {} + def progress_transport_defaults(self) -> tuple[bool, bool]: + return self.config.send_progress, self.config.send_tool_hints + def should_retry_send_error(self, error: Exception) -> bool: if isinstance(error, WeixinAPIError): return error.retryable diff --git a/tests/channels/test_channel_manager_delta_coalescing.py b/tests/channels/test_channel_manager_delta_coalescing.py index 8a009d848..91b6a4a98 100644 --- a/tests/channels/test_channel_manager_delta_coalescing.py +++ b/tests/channels/test_channel_manager_delta_coalescing.py @@ -17,6 +17,7 @@ from nanobot.bus.outbound_events import ( from nanobot.bus.queue import MessageBus from nanobot.channels.base import BaseChannel from nanobot.channels.manager import ChannelManager +from nanobot.channels.mattermost.runtime import MattermostChannel from nanobot.config.schema import Config @@ -311,6 +312,38 @@ class TestProgressFiltering: assert manager._should_send_progress("mock", tool_hint=False) is False assert manager._should_send_progress("mock", tool_hint=True) is False + def test_channel_config_defaults_do_not_override_global_policy(self, bus): + manager = ChannelManager.__new__(ChannelManager) + manager.config = Config.model_validate({ + "channels": { + "sendProgress": False, + "sendToolHints": False, + }, + }) + manager.bus = bus + + channel = manager._build_channel( + "mattermost", + MattermostChannel, + {"enabled": True}, + ) + + assert channel.send_progress is False + assert channel.send_tool_hints is False + + opted_in = manager._build_channel( + "mattermost", + MattermostChannel, + { + "enabled": True, + "sendProgress": True, + "sendToolHints": True, + }, + ) + + assert opted_in.send_progress is True + assert opted_in.send_tool_hints is True + def test_progress_visibility_returns_false_for_missing_channel(self, manager): assert manager._should_send_progress("nonexistent", tool_hint=False) is False assert manager._should_send_progress("nonexistent", tool_hint=True) is False