From 1f7a81e5eebafad5e21c5760a92880c3155bcefe Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 23 Feb 2026 10:15:12 +0000 Subject: [PATCH 1/2] feat(slack): isolate session context per thread Each Slack thread now gets its own conversation session instead of sharing one session per channel. DM sessions are unchanged. Added as a generic feature to also support if Feishu threads support is added in the future. --- nanobot/bus/events.py | 3 ++- nanobot/channels/base.py | 4 +++- nanobot/channels/slack.py | 6 +++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/nanobot/bus/events.py b/nanobot/bus/events.py index a149e20..a48660d 100644 --- a/nanobot/bus/events.py +++ b/nanobot/bus/events.py @@ -16,11 +16,12 @@ class InboundMessage: timestamp: datetime = field(default_factory=datetime.now) media: list[str] = field(default_factory=list) # Media URLs metadata: dict[str, Any] = field(default_factory=dict) # Channel-specific data + session_key_override: str | None = None # Optional override for thread-scoped sessions @property def session_key(self) -> str: """Unique key for session identification.""" - return f"{self.channel}:{self.chat_id}" + return self.session_key_override or f"{self.channel}:{self.chat_id}" @dataclass diff --git a/nanobot/channels/base.py b/nanobot/channels/base.py index 3a5a785..2201686 100644 --- a/nanobot/channels/base.py +++ b/nanobot/channels/base.py @@ -111,13 +111,15 @@ class BaseChannel(ABC): ) return + meta = metadata or {} msg = InboundMessage( channel=self.name, sender_id=str(sender_id), chat_id=str(chat_id), content=content, media=media or [], - metadata=metadata or {} + metadata=meta, + session_key_override=meta.get("session_key"), ) await self.bus.publish_inbound(msg) diff --git a/nanobot/channels/slack.py b/nanobot/channels/slack.py index b0f9bbb..2e91f7b 100644 --- a/nanobot/channels/slack.py +++ b/nanobot/channels/slack.py @@ -179,6 +179,9 @@ class SlackChannel(BaseChannel): except Exception as e: logger.debug("Slack reactions_add failed: {}", e) + # Thread-scoped session key for channel/group messages + session_key = f"slack:{chat_id}:{thread_ts}" if thread_ts and channel_type != "im" else None + try: await self._handle_message( sender_id=sender_id, @@ -189,7 +192,8 @@ class SlackChannel(BaseChannel): "event": event, "thread_ts": thread_ts, "channel_type": channel_type, - } + }, + "session_key": session_key, }, ) except Exception: From 2b983c708dc0f99ad8402b1eaafdf2b14894eeeb Mon Sep 17 00:00:00 2001 From: Re-bin Date: Mon, 23 Feb 2026 13:10:47 +0000 Subject: [PATCH 2/2] refactor: pass session_key as explicit param instead of via metadata --- nanobot/channels/base.py | 9 +++++---- nanobot/channels/slack.py | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/nanobot/channels/base.py b/nanobot/channels/base.py index 2201686..3010373 100644 --- a/nanobot/channels/base.py +++ b/nanobot/channels/base.py @@ -89,7 +89,8 @@ class BaseChannel(ABC): chat_id: str, content: str, media: list[str] | None = None, - metadata: dict[str, Any] | None = None + metadata: dict[str, Any] | None = None, + session_key: str | None = None, ) -> None: """ Handle an incoming message from the chat platform. @@ -102,6 +103,7 @@ class BaseChannel(ABC): content: Message text content. media: Optional list of media URLs. metadata: Optional channel-specific metadata. + session_key: Optional session key override (e.g. thread-scoped sessions). """ if not self.is_allowed(sender_id): logger.warning( @@ -111,15 +113,14 @@ class BaseChannel(ABC): ) return - meta = metadata or {} msg = InboundMessage( channel=self.name, sender_id=str(sender_id), chat_id=str(chat_id), content=content, media=media or [], - metadata=meta, - session_key_override=meta.get("session_key"), + metadata=metadata or {}, + session_key_override=session_key, ) await self.bus.publish_inbound(msg) diff --git a/nanobot/channels/slack.py b/nanobot/channels/slack.py index 2e91f7b..906593b 100644 --- a/nanobot/channels/slack.py +++ b/nanobot/channels/slack.py @@ -193,8 +193,8 @@ class SlackChannel(BaseChannel): "thread_ts": thread_ts, "channel_type": channel_type, }, - "session_key": session_key, }, + session_key=session_key, ) except Exception: logger.exception("Error handling Slack message from {}", sender_id)