NousResearch/hermes-agent

Toggle to enable/disable automatic title generation

Cluster 27877 · agent. Data as of

1 open item. Fix merged in #113955 on 17 Sep 2026. Check it covers your case.

The short answer

1open issue
0open PRs
10items grouped
Known?
Yes. 10 related items grouped here, 3 issues already closed.
Fix event
A fix was merged to main in PR #113955. Check it covers your case. No release data is tracked.
Tracker
The maintainers' tracker marks this unresolved. That is separate from the fix event.
Still open
1 issue and 0 PRs.

These issues and pull requests concern adding and correctly honoring a configuration setting to enable or disable auxiliary title generation. Multiple bug reports indicate the enabled flag is ignored, with several fix attempts addressing this behavior.

Open issues1

  • Issue#29456[Feature]: Ability to toggle title generation

Open PRs0

No open pull requests.

Fixes on record

Merged to main. A stamp counts the issues a fix is linked to.

  • #66049 fix(title): honor auxiliary.title_generation.enabled to disable auto titles (salvage #37349)

    merged to main

    files: agent/title_generator.py, cli-config.yaml.example, hermes_cli/config.py, scripts/release.py, tests/agent/test_title_generator.py, tests/hermes_cli/test_aux_config.py, website/docs/user-guide/configuration.md

    Show the diff
    • agent/title_generator.py
      @@ -48,6 +48,23 @@ def _title_language() -> str:
               return ""
       
       
      +def _auto_title_enabled() -> bool:
      +    """Return whether automatic session title generation is enabled."""
      +    try:
      +        # Lazy imports, matching _title_language(): title_generator is imported
      +        # from agent code paths where a module-level hermes_cli import risks
      +        # circularity, and the read-only loader avoids config-migration writes.
      +        from hermes_cli.config import load_config_readonly
      +        from utils import is_truthy_value
      +
      +        config = load_config_readonly()
      +        title_config = (config.get("auxiliary") or {}).get("title_generation") or {}
      +        return is_truthy_value(title_config.get("enabled"), default=True)
      +    except Exception:
      +        logger.debug("Failed to read title_generation.enabled", exc_info=True)
      +        return True
      +
      +
       def generate_title(
           user_message: str,
           assistant_response: str,
      @@ -66,6 +83,10 @@ def generate_title(
           ``AIAgent._emit_auxiliary_failure`` so the user sees a warning instead
           of silently accumulating untitled sessions.
           """
      +    if not _auto_title_enabled():
      +        logger.debug("Auto-title skipped: auxiliary.title_generation.enabled=false")
      +        return None
      +
           # Truncate long messages to keep the request small
           user_snippet = user_message[:500] if user_message else ""
           assistant_snippet = assistant_response[:500] if assistant_response else ""
      @@ -254,6 +275,12 @@ def maybe_auto_title(
           if user_msg_count > 2:
               return
       
      +    # Config read comes after the cheap first-exchange guard so the file
      +    # isn't touched on every subsequent turn of a long session.
      +    if not _auto_title_enabled():
      +        logger.debug("Auto-title skipped: auxiliary.title_generation.enabled=false")
      +        return
      +
           thread = threading.Thread(
               target=auto_title_session,
               args=(session_db, session_id, user_message, assistant_response),
    • cli-config.yaml.example
      @@ -526,6 +526,14 @@ prompt_caching:
       #     model: ""
       #     timeout: 30
       #
      +#   # Automatic session title generation after the first exchange
      +#   title_generation:
      +#     enabled: true          # set false to disable auto-title generation
      +#     provider: "auto"
      +#     model: ""
      +#     timeout: 30
      +#     language: ""           # empty = match the user's language; or e.g. "English"
      +#
       #   # Session search — summarizes matching past sessions
       #   session_search:
       #     provider: "auto"
    • hermes_cli/config.py
      @@ -1643,6 +1643,7 @@ def _ensure_hermes_home_managed(home: Path):
                   "reasoning_effort": "",  # per-task thinking level: none|minimal|low|medium|high|xhigh|max|ultra (empty = provider default)
               },
               "title_generation": {
      +            "enabled": True,
                   "provider": "auto",
                   "model": "",
                   "base_url": "",
    • scripts/release.py
      @@ -363,6 +363,7 @@
           "[email protected]": "synapsesx",
           "[email protected]": "itsflownium",
           "[email protected]": "dirtyren",
      +    "[redacted:email]": "s96919",
           "[redacted:email]": "doxe0x",
           "[redacted:email]": "Code-suphub",
           "[email protected]": "Code-suphub",
    • tests/agent/test_title_generator.py
      @@ -210,6 +210,18 @@ def mock_call_llm(**kwargs):
               user_content = captured_kwargs["messages"][1]["content"]
               assert len(user_content) < 1100  # 500 + 500 + formatting
       
      +    def test_skips_when_title_generation_disabled(self):
      +        """auxiliary.title_generation.enabled=false disables automatic titles."""
      +        config = {"auxiliary": {"title_generation": {"enabled": False}}}
      +
      +        with (
      +            patch("hermes_cli.config.load_config_readonly", return_value=config),
      +            patch("agent.title_generator.call_llm") as mock_call_llm,
      +        ):
      +            assert generate_title("question", "answer") is None
      +
      +        mock_call_llm.assert_not_called()
      +
       
       class TestAutoTitleSession:
           """Tests for auto_title_session() — the sync worker function."""
      @@ -347,6 +359,23 @@ def test_fires_on_first_exchange(self):
                       title_callback=None,
                   )
       
      +    def test_skips_when_title_generation_disabled(self):
      +        """Disabled title generation should not even start the background worker."""
      +        db = MagicMock()
      +        history = [
      +            {"role": "user", "content": "hello"},
      +            {"role": "assistant", "content": "hi there"},
      +        ]
      +        config = {"auxiliary": {"title_generation": {"enabled": False}}}
      +
      +        with (
      +            patch("hermes_cli.config.load_config_readonly", return_value=config),
      +            patch("agent.title_generator.auto_title_session") as mock_auto,
      +        ):
      +            maybe_auto_title(db, "sess-1", "hello", "hi there", history)
      +
      +        mock_auto.assert_not_called()
      +
           def test_forwards_failure_callback_to_worker(self):
               """maybe_auto_title must forward failure_callback into the thread."""
               db = MagicMock()
    • tests/hermes_cli/test_aux_config.py
      @@ -36,6 +36,7 @@ def test_title_generation_present_in_default_config():
           """
           assert "title_generation" in DEFAULT_CONFIG["auxiliary"]
           tg = DEFAULT_CONFIG["auxiliary"]["title_generation"]
      +    assert tg["enabled"] is True
           assert tg["provider"] == "auto"
           assert tg["model"] == ""
           assert tg["timeout"] > 0
    • website/docs/user-guide/configuration.md
      @@ -958,6 +958,10 @@ $ hermes model
       
       Select a task, pick a provider (OAuth flows open a browser; API-key providers prompt), pick a model. The change persists to `auxiliary.<task>.*` in `config.yaml`. Same machinery as the main-model picker — no extra syntax to learn.
       
      +If you do not want Hermes to auto-generate titles after the first exchange, set
      +`auxiliary.title_generation.enabled: false`. Manual titles still work through
      +`/title` and `hermes sessions rename`.
      +
       ### Video Tutorial
       
       <div style={{position: 'relative', width: '100%', aspectRatio: '16 / 9', marginBottom: '1.5rem'}}>
      @@ -1070,6 +1074,7 @@ auxiliary:
         # Auto-generated session titles. Empty language follows the conversation;
         # set e.g. "English" or "Japanese" to pin titles to one language.
         title_generation:
      +    enabled: true              # set false to disable auto-title generation
           provider: "auto"
           model: ""
           base_url: ""
  • #113955 fix(aux): session titles keep the instant derived title while the model upgrade is switched off (#85194, salvage #85401)

    merged to main

    files: agent/title_generator.py, cli-config.yaml.example, contributors/emails/[redacted:email], hermes_cli/config_defaults.py, tests/agent/test_title_generator.py, website/docs/user-guide/configuration.md

4 PRs closed without merging

From the close comments: done elsewhere 3 · duplicate 1

  • #27977 feat(auxiliary): add enabled toggle for individual auxiliary tasks (#27877) · done elsewhere → e20c3c1, #113955
  • #41760 fix(auxiliary): respect auxiliary.title_generation.enabled config · done elsewhere → #66049, #37349
  • #41951 fix(title_gen): honor auxiliary.title_generation.enabled config setting (#41744) · duplicate → #41760, #41744, #37349
  • #42098 fix(agent): honor auxiliary.title_generation.enabled to disable auto titles · done elsewhere → #66049, #37349
Closed issues · 3
  • Closed#27877Feature: add `enabled` toggle for auxiliary tasks (title_generation, etc.)
  • Closed#41744auxiliary.title.enabled config is ignored — title generation runs regardless
  • Closed#54577bug(title_generation): `enabled: false` config is ignored, title generation always runs
How this cluster was grouped
  • Link evidence (partly structural links): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text.
  • Members outside that structural core may belong to a different underlying problem.
  • Pairwise link density 0.16: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. 1.00 means every pair is linked.

Pick up a Lab task

Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.

Open the Lab

At a glance

Subsystem
agent
Merged PRs
#66049, #113955
Closed issues
3
Attempts
4 PRs closed without merging
Fix link checked
17 Sep 2026

For your agent

The same facts, typed.