feat(curator): make skill consolidation opt-in (prune stays default-on) (#47840)
The curator now defaults to prune-only: the deterministic inactivity pass (mark stale / archive long-unused skills) still runs whenever the curator is enabled, but the opinionated LLM umbrella-building consolidation fork is OFF by default. - agent/curator.py: add DEFAULT_CONSOLIDATE=False + get_consolidate(); gate the forked aux-model review in run_curator_review behind it (new consolidate param, None=read config). When off, the LLM pass is skipped entirely (no aux-model cost); the run is still recorded and reported. - config.py: add curator.consolidate (default false); v29->v30 migration seeds the key for existing installs without clobbering a user-set value. - hermes_cli/curator.py: 'hermes curator run --consolidate' override; status shows consolidate state; prune-only notice on run. - docs + tests.
This commit is contained in:
parent
e48803daec
commit
7bbffceb9c
5 changed files with 217 additions and 11 deletions
|
|
@ -1895,6 +1895,14 @@ DEFAULT_CONFIG = {
|
|||
# Archive a skill (move to skills/.archive/) after this many days
|
||||
# without use. Archived skills are recoverable — no auto-deletion.
|
||||
"archive_after_days": 90,
|
||||
# Run the LLM consolidation (umbrella-building) pass. OFF by default.
|
||||
# When off, a curator run does ONLY the deterministic inactivity prune
|
||||
# (mark stale / archive long-unused skills) and skips the forked
|
||||
# aux-model review entirely — no umbrella-building, no aux-model cost.
|
||||
# Set to true to opt back into merging overlapping skills into
|
||||
# class-level umbrellas. `hermes curator run --consolidate` overrides
|
||||
# this for a single invocation.
|
||||
"consolidate": False,
|
||||
# Also prune (archive) bundled built-in skills after the inactivity
|
||||
# period, not just agent-created ones. ON by default. Built-ins are
|
||||
# normally restored on every `hermes update`, so pruning them only
|
||||
|
|
@ -2569,7 +2577,7 @@ DEFAULT_CONFIG = {
|
|||
|
||||
|
||||
# Config schema version - bump this when adding new required fields
|
||||
"_config_version": 29,
|
||||
"_config_version": 30,
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
|
|
@ -4858,6 +4866,29 @@ def migrate_config(interactive: bool = True, quiet: bool = False) -> Dict[str, A
|
|||
if not quiet:
|
||||
print(" ✓ Renamed write_mode → write_approval (boolean gate)")
|
||||
|
||||
# ── Version 29 → 30: seed curator.consolidate (default false) ──
|
||||
# Consolidation (the LLM umbrella-building fork) is now an opt-in toggle,
|
||||
# OFF by default. The deterministic inactivity prune still runs whenever
|
||||
# the curator is enabled; only the opinionated, aux-model-cost LLM pass is
|
||||
# gated. The runtime deep-merge already supplies the default, but we seed
|
||||
# the key so it's visible/editable in config.yaml. Existing installs that
|
||||
# WANT the old always-consolidate behavior must set it to true explicitly.
|
||||
# Only add the key when a curator section exists and lacks it — never
|
||||
# clobber a value the user already set.
|
||||
if current_ver < 30:
|
||||
config = read_raw_config()
|
||||
raw_curator = config.get("curator")
|
||||
if isinstance(raw_curator, dict) and "consolidate" not in raw_curator:
|
||||
raw_curator["consolidate"] = False
|
||||
config["curator"] = raw_curator
|
||||
save_config(config)
|
||||
results["config_added"].append("curator.consolidate=false")
|
||||
if not quiet:
|
||||
print(
|
||||
" ✓ Seeded curator.consolidate: false "
|
||||
"(LLM consolidation is now opt-in; pruning stays on)"
|
||||
)
|
||||
|
||||
# ── Post-migration: disable exfiltration-shaped MCP stdio entries ──
|
||||
# Users can hand-edit mcp_servers, and older installs may already contain a
|
||||
# malicious entry. Preserve the stanza for auditability but mark it
|
||||
|
|
|
|||
|
|
@ -77,6 +77,10 @@ def _cmd_status(args) -> int:
|
|||
print(f" interval: every {_interval_label}")
|
||||
print(f" stale after: {curator.get_stale_after_days()}d unused")
|
||||
print(f" archive after: {curator.get_archive_after_days()}d unused")
|
||||
print(
|
||||
f" consolidate: {'on' if curator.get_consolidate() else 'off'}"
|
||||
f"{'' if curator.get_consolidate() else ' (prune-only; LLM merge pass opt-in)'}"
|
||||
)
|
||||
|
||||
rows = skill_usage.agent_created_report()
|
||||
if not rows:
|
||||
|
|
@ -174,10 +178,20 @@ def _cmd_run(args) -> int:
|
|||
dry = bool(getattr(args, "dry_run", False))
|
||||
background = bool(getattr(args, "background", False))
|
||||
synchronous = bool(getattr(args, "synchronous", False)) or not background
|
||||
# --consolidate forces the LLM umbrella-building pass on for this run,
|
||||
# overriding the config default (off). When the flag is absent, pass None
|
||||
# so run_curator_review reads curator.consolidate from config.
|
||||
consolidate = True if bool(getattr(args, "consolidate", False)) else None
|
||||
if dry:
|
||||
print("curator: running DRY-RUN (report only, no mutations)...")
|
||||
else:
|
||||
print("curator: running review pass...")
|
||||
if consolidate is None and not curator.get_consolidate():
|
||||
print(
|
||||
"curator: consolidation is off — running prune-only "
|
||||
"(deterministic stale/archive). Pass --consolidate or set "
|
||||
"`curator.consolidate: true` to enable the LLM merge pass."
|
||||
)
|
||||
|
||||
def _on_summary(msg: str) -> None:
|
||||
print(msg)
|
||||
|
|
@ -186,6 +200,7 @@ def _cmd_run(args) -> int:
|
|||
on_summary=_on_summary,
|
||||
synchronous=synchronous,
|
||||
dry_run=dry,
|
||||
consolidate=consolidate,
|
||||
)
|
||||
auto = result.get("auto_transitions", {})
|
||||
if auto:
|
||||
|
|
@ -503,6 +518,12 @@ def register_cli(parent: argparse.ArgumentParser) -> None:
|
|||
help="Report only — no state changes, no archives, no consolidation "
|
||||
"(use this to preview what curator would do)",
|
||||
)
|
||||
p_run.add_argument(
|
||||
"--consolidate", dest="consolidate", action="store_true",
|
||||
help="Force the LLM umbrella-building consolidation pass on for this "
|
||||
"run, overriding the config default (off). Without this flag the "
|
||||
"run is prune-only unless `curator.consolidate: true` is set.",
|
||||
)
|
||||
p_run.set_defaults(func=_cmd_run)
|
||||
|
||||
p_pause = subs.add_parser("pause", help="Pause the curator until resumed")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue