Skip to content

fix(cron): record no_agent runs as sessions, keep them out of the sidebar - #62247

Open
fangl12 wants to merge 1 commit into
NousResearch:mainfrom
fangl12:fix/cron-run-history-and-sidebar-leak
Open

fix(cron): record no_agent runs as sessions, keep them out of the sidebar#62247
fangl12 wants to merge 1 commit into
NousResearch:mainfrom
fangl12:fix/cron-run-history-and-sidebar-leak

Conversation

@fangl12

@fangl12 fangl12 commented Jul 10, 2026

Copy link
Copy Markdown

Summary

no_agent: true (script-mode) cron jobs short-circuit run_job() before any SessionDB work, so they never produce the cron_{job_id}_{timestamp} session row that the run-history endpoint (GET /api/cron/jobs/{id}/runs) is built from. Manually triggering such a job gives zero feedback in the Desktop GUI: no running indicator, no run record, no output.

This overlaps with #44080 / #42433 / #53692 — same root cause, but this PR also fixes a second bug that those left open: recording the run session introduces a sidebar leak.

Fix 1 — cron/scheduler.py

In the no_agent branch of run_job(): create the run session (cron_{job_id}_{ts}, source='cron', matching the agent path's shape) before executing the script, so the runs endpoint's is_active check reflects an in-flight run. After execution, persist the outcome doc as an assistant message, title the session, and end_session. Covers all four exit paths: success, script failure, empty-stdout silent run, and wakeAgent=false silent run.

  • Best-effort recording — a broken state store degrades to the old no-record behaviour and never blocks the script run.
  • The no_agent cost contract is preserved: run_agent/AIAgent are still never imported on this path (test_run_job_no_agent_never_invokes_aiagent still passes).

Fix 2 — apps/desktop/src/app/session/hooks/use-session-list-actions.ts

Recording the run session (Fix 1) surfaces a second, previously-latent bug: opening a cron run from the Cron panel's run-history list navigates to /session/<cron_id>, which sets $selectedStoredSessionId. The desktop sidebar's sessionsToKeep() helper unconditionally re-adds the "active" session id on every refresh (to survive an in-flight-first-turn race) — with no source check. So a cron session (source='cron', normally excluded from the recents list via SIDEBAR_EXCLUDED_SOURCES) gets permanently pinned into the main sidebar the moment its run history is viewed once, and stays there across refreshes indefinitely.

Fix: sessionsToKeep() takes an optional excludedSources list; the active row is only kept if its source isn't in that list. The two recents-list call sites (refreshSessions, loadMoreSessionsForProfile) pass SIDEBAR_EXCLUDED_SOURCES so a viewed cron/subagent/tool/messaging session can no longer leak into the main chat list.

Testing

tests/cron/test_cron_no_agent.py: 22 passed (4 new — success/failure/silent run-session
recording + broken-store fallback)
apps/desktop: tsc -p . --noEmit clean; eslint clean on the changed file

Related issues

Fixes #44080. Related: #42433, #53692 (same root cause, this PR additionally fixes the
sidebar-leak regression those introduce once the run session exists).

…ebar

no_agent (script-mode) cron jobs short-circuit run_job() before any
SessionDB work, so they never produce the cron_{job_id}_{ts} session row
that the run-history endpoint (GET /api/cron/jobs/{id}/runs) is built
from. Manually triggering such a job gives zero feedback in the Desktop
GUI: no running indicator, no run record, no output.

Fix (cron/scheduler.py): in the no_agent branch of run_job(), create the
run session before executing the script (source='cron', matching the
agent path's shape) so the runs endpoint's is_active check reflects an
in-flight run. After execution, persist the outcome doc as an assistant
message, title the session, and end_session. Covers all four exit paths:
success, script failure, empty-stdout silent run, and wakeAgent=false
silent run. Recording is best-effort — a broken state store degrades to
the old no-record behaviour and never blocks the script run. The
no_agent cost contract is preserved: run_agent/AIAgent are still never
imported on this path.

This alone introduces a second bug: opening a cron run from the Cron
panel's run-history list navigates to /session/<cron_id>, which sets
. The desktop sidebar's sessionsToKeep() helper
unconditionally re-adds the 'active' session id on every refresh to
survive an in-flight-first-turn race — with no source check, so a cron
session (source='cron', normally excluded from the recents list) gets
permanently pinned into the main sidebar the moment its run history is
viewed once.

Fix (apps/desktop): sessionsToKeep() takes an optional excludedSources
list; the active row is only kept if its source isn't in that list. The
two recents-list call sites (refreshSessions, loadMoreSessionsForProfile)
pass SIDEBAR_EXCLUDED_SOURCES so a viewed cron/subagent/tool/messaging
session can no longer leak into the main chat list.

Tests: tests/cron/test_cron_no_agent.py — 22 passed (4 new, covering
success/failure/silent run-session recording + broken-store fallback).
apps/desktop: tsc --noEmit and eslint clean on the changed file.
@alt-glitch alt-glitch added type/bug Something isn't working comp/cron Cron scheduler and job management comp/desktop Electron desktop app (apps/desktop/*) sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state P3 Low — cosmetic, nice to have labels Jul 10, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for tracing both the missing no-agent run row and the sidebar interaction. The premise holds on current main: cron/scheduler.py:2523-2606 returns from the no-agent path before the SessionDB initialization at cron/scheduler.py:2616-2623, while hermes_cli/web_server.py:10216-10224 builds run history from cron session rows.

Problems

  • apps/desktop/src/app/session/hooks/use-session-list-actions.ts includes the unresolved source comment #<issue-number-tbd>. Please replace it with the relevant issue reference or remove it.

Suggested changes

  • Add a desktop regression test for the sessionsToKeep exclusion: a selected cron row must not survive a recents refresh, while a selected ordinary chat still does.

Automated hermes-sweeper review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/sessions Session lifecycle, resume, persistence, history comp/cron Cron scheduler and job management comp/desktop Electron desktop app (apps/desktop/*) P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Desktop GUI shows no execution status/output after manually triggering no_agent cron jobs

3 participants