From 68717937e89dd45787462ea9136a05fbefe0a24b Mon Sep 17 00:00:00 2001 From: Xubin Ren <52506698+Re-bin@users.noreply.github.com> Date: Mon, 27 Jul 2026 02:08:03 +0800 Subject: [PATCH] fix(agent): throttle idle scans by default --- docs/configuration.md | 6 +++--- nanobot/config/schema.py | 2 +- tests/agent/test_auto_compact.py | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 04699b2a..533e159f 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -2159,7 +2159,7 @@ When a user is idle for longer than a configured threshold, nanobot **proactivel "agents": { "defaults": { "idleCompactAfterMinutes": 15, - "idleCompactCheckIntervalSeconds": 0 + "idleCompactCheckIntervalSeconds": 60 } } } @@ -2168,12 +2168,12 @@ When a user is idle for longer than a configured threshold, nanobot **proactivel | Option | Default | Description | |--------|---------|-------------| | `agents.defaults.idleCompactAfterMinutes` | `15` | Minutes of idle time before auto-compaction starts. Set to `0` to disable. The default is close to a typical LLM KV cache expiry window, so stale sessions get compacted before the user returns. | -| `agents.defaults.idleCompactCheckIntervalSeconds` | `0` | Minimum number of seconds between scans for idle sessions. | +| `agents.defaults.idleCompactCheckIntervalSeconds` | `60` | Minimum number of seconds between scans for idle sessions. Set to `0` to scan on every idle tick (~1 s). | `sessionTtlMinutes` remains accepted as a legacy alias for backward compatibility, but `idleCompactAfterMinutes` is the preferred config key going forward. How it works: -1. **Idle detection**: On each idle tick (~1 s), checks all sessions for expiration, subject to the minimum interval set by `idleCompactCheckIntervalSeconds`. +1. **Idle detection**: On each idle tick (~1 s), checks whether an idle-session scan is due. By default, the full scan runs at most once per minute. 2. **Background compaction**: Idle sessions summarize the older live prefix via LLM and keep the most recent legal suffix (currently 8 messages). 3. **Summary injection**: When the user returns, the summary is injected as runtime context (one-shot, not persisted) alongside the retained recent suffix. 4. **Restart-safe resume**: The summary is also mirrored into session metadata so it can still be recovered after a process restart. diff --git a/nanobot/config/schema.py b/nanobot/config/schema.py index 452c5406..a36ded81 100644 --- a/nanobot/config/schema.py +++ b/nanobot/config/schema.py @@ -155,7 +155,7 @@ class AgentDefaults(Base): serialization_alias="idleCompactAfterMinutes", ) # Auto-compact idle threshold in minutes (0 = disabled) idle_compact_check_interval_seconds: int = Field( - default=0, + default=60, ge=0, ) # Minimum interval in seconds between scans for idle sessions consolidation_ratio: float = Field( diff --git a/tests/agent/test_auto_compact.py b/tests/agent/test_auto_compact.py index af27ab6e..957d0498 100644 --- a/tests/agent/test_auto_compact.py +++ b/tests/agent/test_auto_compact.py @@ -180,10 +180,10 @@ class TestSessionTTLConfig: assert data["idleCompactAfterMinutes"] == 30 assert "sessionTtlMinutes" not in data - def test_idle_scan_interval_defaults_to_zero(self): - """The default should preserve a scan on every idle tick.""" + def test_idle_scan_interval_defaults_to_sixty_seconds(self): + """The config default should avoid scanning all sessions every idle tick.""" defaults = AgentDefaults() - assert defaults.idle_compact_check_interval_seconds == 0 + assert defaults.idle_compact_check_interval_seconds == 60 def test_idle_scan_interval_uses_camel_case_config_key(self): """The JSON config should use the standard camelCase alias.""" @@ -226,8 +226,8 @@ class TestIdleScanThrottling: assert loop.auto_compact.check_expired.call_count == 2 - def test_default_idle_scan_interval_checks_every_tick(self, tmp_path, monkeypatch): - """The zero default should leave each idle tick eligible to scan.""" + def test_zero_idle_scan_interval_checks_every_tick(self, tmp_path, monkeypatch): + """An explicit zero should leave each idle tick eligible to scan.""" monkeypatch.setattr("nanobot.agent.loop.time.monotonic", lambda: 1_000.0) loop = _make_loop(tmp_path) loop.auto_compact.check_expired = MagicMock()