From 4c5e340186f507fd353336c95b5f0f9d822c7fb5 Mon Sep 17 00:00:00 2001 From: chengyongru <2755839590@qq.com> Date: Wed, 17 Jun 2026 00:14:32 +0800 Subject: [PATCH] feat(memory): enable idle auto compact by default --- docs/configuration.md | 4 ++-- nanobot/config/schema.py | 2 +- tests/agent/test_auto_compact.py | 9 +++++++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index d34a6cd3..50397213 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1865,7 +1865,7 @@ When a user is idle for longer than a configured threshold, nanobot **proactivel | Option | Default | Description | |--------|---------|-------------| -| `agents.defaults.idleCompactAfterMinutes` | `0` (disabled) | Minutes of idle time before auto-compaction starts. Set to `0` to disable. Recommended: `15` — close to a typical LLM KV cache expiry window, so stale sessions get compacted before the user returns. | +| `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. | `sessionTtlMinutes` remains accepted as a legacy alias for backward compatibility, but `idleCompactAfterMinutes` is the preferred config key going forward. @@ -1880,7 +1880,7 @@ How it works: > > Concretely, auto compact rewrites `sessions/.jsonl` in place: older messages (including their structured `tool_calls` / `tool_call_id` / `reasoning_content`) are replaced by just the retained recent suffix (currently 8 messages), while the archived prefix is preserved only as a plain-text summary appended to `memory/history.jsonl` (or a `[RAW] ...` flattened dump if LLM summarization fails). The original structured JSON of those turns is no longer recoverable from the session file. > -> This differs from the **token-driven soft consolidation** that fires when a prompt exceeds the context budget: that path only advances an internal `last_consolidated` cursor and leaves the session file untouched, so the raw tool-call trail stays on disk and can still be replayed or audited. If you rely on that trail for debugging or auditing, leave `idleCompactAfterMinutes` at the default `0` and let only the token-driven path run. +> This differs from the **token-driven soft consolidation** that fires when a prompt exceeds the context budget: that path only advances an internal `last_consolidated` cursor and leaves the session file untouched, so the raw tool-call trail stays on disk and can still be replayed or audited. If you rely on that trail for debugging or auditing, set `idleCompactAfterMinutes` to `0` and let only the token-driven path run. ## Timezone diff --git a/nanobot/config/schema.py b/nanobot/config/schema.py index bc0b1349..9980d309 100644 --- a/nanobot/config/schema.py +++ b/nanobot/config/schema.py @@ -145,7 +145,7 @@ class AgentDefaults(Base): unified_session: bool = False # Share one session across all channels (single-user multi-device) disabled_skills: list[str] = Field(default_factory=list) # Skill names to exclude from loading (e.g. ["summarize", "skill-creator"]) session_ttl_minutes: int = Field( - default=0, + default=15, ge=0, validation_alias=AliasChoices("idleCompactAfterMinutes", "sessionTtlMinutes"), serialization_alias="idleCompactAfterMinutes", diff --git a/tests/agent/test_auto_compact.py b/tests/agent/test_auto_compact.py index e6293ebe..398cbc95 100644 --- a/tests/agent/test_auto_compact.py +++ b/tests/agent/test_auto_compact.py @@ -151,9 +151,14 @@ async def _drain_background_tasks(loop: AgentLoop) -> None: class TestSessionTTLConfig: """Test session TTL configuration.""" - def test_default_ttl_is_zero(self): - """Default TTL should be 0 (disabled).""" + def test_default_ttl_is_fifteen_minutes(self): + """Default TTL should proactively compact stale sessions.""" defaults = AgentDefaults() + assert defaults.session_ttl_minutes == 15 + + def test_explicit_zero_disables_ttl(self): + """Explicit 0 should still disable idle auto-compact.""" + defaults = AgentDefaults(session_ttl_minutes=0) assert defaults.session_ttl_minutes == 0 def test_custom_ttl(self):