From 8e00b3a69e8c2910aae0c1f9ea3bf2619ab01699 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Sat, 11 Apr 2026 23:20:39 -0700 Subject: [PATCH] fix(cron): steer model away from explicit deliver targets that lose topic context (#8187) Rewrite the cronjob tool's 'deliver' parameter description to strongly guide models toward omitting the parameter (which auto-detects origin including thread/topic). The previous description listed all platform names equally, inviting models to construct explicit targets like 'telegram:' which silently drops the thread_id. New description: - Leads with 'Omit this parameter' as the recommended path - Explicitly warns that platform:chat_id without :thread_id loses topics - Removes the long flat list of platform names that invited construction Also adds diagnostic logging at two key points: - _origin_from_env(): logs when thread_id is captured during job creation - _deliver_result(): warns when origin has thread_id but delivery target lost it; logs at debug when delivering to a specific thread Helps diagnose user-reported issue where cron responses from Telegram topics are delivered to the main chat instead of the originating topic. --- cron/scheduler.py | 15 +++++++++++++++ tools/cronjob_tools.py | 13 +++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/cron/scheduler.py b/cron/scheduler.py index 34bf76b34..e6db77c09 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 80c88e353..d5c81ad7a 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",