feat(agent): two-stage memory system with Dream consolidation

Replace single-stage MemoryConsolidator with a two-stage architecture:

- Consolidator: lightweight token-budget triggered summarization,
  appends to HISTORY.md with cursor-based tracking
- Dream: cron-scheduled two-phase processor that analyzes HISTORY.md
  and updates SOUL.md, USER.md, MEMORY.md via AgentRunner with
  edit_file tools for surgical, fault-tolerant updates

New files: MemoryStore (pure file I/O), Dream class, DreamConfig,
/dream and /dream-log commands. 89 tests covering all components.
This commit is contained in:
chengyongru
2026-04-02 22:42:25 +08:00
committed by chengyongru
parent 7113ad34f4
commit b9616674f0
18 changed files with 856 additions and 728 deletions
+25
View File
@@ -22,6 +22,7 @@ if sys.platform == "win32":
pass
import typer
from loguru import logger
from prompt_toolkit import PromptSession, print_formatted_text
from prompt_toolkit.application import run_in_terminal
from prompt_toolkit.formatted_text import ANSI, HTML
@@ -649,6 +650,15 @@ def gateway(
# Set cron callback (needs agent)
async def on_cron_job(job: CronJob) -> str | None:
"""Execute a cron job through the agent."""
# Dream is an internal job — run directly, not through the agent loop.
if job.name == "dream":
try:
await agent.dream.run()
logger.info("Dream cron job completed")
except Exception:
logger.exception("Dream cron job failed")
return None
from nanobot.agent.tools.cron import CronTool
from nanobot.agent.tools.message import MessageTool
from nanobot.utils.evaluator import evaluate_response
@@ -768,6 +778,21 @@ def gateway(
console.print(f"[green]✓[/green] Heartbeat: every {hb_cfg.interval_s}s")
# Register Dream cron job (always-on, idempotent on restart)
dream_cfg = config.agents.defaults.dream
if dream_cfg.model:
agent.dream.model = dream_cfg.model
agent.dream.max_batch_size = dream_cfg.max_batch_size
agent.dream.max_iterations = dream_cfg.max_iterations
from nanobot.cron.types import CronJob, CronPayload, CronSchedule
cron.register_system_job(CronJob(
id="dream",
name="dream",
schedule=CronSchedule(kind="cron", expr=dream_cfg.cron, tz=config.agents.defaults.timezone),
payload=CronPayload(kind="system_event"),
))
console.print(f"[green]✓[/green] Dream: cron {dream_cfg.cron}")
async def run():
try:
await cron.start()