diff --git a/cron/scheduler.py b/cron/scheduler.py index 34bf76b345..e6db77c098 100644 --- a/cron/scheduler.py +++ b/cron/scheduler.py @@ -219,6 +219,21 @@ def _deliver_result(job: dict, content: str, adapters=None, loop=None) -> Option chat_id = target["chat_id"] thread_id = target.get("thread_id") + # Diagnostic: log thread_id for topic-aware delivery debugging + origin = job.get("origin") or {} + origin_thread = origin.get("thread_id") + if origin_thread and not thread_id: + logger.warning( + "Job '%s': origin has thread_id=%s but delivery target lost it " + "(deliver=%s, target=%s)", + job["id"], origin_thread, job.get("deliver", "local"), target, + ) + elif thread_id: + logger.debug( + "Job '%s': delivering to %s:%s thread_id=%s", + job["id"], platform_name, chat_id, thread_id, + ) + from tools.send_message_tool import _send_to_platform from gateway.config import load_gateway_config, Platform diff --git a/tools/cronjob_tools.py b/tools/cronjob_tools.py index 80c88e3534..d5c81ad7a8 100644 --- a/tools/cronjob_tools.py +++ b/tools/cronjob_tools.py @@ -6,12 +6,15 @@ Compatibility wrappers remain for direct Python callers and legacy tests. """ import json +import logging import os import re import sys from pathlib import Path from typing import Any, Dict, List, Optional +logger = logging.getLogger(__name__) + # Import from cron module (will be available when properly installed) sys.path.insert(0, str(Path(__file__).parent.parent)) @@ -68,11 +71,17 @@ def _origin_from_env() -> Optional[Dict[str, str]]: origin_platform = get_session_env("HERMES_SESSION_PLATFORM") origin_chat_id = get_session_env("HERMES_SESSION_CHAT_ID") if origin_platform and origin_chat_id: + thread_id = get_session_env("HERMES_SESSION_THREAD_ID") or None + if thread_id: + logger.debug( + "Cron origin captured thread_id=%s for %s:%s", + thread_id, origin_platform, origin_chat_id, + ) return { "platform": origin_platform, "chat_id": origin_chat_id, "chat_name": get_session_env("HERMES_SESSION_CHAT_NAME") or None, - "thread_id": get_session_env("HERMES_SESSION_THREAD_ID") or None, + "thread_id": thread_id, } return None @@ -456,7 +465,7 @@ Important safety rule: cron-run sessions should not recursively schedule more cr }, "deliver": { "type": "string", - "description": "Delivery target: origin, local, telegram, discord, slack, whatsapp, signal, weixin, matrix, mattermost, homeassistant, dingtalk, feishu, wecom, wecom_callback, email, sms, bluebubbles, or platform:chat_id or platform:chat_id:thread_id for Telegram topics. Examples: 'origin', 'local', 'telegram', 'telegram:-1001234567890:17585', 'discord:#engineering'" + "description": "Omit this parameter to auto-deliver back to the current chat and topic (recommended). Auto-detection preserves thread/topic context. Only set explicitly when the user asks to deliver somewhere OTHER than the current conversation. Values: 'origin' (same as omitting), 'local' (no delivery, save only), or platform:chat_id:thread_id for a specific destination. Examples: 'telegram:-1001234567890:17585', 'discord:#engineering'. WARNING: 'platform:chat_id' without :thread_id loses topic targeting." }, "skills": { "type": "array",