Files
hermes-agent/hermes_cli/plugins_aux_tasks.py
Teknium 68420e1912 refactor: move aux-slot growth into topical siblings (code-health caps)
No behavior change. Keeps every touched file and function at or below
its value on main:

- agent/auxiliary_task_config.py: read-time `auxiliary.<task>`
  resolution (plugin defaults, inherit_from, whole-route pin); still
  re-exported as agent.auxiliary_client._get_auxiliary_task_config.
- hermes_cli/plugins_aux_tasks.py: register_auxiliary_task validation
  and entry shape.
- web/src/lib/api-aux.ts: /api/model/auxiliary wire types.
- Desktop: aux row list, label resolver, deep-link highlight, route
  summary, Follow button and the set-to-main route in
  settings/aux-task-rows.tsx; one assignAuxiliary callback replaces
  setAuxiliaryToMain. Built-in aux slot labels move from each locale
  into <locale>_aux_tasks.ts (types_aux_tasks.ts holds the shape).
2026-10-05 17:24:40 -07:00

55 lines
3.0 KiB
Python

"""Validation and entry shape for ``PluginContext.register_auxiliary_task``."""
from __future__ import annotations
import logging
from typing import Any, Dict, Optional
logger = logging.getLogger("hermes_cli.plugins")
def build_auxiliary_task_entry(
me: str, owner_id: str, key: str, *, display_name: str, description: str,
defaults: Optional[Dict[str, Any]], inherit_from: Optional[str], registered: Dict[str, Dict[str, Any]],
) -> Dict[str, Any]:
"""Registry entry for a plugin auxiliary task, or ``ValueError`` for a bad key.
``me`` is the manifest name (for messages); ``owner_id`` is the canonical id ``ctx.llm`` is
bound to, so agent/plugin_llm.py can match it. ``registered`` is the manager's current
``_aux_tasks`` (re-registration by the same owner is allowed).
"""
if not key or not isinstance(key, str):
raise ValueError(f"Plugin '{me}' tried to register auxiliary task with invalid key {key!r}")
if not all(c.isalnum() or c == "_" for c in key):
raise ValueError(f"Plugin '{me}' auxiliary task key {key!r} "
f"must contain only alphanumeric characters and underscores")
from hermes_cli.main_provider_setup import _AUX_TASKS as _BUILTIN_AUX_TASKS
builtin_aux_keys = {k for k, _name, _desc in _BUILTIN_AUX_TASKS}
if key in builtin_aux_keys:
raise ValueError(f"Plugin '{me}' cannot register auxiliary task {key!r} — that key is reserved "
f"for a built-in task. Pick a plugin-namespaced key (e.g. '{me}_{key}').")
existing = registered.get(key)
if existing is not None and existing.get("plugin") != owner_id:
raise ValueError(f"Plugin '{me}' cannot register auxiliary task {key!r} — already registered "
f"by plugin '{existing.get('plugin')}'")
# A bad base degrades to "no inheritance" rather than failing the whole plugin load; a
# self-reference would otherwise only surface at read time, as a cycle.
if inherit_from is not None and (
not isinstance(inherit_from, str) or inherit_from == key
or (inherit_from not in builtin_aux_keys and inherit_from not in registered)):
logger.warning("Plugin '%s' auxiliary task %r: ignoring inherit_from=%r — not a built-in "
"auxiliary task or one already registered by a plugin", me, key, inherit_from)
inherit_from = None
# Plugin owns the schema; routing fields are guaranteed present so consumers don't crash.
# With inheritance the base supplies the shape, so only the plugin's own overrides go here.
task_defaults = (dict(defaults or {}) if inherit_from else
{"provider": "auto", "model": "", "base_url": "", "api_key": "", "timeout": 60,
"extra_body": {}, **(defaults or {})})
entry = {
"key": key, "display_name": display_name, "description": description,
"defaults": task_defaults,
"inherit_from": inherit_from,
"plugin": owner_id, "plugin_key": owner_id,
}
return entry