fix(message): limit session recording to proactive sends

Only mark message-tool deliveries for channel-session recording while cron jobs are running, avoiding duplicate session writes during normal user turns.

Made-with: Cursor
This commit is contained in:
Xubin Ren
2026-04-26 20:08:21 +08:00
committed by Xubin Ren
parent 799db33517
commit 6036355ac5
3 changed files with 65 additions and 7 deletions
+19 -3
View File
@@ -42,6 +42,10 @@ class MessageTool(Tool):
default=default_message_id,
)
self._sent_in_turn_var: ContextVar[bool] = ContextVar("message_sent_in_turn", default=False)
self._record_channel_delivery_var: ContextVar[bool] = ContextVar(
"message_record_channel_delivery",
default=False,
)
def set_context(self, channel: str, chat_id: str, message_id: str | None = None) -> None:
"""Set the current message context."""
@@ -57,6 +61,14 @@ class MessageTool(Tool):
"""Reset per-turn send tracking."""
self._sent_in_turn = False
def set_record_channel_delivery(self, active: bool):
"""Mark tool-sent messages as proactive channel deliveries."""
return self._record_channel_delivery_var.set(active)
def reset_record_channel_delivery(self, token) -> None:
"""Restore previous proactive delivery recording state."""
self._record_channel_delivery_var.reset(token)
@property
def _sent_in_turn(self) -> bool:
return self._sent_in_turn_var.get()
@@ -117,15 +129,19 @@ class MessageTool(Tool):
if not self._send_callback:
return "Error: Message sending not configured"
metadata = {
"message_id": message_id,
} if message_id else {}
if self._record_channel_delivery_var.get():
metadata["_record_channel_delivery"] = True
msg = OutboundMessage(
channel=channel,
chat_id=chat_id,
content=content,
media=media or [],
buttons=buttons or [],
metadata={
"message_id": message_id,
} if message_id else {},
metadata=metadata,
)
try:
+25 -4
View File
@@ -716,8 +716,20 @@ def _run_gateway(
else f"{channel}:{chat_id}"
)
async def _deliver_to_channel(msg: OutboundMessage, *, record: bool = True) -> None:
async def _deliver_to_channel(msg: OutboundMessage, *, record: bool = False) -> None:
"""Publish a user-visible message and mirror it into that channel's session."""
metadata = dict(msg.metadata or {})
record = record or bool(metadata.pop("_record_channel_delivery", False))
if metadata != (msg.metadata or {}):
msg = OutboundMessage(
channel=msg.channel,
chat_id=msg.chat_id,
content=msg.content,
reply_to=msg.reply_to,
media=msg.media,
metadata=metadata,
buttons=msg.buttons,
)
if (
record
and msg.channel != "cli"
@@ -762,6 +774,10 @@ def _run_gateway(
async def _silent(*_args, **_kwargs):
pass
message_record_token = None
if isinstance(message_tool, MessageTool):
message_record_token = message_tool.set_record_channel_delivery(True)
try:
resp = await agent.process_direct(
reminder_note,
@@ -773,10 +789,11 @@ def _run_gateway(
finally:
if isinstance(cron_tool, CronTool) and cron_token is not None:
cron_tool.reset_cron_context(cron_token)
if isinstance(message_tool, MessageTool) and message_record_token is not None:
message_tool.reset_record_channel_delivery(message_record_token)
response = resp.content if resp else ""
message_tool = agent.tools.get("message")
if job.payload.deliver and isinstance(message_tool, MessageTool) and message_tool._sent_in_turn:
return response
@@ -790,7 +807,8 @@ def _run_gateway(
channel=job.payload.channel or "cli",
chat_id=job.payload.to,
content=response,
)
),
record=True,
)
return response
@@ -853,7 +871,10 @@ def _run_gateway(
if channel == "cli":
return # No external channel available to deliver to
await _deliver_to_channel(OutboundMessage(channel=channel, chat_id=chat_id, content=response))
await _deliver_to_channel(
OutboundMessage(channel=channel, chat_id=chat_id, content=response),
record=True,
)
hb_cfg = config.gateway.heartbeat
heartbeat = HeartbeatService(
+21
View File
@@ -1,6 +1,7 @@
import pytest
from nanobot.agent.tools.message import MessageTool
from nanobot.bus.events import OutboundMessage
@pytest.mark.asyncio
@@ -29,3 +30,23 @@ async def test_message_tool_rejects_malformed_buttons(bad) -> None:
content="hi", channel="telegram", chat_id="1", buttons=bad,
)
assert result == "Error: buttons must be a list of list of strings"
@pytest.mark.asyncio
async def test_message_tool_marks_channel_delivery_only_when_enabled() -> None:
sent: list[OutboundMessage] = []
async def _send(msg: OutboundMessage) -> None:
sent.append(msg)
tool = MessageTool(send_callback=_send)
await tool.execute(content="normal", channel="telegram", chat_id="1")
token = tool.set_record_channel_delivery(True)
try:
await tool.execute(content="cron", channel="telegram", chat_id="1")
finally:
tool.reset_record_channel_delivery(token)
assert sent[0].metadata == {}
assert sent[1].metadata == {"_record_channel_delivery": True}