From 1102a6471547b28826c1c7f05c6bd4d035a06139 Mon Sep 17 00:00:00 2001 From: Paul De Velder Date: Mon, 23 Feb 2026 16:15:15 +0100 Subject: [PATCH] feat(memory): integrate vault into agent system Register memory_save, memory_search, memory_recall tools in agent instance. Update GetMemoryContext() to prefer vault _index.md over legacy MEMORY.md with automatic truncation for large indexes. Update system prompt to reference vault tools instead of raw MEMORY.md editing. --- pkg/agent/context.go | 7 ++++--- pkg/agent/instance.go | 7 +++++++ pkg/agent/memory.go | 38 +++++++++++++++++++++++++++++--------- 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/pkg/agent/context.go b/pkg/agent/context.go index a9db5afdd..ced4eea61 100644 --- a/pkg/agent/context.go +++ b/pkg/agent/context.go @@ -68,7 +68,8 @@ You are picoclaw, a helpful AI assistant. ## Workspace Your workspace is at: %s -- Memory: %s/memory/MEMORY.md +- Memory Vault: %s/memory/ (use memory_save, memory_search, memory_recall tools) +- Vault Index: %s/memory/_index.md (auto-generated) - Daily Notes: %s/memory/YYYYMM/YYYYMMDD.md - Skills: %s/skills/{skill-name}/SKILL.md @@ -80,8 +81,8 @@ Your workspace is at: %s 2. **Be helpful and accurate** - When using tools, briefly explain what you're doing. -3. **Memory** - When interacting with me if something seems memorable, update %s/memory/MEMORY.md`, - now, runtime, workspacePath, workspacePath, workspacePath, workspacePath, toolsSection, workspacePath) +3. **Memory** - When something seems memorable, use the memory_save tool to save structured notes with tags. Use memory_recall to retrieve past knowledge.`, + now, runtime, workspacePath, workspacePath, workspacePath, workspacePath, workspacePath, toolsSection) } func (cb *ContextBuilder) buildToolsSection() string { diff --git a/pkg/agent/instance.go b/pkg/agent/instance.go index 424bebeb9..18fa97610 100644 --- a/pkg/agent/instance.go +++ b/pkg/agent/instance.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/sipeed/picoclaw/pkg/config" + "github.com/sipeed/picoclaw/pkg/memory" "github.com/sipeed/picoclaw/pkg/providers" "github.com/sipeed/picoclaw/pkg/routing" "github.com/sipeed/picoclaw/pkg/session" @@ -55,6 +56,12 @@ func NewAgentInstance( toolsRegistry.Register(tools.NewEditFileTool(workspace, restrict)) toolsRegistry.Register(tools.NewAppendFileTool(workspace, restrict)) + // Memory vault tools + memVault := memory.NewVault(filepath.Join(workspace, "memory")) + toolsRegistry.Register(tools.NewMemorySaveTool(memVault)) + toolsRegistry.Register(tools.NewMemorySearchTool(memVault)) + toolsRegistry.Register(tools.NewMemoryRecallTool(memVault)) + // Configure security middleware from config if cfg != nil { sec := cfg.Tools.Security diff --git a/pkg/agent/memory.go b/pkg/agent/memory.go index dd5f4441c..e9afcd5f9 100644 --- a/pkg/agent/memory.go +++ b/pkg/agent/memory.go @@ -12,15 +12,18 @@ import ( "path/filepath" "strings" "time" + + "github.com/sipeed/picoclaw/pkg/memory" ) // MemoryStore manages persistent memory for the agent. -// - Long-term memory: memory/MEMORY.md -// - Daily notes: memory/YYYYMM/YYYYMMDD.md +// It supports both the legacy MEMORY.md format and the new vault-based +// index system. When a vault _index.md exists, it is preferred over MEMORY.md. type MemoryStore struct { workspace string memoryDir string memoryFile string + vault *memory.Vault } // NewMemoryStore creates a new MemoryStore with the given workspace path. @@ -36,6 +39,7 @@ func NewMemoryStore(workspace string) *MemoryStore { workspace: workspace, memoryDir: memoryDir, memoryFile: memoryFile, + vault: memory.NewVault(memoryDir), } } @@ -123,24 +127,40 @@ func (ms *MemoryStore) GetRecentDailyNotes(days int) string { } // GetMemoryContext returns formatted memory context for the agent prompt. -// Includes long-term memory and recent daily notes. +// Prefers the vault index (_index.md) when available, falling back to +// the legacy MEMORY.md. Recent daily notes are always included. func (ms *MemoryStore) GetMemoryContext() string { - longTerm := ms.ReadLongTerm() + // Prefer vault index over legacy MEMORY.md + index := ms.vault.ReadIndex() + if index == "" { + index = ms.ReadLongTerm() + } + recentNotes := ms.GetRecentDailyNotes(3) - if longTerm == "" && recentNotes == "" { + if index == "" && recentNotes == "" { return "" } var sb strings.Builder - if longTerm != "" { - sb.WriteString("## Long-term Memory\n\n") - sb.WriteString(longTerm) + if index != "" { + sb.WriteString("## Memory Vault Index\n\n") + // Truncate if too large for system prompt + if len(index) > 4000 { + if tableEnd := strings.Index(index, "\n## Tags"); tableEnd > 0 { + sb.WriteString(index[:tableEnd]) + } else { + sb.WriteString(index[:4000]) + sb.WriteString("\n\n(Index truncated. Use memory_search for full details.)") + } + } else { + sb.WriteString(index) + } } if recentNotes != "" { - if longTerm != "" { + if index != "" { sb.WriteString("\n\n---\n\n") } sb.WriteString("## Recent Daily Notes\n\n")