feat(agent): wire memory config, factory, and session migration into loop

Agent loop now respects Memory.Enabled toggle, uses NewFromConfig factory
for delegate construction, reads OffloadThresholdTokens from config, and
runs one-time file session migration on startup.
This commit is contained in:
ZanzyTHEbar 2026-02-18 13:06:10 +00:00
parent 5e0319f54b
commit b6ed552089

View file

@ -24,6 +24,7 @@ import (
"github.com/sipeed/picoclaw/pkg/constants" "github.com/sipeed/picoclaw/pkg/constants"
picofantasy "github.com/sipeed/picoclaw/pkg/fantasy" picofantasy "github.com/sipeed/picoclaw/pkg/fantasy"
"github.com/sipeed/picoclaw/pkg/logger" "github.com/sipeed/picoclaw/pkg/logger"
"github.com/sipeed/picoclaw/pkg/memory"
"github.com/sipeed/picoclaw/pkg/memory/delegate" "github.com/sipeed/picoclaw/pkg/memory/delegate"
memstore "github.com/sipeed/picoclaw/pkg/memory/store" memstore "github.com/sipeed/picoclaw/pkg/memory/store"
"github.com/sipeed/picoclaw/pkg/messages" "github.com/sipeed/picoclaw/pkg/messages"
@ -148,10 +149,11 @@ func NewAgentLoop(cfg *config.Config, msgBus *bus.MessageBus, model fantasy.Lang
// Initialize 3-tier MemGPT memory system // Initialize 3-tier MemGPT memory system
var ms *memstore.MemoryStore var ms *memstore.MemoryStore
if cfg.Memory.Enabled {
memDBPath := filepath.Join(workspace, "memory", "picoclaw.db") memDBPath := filepath.Join(workspace, "memory", "picoclaw.db")
os.MkdirAll(filepath.Dir(memDBPath), 0755) os.MkdirAll(filepath.Dir(memDBPath), 0755)
del, err := delegate.NewLibSQLDelegate(memDBPath) del, err := delegate.NewFromConfig(cfg.Memory, memDBPath)
if err != nil { if err != nil {
logger.WarnCF("agent", "Failed to create memory delegate, memory system disabled", logger.WarnCF("agent", "Failed to create memory delegate, memory system disabled",
map[string]interface{}{"error": err.Error()}) map[string]interface{}{"error": err.Error()})
@ -161,21 +163,39 @@ func NewAgentLoop(cfg *config.Config, msgBus *bus.MessageBus, model fantasy.Lang
map[string]interface{}{"error": err.Error()}) map[string]interface{}{"error": err.Error()})
del.Close() del.Close()
} else { } else {
offloadThreshold := cfg.Memory.OffloadThresholdTokens
if offloadThreshold <= 0 {
offloadThreshold = 4000
}
chunker := memstore.NewMarkdownChunker(memstore.DefaultMarkdownChunkerConfig()) chunker := memstore.NewMarkdownChunker(memstore.DefaultMarkdownChunkerConfig())
ms = memstore.New(del, chunker, nil, memstore.Config{
// Create embedding provider from config (nil = FTS5-only search)
embedder, embErr := memstore.NewEmbedderFromConfig(cfg.Memory.Embedding, cfg.Providers)
if embErr != nil {
logger.WarnCF("agent", "Failed to create embedding provider, archival search will use FTS5 only",
map[string]interface{}{"error": embErr.Error()})
}
ms = memstore.New(del, chunker, embedder, memstore.Config{
ContextWindowTokens: cfg.Agents.Defaults.MaxTokens, ContextWindowTokens: cfg.Agents.Defaults.MaxTokens,
OffloadThresholdTokens: 4000, OffloadThresholdTokens: offloadThreshold,
}) })
contextBuilder.SetMemoryStore(ms) contextBuilder.SetMemoryStore(ms)
// Register the memory tool
memTool := NewMemGPTTool(ms, "picoclaw", "default") memTool := NewMemGPTTool(ms, "picoclaw", "default")
toolsRegistry.Register(memTool) toolsRegistry.Register(memTool)
logger.InfoCF("agent", "3-tier memory system initialized", // One-time migration of file-based sessions into recall memory
map[string]interface{}{"db_path": memDBPath}) sessionsDir := filepath.Join(workspace, "sessions")
if _, migErr := memory.MigrateFileSessions(context.Background(), del, "picoclaw", sessionsDir); migErr != nil {
logger.WarnCF("agent", "Session migration failed (non-fatal)",
map[string]interface{}{"error": migErr.Error()})
} }
} }
}
} else {
logger.InfoCF("agent", "Memory system disabled by config", nil)
}
// Register meta-tools for progressive disclosure (tool_search + tool_call) // Register meta-tools for progressive disclosure (tool_search + tool_call)
toolsRegistry.RegisterMetaTools() toolsRegistry.RegisterMetaTools()