From 47a5f4b6021fc419231c9a079b0cf8fd42464fbc Mon Sep 17 00:00:00 2001 From: lpf Date: Fri, 13 Feb 2026 14:35:23 +0800 Subject: [PATCH] fix bug --- pkg/agent/loop.go | 3 +++ pkg/agent/memory.go | 50 +++++++++++++++++++++++++++++++++++++++------ pkg/tools/memory.go | 45 +++++++++++++++++++++++++--------------- 3 files changed, 76 insertions(+), 22 deletions(-) diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index ff56dbd..5089535 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -48,6 +48,9 @@ type AgentLoop struct { func NewAgentLoop(cfg *config.Config, msgBus *bus.MessageBus, provider providers.LLMProvider, cs *cron.CronService) *AgentLoop { workspace := cfg.WorkspacePath() os.MkdirAll(workspace, 0755) + logger.InfoCF("agent", "Agent workspace initialized", map[string]interface{}{ + "workspace": workspace, + }) toolsRegistry := tools.NewToolRegistry() toolsRegistry.Register(&tools.ReadFileTool{}) diff --git a/pkg/agent/memory.go b/pkg/agent/memory.go index 042ed18..bc95a07 100644 --- a/pkg/agent/memory.go +++ b/pkg/agent/memory.go @@ -11,6 +11,8 @@ import ( "os" "path/filepath" "time" + + "clawgo/pkg/logger" ) // MemoryStore manages persistent memory for the agent. @@ -29,7 +31,38 @@ func NewMemoryStore(workspace string) *MemoryStore { memoryFile := filepath.Join(memoryDir, "MEMORY.md") // Ensure memory directory exists - os.MkdirAll(memoryDir, 0755) + if err := os.MkdirAll(memoryDir, 0755); err != nil { + logger.ErrorCF("memory", "Failed to create memory directory", map[string]interface{}{ + "memory_dir": memoryDir, + "error": err.Error(), + }) + } + + // Ensure MEMORY.md exists for first run (even without onboard). + if _, err := os.Stat(memoryFile); os.IsNotExist(err) { + initial := `# Long-term Memory + +This file stores important information that should persist across sessions. + +## User Information + +(Important facts about user) + +## Preferences + +(User preferences learned over time) + +## Important Notes + +(Things to remember) +` + if writeErr := os.WriteFile(memoryFile, []byte(initial), 0644); writeErr != nil { + logger.ErrorCF("memory", "Failed to initialize MEMORY.md", map[string]interface{}{ + "memory_file": memoryFile, + "error": writeErr.Error(), + }) + } + } return &MemoryStore{ workspace: workspace, @@ -40,8 +73,8 @@ func NewMemoryStore(workspace string) *MemoryStore { // getTodayFile returns the path to today's daily note file (memory/YYYYMM/YYYYMMDD.md). func (ms *MemoryStore) getTodayFile() string { - today := time.Now().Format("20060102") // YYYYMMDD - monthDir := today[:6] // YYYYMM + today := time.Now().Format("20060102") // YYYYMMDD + monthDir := today[:6] // YYYYMM filePath := filepath.Join(ms.memoryDir, monthDir, today+".md") return filePath } @@ -57,6 +90,9 @@ func (ms *MemoryStore) ReadLongTerm() string { // WriteLongTerm writes content to the long-term memory file (MEMORY.md). func (ms *MemoryStore) WriteLongTerm(content string) error { + if err := os.MkdirAll(ms.memoryDir, 0755); err != nil { + return err + } return os.WriteFile(ms.memoryFile, []byte(content), 0644) } @@ -77,7 +113,9 @@ func (ms *MemoryStore) AppendToday(content string) error { // Ensure month directory exists monthDir := filepath.Dir(todayFile) - os.MkdirAll(monthDir, 0755) + if err := os.MkdirAll(monthDir, 0755); err != nil { + return err + } var existingContent string if data, err := os.ReadFile(todayFile); err == nil { @@ -104,8 +142,8 @@ func (ms *MemoryStore) GetRecentDailyNotes(days int) string { for i := 0; i < days; i++ { date := time.Now().AddDate(0, 0, -i) - dateStr := date.Format("20060102") // YYYYMMDD - monthDir := dateStr[:6] // YYYYMM + dateStr := date.Format("20060102") // YYYYMMDD + monthDir := dateStr[:6] // YYYYMM filePath := filepath.Join(ms.memoryDir, monthDir, dateStr+".md") if data, err := os.ReadFile(filePath); err == nil { diff --git a/pkg/tools/memory.go b/pkg/tools/memory.go index 4f002ba..178063c 100644 --- a/pkg/tools/memory.go +++ b/pkg/tools/memory.go @@ -70,7 +70,7 @@ func (t *MemorySearchTool) Execute(ctx context.Context, args map[string]interfac } files := t.getMemoryFiles() - + resultsChan := make(chan []searchResult, len(files)) var wg sync.WaitGroup @@ -126,23 +126,36 @@ func (t *MemorySearchTool) Execute(ctx context.Context, args map[string]interfac func (t *MemorySearchTool) getMemoryFiles() []string { var files []string - - // Check main MEMORY.md - mainMem := filepath.Join(t.workspace, "MEMORY.md") - if _, err := os.Stat(mainMem); err == nil { - files = append(files, mainMem) - } + seen := map[string]struct{}{} - // Check memory/ directory - memDir := filepath.Join(t.workspace, "memory") - entries, err := os.ReadDir(memDir) - if err == nil { - for _, entry := range entries { - if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".md") { - files = append(files, filepath.Join(memDir, entry.Name())) - } + addIfExists := func(path string) { + if _, ok := seen[path]; ok { + return + } + if _, err := os.Stat(path); err == nil { + files = append(files, path) + seen[path] = struct{}{} } } + + // Check long-term memory in both legacy and current locations. + addIfExists(filepath.Join(t.workspace, "MEMORY.md")) + addIfExists(filepath.Join(t.workspace, "memory", "MEMORY.md")) + + // Check memory/ directory recursively (e.g., memory/YYYYMM/YYYYMMDD.md). + memDir := filepath.Join(t.workspace, "memory") + _ = filepath.Walk(memDir, func(path string, info os.FileInfo, err error) error { + if err != nil || info == nil || info.IsDir() { + return nil + } + if strings.HasSuffix(strings.ToLower(info.Name()), ".md") { + if _, ok := seen[path]; !ok { + files = append(files, path) + seen[path] = struct{}{} + } + } + return nil + }) return files } @@ -210,7 +223,7 @@ func (t *MemorySearchTool) searchFile(path string, keywords []string) ([]searchR // 1. Headers start new blocks // 2. Empty lines separate blocks // 3. List items start new blocks (optional, but good for logs) - + isHeader := strings.HasPrefix(trimmed, "#") isEmpty := trimmed == "" isList := strings.HasPrefix(trimmed, "- ") || strings.HasPrefix(trimmed, "* ") || (len(trimmed) > 3 && trimmed[1] == '.' && trimmed[2] == ' ')