mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-21 16:18:55 +00:00
fix(config): shipped template no longer enables session auto-reset (#67772)
#60194 flipped SessionResetPolicy's default to mode: none, but cli-config.yaml.example still shipped session_reset.mode: both. Every install path (install.sh, install.ps1, docker stage2-hook, hermes doctor) copies the template verbatim to ~/.hermes/config.yaml, so fresh installs got an EXPLICIT mode: both that overrides the code default — users hit 24h-idle resets with 'nothing' in their config enabling it. - cli-config.yaml.example: session_reset.mode both -> none, comments rewritten to describe auto-reset as opt-in - docs/session-lifecycle.md: appendix example updated to match - tests/gateway/test_config.py: invariant tests — template seed, absent config, and mode-less session_reset block all resolve to mode none; explicit opt-in still honored
This commit is contained in:
parent
1157c636c5
commit
0d7fad7b88
3 changed files with 84 additions and 18 deletions
|
|
@ -583,28 +583,27 @@ memory:
|
|||
# Session Reset Policy (Messaging Platforms)
|
||||
# =============================================================================
|
||||
# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are
|
||||
# automatically cleared. Without resets, conversation context grows indefinitely
|
||||
# which increases API costs with every message.
|
||||
# automatically cleared. Default is "none": sessions never auto-reset —
|
||||
# conversation context lives until you /reset or /new manually, or context
|
||||
# compression kicks in. Opt in to automatic resets if you prefer sessions to
|
||||
# clear on a schedule (long-lived context increases API cost per message,
|
||||
# though prompt caching and compression keep this manageable).
|
||||
#
|
||||
# When a reset triggers, the agent first saves important information to its
|
||||
# persistent memory — but the conversation context is wiped. The agent starts
|
||||
# fresh but retains learned facts via its memory system.
|
||||
#
|
||||
# Users can always manually reset with /reset or /new in chat.
|
||||
# When an automatic reset triggers, the agent first saves important
|
||||
# information to its persistent memory — but the conversation context is
|
||||
# wiped. The agent starts fresh but retains learned facts via its memory
|
||||
# system.
|
||||
#
|
||||
# Modes:
|
||||
# "both" - Reset on EITHER inactivity timeout or daily boundary (recommended)
|
||||
# "idle" - Reset only after N minutes of inactivity
|
||||
# "daily" - Reset only at a fixed hour each day
|
||||
# "none" - Never auto-reset; context lives until /reset or compression kicks in
|
||||
#
|
||||
# When a reset triggers, the agent gets one turn to save important memories and
|
||||
# skills before the context is wiped. Persistent memory carries across sessions.
|
||||
# "none" - Never auto-reset (default); context lives until /reset or compression
|
||||
# "idle" - Reset after N minutes of inactivity
|
||||
# "daily" - Reset at a fixed hour each day
|
||||
# "both" - Reset on EITHER inactivity timeout or daily boundary
|
||||
#
|
||||
session_reset:
|
||||
mode: both # "both", "idle", "daily", or "none"
|
||||
idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)
|
||||
at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)
|
||||
mode: none # "none", "idle", "daily", or "both"
|
||||
idle_minutes: 1440 # Inactivity timeout in minutes (used by "idle"/"both")
|
||||
at_hour: 4 # Daily reset hour, 0-23 local time (used by "daily"/"both")
|
||||
|
||||
# Maximum number of simultaneously active chat sessions across CLI, TUI,
|
||||
# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow
|
||||
|
|
|
|||
|
|
@ -628,7 +628,7 @@ When a session expires:
|
|||
|
||||
```yaml
|
||||
session_reset:
|
||||
mode: both # none | idle | daily | both
|
||||
mode: none # none (default) | idle | daily | both
|
||||
at_hour: 4 # daily reset hour (local time)
|
||||
idle_minutes: 1440 # idle timeout (24h)
|
||||
notify: true # notify user on auto-reset
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
|
@ -486,6 +487,72 @@ class TestGatewayConfigRoundtrip:
|
|||
|
||||
|
||||
class TestLoadGatewayConfig:
|
||||
def test_shipped_template_does_not_enable_auto_reset(self, tmp_path, monkeypatch):
|
||||
"""A fresh install seeded from cli-config.yaml.example must not
|
||||
auto-reset sessions.
|
||||
|
||||
Installers (scripts/install.sh, scripts/install.ps1,
|
||||
docker/stage2-hook.sh, hermes doctor) copy the template verbatim to
|
||||
~/.hermes/config.yaml, so whatever ``session_reset.mode`` the template
|
||||
ships becomes an EXPLICIT user setting that overrides the code
|
||||
default. After #60194 flipped the default to "none", the template
|
||||
still said "both" — every new install kept 24h-idle resets on
|
||||
(Luciano's report, July 2026). This pins the invariant: template
|
||||
seed == no auto-reset.
|
||||
"""
|
||||
template = (
|
||||
Path(__file__).resolve().parents[2] / "cli-config.yaml.example"
|
||||
)
|
||||
hermes_home = tmp_path / ".hermes"
|
||||
hermes_home.mkdir()
|
||||
(hermes_home / "config.yaml").write_text(
|
||||
template.read_text(encoding="utf-8"), encoding="utf-8"
|
||||
)
|
||||
monkeypatch.setenv("HERMES_HOME", str(hermes_home))
|
||||
|
||||
config = load_gateway_config()
|
||||
|
||||
assert config.default_reset_policy.mode == "none"
|
||||
|
||||
def test_no_config_yaml_means_no_auto_reset(self, tmp_path, monkeypatch):
|
||||
"""With no config.yaml at all, sessions must never auto-reset."""
|
||||
hermes_home = tmp_path / ".hermes"
|
||||
hermes_home.mkdir()
|
||||
monkeypatch.setenv("HERMES_HOME", str(hermes_home))
|
||||
|
||||
config = load_gateway_config()
|
||||
|
||||
assert config.default_reset_policy.mode == "none"
|
||||
|
||||
def test_session_reset_without_mode_means_no_auto_reset(self, tmp_path, monkeypatch):
|
||||
"""A session_reset block that tunes knobs but omits mode stays off."""
|
||||
hermes_home = tmp_path / ".hermes"
|
||||
hermes_home.mkdir()
|
||||
(hermes_home / "config.yaml").write_text(
|
||||
"session_reset:\n idle_minutes: 60\n", encoding="utf-8"
|
||||
)
|
||||
monkeypatch.setenv("HERMES_HOME", str(hermes_home))
|
||||
|
||||
config = load_gateway_config()
|
||||
|
||||
assert config.default_reset_policy.mode == "none"
|
||||
assert config.default_reset_policy.idle_minutes == 60
|
||||
|
||||
def test_explicit_session_reset_opt_in_is_honored(self, tmp_path, monkeypatch):
|
||||
"""Users who explicitly opt in to auto-reset keep their policy."""
|
||||
hermes_home = tmp_path / ".hermes"
|
||||
hermes_home.mkdir()
|
||||
(hermes_home / "config.yaml").write_text(
|
||||
"session_reset:\n mode: idle\n idle_minutes: 30\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
monkeypatch.setenv("HERMES_HOME", str(hermes_home))
|
||||
|
||||
config = load_gateway_config()
|
||||
|
||||
assert config.default_reset_policy.mode == "idle"
|
||||
assert config.default_reset_policy.idle_minutes == 30
|
||||
|
||||
def test_bridges_quick_commands_from_config_yaml(self, tmp_path, monkeypatch):
|
||||
hermes_home = tmp_path / ".hermes"
|
||||
hermes_home.mkdir()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue