diff --git a/cron/jobs.py b/cron/jobs.py index 8fb3f868a..4d34b1534 100644 --- a/cron/jobs.py +++ b/cron/jobs.py @@ -384,6 +384,7 @@ def create_job( provider: Optional[str] = None, base_url: Optional[str] = None, script: Optional[str] = None, + enabled_toolsets: Optional[List[str]] = None, ) -> Dict[str, Any]: """ Create a new cron job. @@ -403,6 +404,9 @@ def create_job( script: Optional path to a Python script whose stdout is injected into the prompt each run. The script runs before the agent turn, and its output is prepended as context. Useful for data collection / change detection. + enabled_toolsets: Optional list of toolset names to restrict the agent to. + When set, only tools from these toolsets are loaded, reducing + token overhead. When omitted, all default tools are loaded. Returns: The created job dict @@ -433,6 +437,8 @@ def create_job( normalized_base_url = normalized_base_url or None normalized_script = str(script).strip() if isinstance(script, str) else None normalized_script = normalized_script or None + normalized_toolsets = [str(t).strip() for t in enabled_toolsets if str(t).strip()] if enabled_toolsets else None + normalized_toolsets = normalized_toolsets or None label_source = (prompt or (normalized_skills[0] if normalized_skills else None)) or "cron job" job = { @@ -464,6 +470,7 @@ def create_job( # Delivery configuration "deliver": deliver, "origin": origin, # Tracks where job was created for "origin" delivery + "enabled_toolsets": normalized_toolsets, } jobs = load_jobs() diff --git a/tools/cronjob_tools.py b/tools/cronjob_tools.py index 8a685a8cc..b6aacf54e 100644 --- a/tools/cronjob_tools.py +++ b/tools/cronjob_tools.py @@ -215,6 +215,8 @@ def _format_job(job: Dict[str, Any]) -> Dict[str, Any]: } if job.get("script"): result["script"] = job["script"] + if job.get("enabled_toolsets"): + result["enabled_toolsets"] = job["enabled_toolsets"] return result @@ -234,6 +236,7 @@ def cronjob( base_url: Optional[str] = None, reason: Optional[str] = None, script: Optional[str] = None, + enabled_toolsets: Optional[List[str]] = None, task_id: str = None, ) -> str: """Unified cron job management tool.""" @@ -271,6 +274,7 @@ def cronjob( provider=_normalize_optional_job_value(provider), base_url=_normalize_optional_job_value(base_url, strip_trailing_slash=True), script=_normalize_optional_job_value(script), + enabled_toolsets=enabled_toolsets or None, ) return json.dumps( { @@ -360,6 +364,8 @@ def cronjob( if script_error: return tool_error(script_error, success=False) updates["script"] = _normalize_optional_job_value(script) if script else None + if enabled_toolsets is not None: + updates["enabled_toolsets"] = enabled_toolsets or None if repeat is not None: # Normalize: treat 0 or negative as None (infinite) normalized_repeat = None if repeat <= 0 else repeat @@ -459,6 +465,11 @@ Important safety rule: cron-run sessions should not recursively schedule more cr "type": "string", "description": f"Optional path to a Python script that runs before each cron job execution. Its stdout is injected into the prompt as context. Use for data collection and change detection. Relative paths resolve under {display_hermes_home()}/scripts/. On update, pass empty string to clear." }, + "enabled_toolsets": { + "type": "array", + "items": {"type": "string"}, + "description": "Optional list of toolset names to restrict the job's agent to (e.g. [\"web\", \"terminal\", \"file\", \"delegation\"]). When set, only tools from these toolsets are loaded, significantly reducing input token overhead. When omitted, all default tools are loaded. Infer from the job's prompt — e.g. use \"web\" if it calls web_search, \"terminal\" if it runs scripts, \"file\" if it reads files, \"delegation\" if it calls delegate_task. On update, pass an empty array to clear." + }, }, "required": ["action"] } @@ -503,6 +514,7 @@ registry.register( base_url=args.get("base_url"), reason=args.get("reason"), script=args.get("script"), + enabled_toolsets=args.get("enabled_toolsets"), task_id=kw.get("task_id"), ))(), check_fn=check_cronjob_requirements,