fix(cron): resolve provider with the job's effective model; default dashboard cron creates to the backend's own profile

Two follow-ups to the per-job model pin surface (#67472 / #49948 review):

- cron/scheduler.py: pass target_model=<effective job model> to
  resolve_runtime_provider() on the primary path, so providers with
  model-specific api_mode routing derive the mode from the model the job
  actually runs (per-job pin > env > config default) instead of the stale
  persisted default. The auth-fallback path already did this for its
  fb_model.

- hermes_cli/web_server.py: POST /api/cron/jobs (and its sync worker) no
  longer hardcodes profile="default" when the request carries no profile
  param. A pool backend scoped to a named profile now resolves its own
  profile via get_active_profile_name(), so pre-profileScoped desktop
  clients can't write a named profile's job into ~/.hermes. Unscoped /
  custom HERMES_HOME keeps the legacy default fallback.

Tests: target_model capture test on run_job; two profile-default tests on
the create endpoint.
This commit is contained in:
teknium1 2026-07-19 05:55:30 -07:00 committed by Teknium
parent 6e676c768c
commit 786df3ca6c
4 changed files with 117 additions and 3 deletions

View file

@ -11096,11 +11096,31 @@ def _cron_profile_dicts() -> List[Dict[str, Any]]:
return _fallback_profile_dicts(profiles_mod)
def _cron_default_profile() -> str:
"""Profile to target when a cron request carries no explicit ``profile``.
A desktop pool backend runs one process per profile (HERMES_HOME already
scoped), but these cron endpoints deliberately route storage through the
profiles tree via ``_cron_profile_home`` so a hardcoded ``"default"``
fallback would write a non-default profile's job into ``~/.hermes``.
Resolve the process's own profile instead. ``custom`` (an unrecognized
HERMES_HOME outside the profiles tree) has no profile-dir equivalent, so
it keeps the legacy ``default`` fallback.
"""
try:
from hermes_cli.profiles import get_active_profile_name
name = get_active_profile_name()
except Exception:
return "default"
return "default" if name in ("default", "custom") else name
def _cron_profile_home(profile: Optional[str]) -> Tuple[str, Path]:
"""Resolve a profile query value to (profile_name, HERMES_HOME)."""
from hermes_cli import profiles as profiles_mod
raw = (profile or "default").strip() or "default"
raw = (profile or _cron_default_profile()).strip() or "default"
try:
canon = profiles_mod.normalize_profile_name(raw)
profiles_mod.validate_profile_name(canon)
@ -11256,7 +11276,7 @@ async def list_cron_job_runs(job_id: str, profile: Optional[str] = None, limit:
return await _run_cron_dashboard_io(_list_cron_job_runs_sync, job_id, profile, limit)
def _create_cron_job_sync(body: CronJobCreate, profile: str = "default"):
def _create_cron_job_sync(body: CronJobCreate, profile: Optional[str] = None):
try:
profile_name, profile_home = _cron_profile_home(profile)
script = _normalize_dashboard_cron_script(body.script, profile_home)
@ -11295,7 +11315,7 @@ def _create_cron_job_sync(body: CronJobCreate, profile: str = "default"):
@app.post("/api/cron/jobs")
async def create_cron_job(body: CronJobCreate, profile: str = "default"):
async def create_cron_job(body: CronJobCreate, profile: Optional[str] = None):
return await _run_cron_dashboard_io(_create_cron_job_sync, body, profile)