Skip to content

fix(cron): do not seed HERMES_SESSION_* contextvars from cron origin (salvage of #22356)#22382

Merged
kshitijk4poor merged 2 commits into
NousResearch:mainfrom
kshitijk4poor:salvage/cron-origin-22356
May 9, 2026
Merged

fix(cron): do not seed HERMES_SESSION_* contextvars from cron origin (salvage of #22356)#22382
kshitijk4poor merged 2 commits into
NousResearch:mainfrom
kshitijk4poor:salvage/cron-origin-22356

Conversation

@kshitijk4poor

Copy link
Copy Markdown
Collaborator

Salvage of #22356 by @oferlaor (Closes #22351). Cherry-picked onto current main with original authorship preserved (origin PR is 366 commits behind main).

What changed

cron/scheduler.py was seeding HERMES_SESSION_PLATFORM, HERMES_SESSION_CHAT_ID, and HERMES_SESSION_CHAT_NAME from the cron job's stored origin immediately before constructing AIAgent. Those contextvars are designed to identify a live inbound gateway sender, not delivery routing metadata. This change replaces the seed with empty values:

_ctx_tokens = set_session_vars(
    platform="",
    chat_id="",
    chat_name="",
)

Cron output delivery is unaffected — it reads job["origin"] directly via _resolve_origin(job) and the HERMES_CRON_AUTO_DELIVER_* contextvars, both of which are still set in the same block.

Why this matters (tightened framing vs the original PR)

The original PR/issue framed this as access-control filtering stripping toolsets (web_search, terminal, read_file) from authorized cron jobs. We were not able to reproduce that on current main — there is no per-identity toolset filter between the contextvar-set and AIAgent construction; enabled_toolsets are passed straight through. We've left the structural fix in place though, because several tool consumers branch on HERMES_SESSION_* during job execution and were observably misbehaving as a result. The expanded comment in the diff lists them; the concrete ones:

  • tools/terminal_tool.py (background-process notification routing) — when a cron job ran terminal(background=True, notify_on_complete=True), watcher_platform / watcher_chat_id / watcher_user_id etc. were populated from the seeded WhatsApp/Telegram origin instead of from the cron's own HERMES_CRON_AUTO_DELIVER_* machinery, so completion notifications routed to the gateway origin chat. This is the most user-visible bug the change fixes.
  • tools/tts_tool.pyplatform == "telegram" decides Opus vs MP3. A cron with a non-telegram origin would silently pick the wrong format if the agent called TTS.
  • tools/skills_tool.py + agent/prompt_builder.py — per-platform skill-disable lists and the system-prompt cache key both consume HERMES_SESSION_PLATFORM. Cron was slotting into the origin platform's cache instead of cron's own.
  • tools/send_message_tool.py — mirror source labelling and the _check_send_message gate read HERMES_SESSION_PLATFORM.
  • tools/cronjob_tools.py::_origin_from_env — falls back to the seeded vars; mostly defensive since the cronjob toolset is in disabled_toolsets for cron, but still wrong shape.

Verification

E2E test against a fresh main worktree, simulating a cron job created from a WhatsApp group origin:

Probe at AIAgent construction time Before fix After fix
HERMES_SESSION_PLATFORM "whatsapp" ""
HERMES_SESSION_CHAT_ID "123-456-789@g.us" ""
HERMES_SESSION_CHAT_NAME "Test Group" ""
HERMES_CRON_AUTO_DELIVER_PLATFORM "whatsapp" "whatsapp" ✅ (delivery routing preserved)
HERMES_CRON_AUTO_DELIVER_CHAT_ID "123-456-789@g.us" "123-456-789@g.us"
tools/cronjob_tools._origin_from_env() {platform: whatsapp, chat_id: ...} None
tools/tts_tool platform == "telegram" check False (correct here, depends on origin) False
enabled_toolsets passed to AIAgent ["web", "file", "terminal"] ["web", "file", "terminal"] (unchanged — confirms no filtering happens regardless)
kwargs["platform"] to AIAgent "cron" "cron"

Tests run from the worktree:

$ bash scripts/run_tests.sh tests/cron/ tests/tools/test_cronjob_tools.py
362 passed in 10.92s

$ bash scripts/run_tests.sh tests/tools/test_send_message_tool.py tests/tools/test_cron_approval_mode.py \
                            tests/agent/test_skill_commands.py tests/gateway/test_session_env.py tests/cron/
504 passed in 13.03s

No regressions in any consumer test path. ruff check clean.

Files

  • cron/scheduler.py — the patch (kept @oferlaor's original edit; expanded the comment to document the real consumer list rather than "access-control filtering" so future readers don't go looking for a per-identity toolset filter that doesn't exist).
  • scripts/release.py — added agentsmithlaor@gmail.com → oferlaor to AUTHOR_MAP for the contributor audit.

Original PR

Closes #22351. Original PR (#22356, 366 commits behind main) being closed with credit pointing here.

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/cron Cron scheduler and job management labels May 9, 2026
@kshitijk4poor kshitijk4poor merged commit f2afa68 into NousResearch:main May 9, 2026
9 of 11 checks passed
@kshitijk4poor kshitijk4poor deleted the salvage/cron-origin-22356 branch May 9, 2026 07:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cron Cron scheduler and job management P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cron delivery origin should not seed live gateway sender identity for tool access-control

2 participants