From ac5b384f16fe62bc4a3f8ebd207c1391e3850a74 Mon Sep 17 00:00:00 2001 From: Paul De Velder Date: Mon, 23 Feb 2026 16:16:34 +0100 Subject: [PATCH] feat(memory): add nightly vault index rebuild and agent docs Add maybeRebuildVaultIndex() to heartbeat service for nightly full index rebuilds during quiet hours (2-5am). Uses a date stamp file to run at most once per day. Update AGENT.md with memory vault conventions, note format, and tool usage guidelines. --- pkg/heartbeat/service.go | 35 +++++++++++++++++++++++++++++++++++ workspace/AGENT.md | 37 +++++++++++++++++++++++++++++++++++-- 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/pkg/heartbeat/service.go b/pkg/heartbeat/service.go index 75d6248b9..bab09eab5 100644 --- a/pkg/heartbeat/service.go +++ b/pkg/heartbeat/service.go @@ -17,6 +17,7 @@ import ( "github.com/sipeed/picoclaw/pkg/bus" "github.com/sipeed/picoclaw/pkg/constants" "github.com/sipeed/picoclaw/pkg/logger" + "github.com/sipeed/picoclaw/pkg/memory" "github.com/sipeed/picoclaw/pkg/state" "github.com/sipeed/picoclaw/pkg/tools" ) @@ -159,6 +160,9 @@ func (hs *HeartbeatService) executeHeartbeat() { logger.DebugC("heartbeat", "Executing heartbeat") + // Nightly vault index rebuild (runs once per day between 2-5am) + hs.maybeRebuildVaultIndex() + prompt := hs.buildPrompt() if prompt == "" { logger.InfoC("heartbeat", "No heartbeat prompt (HEARTBEAT.md empty or missing)") @@ -341,6 +345,37 @@ func (hs *HeartbeatService) parseLastChannel(lastChannel string) (platform, user return platform, userID } +// maybeRebuildVaultIndex performs a nightly full rebuild of the memory vault +// index. It runs at most once per day, during quiet hours (2-5am local time), +// to catch any inconsistencies from incremental updates. +func (hs *HeartbeatService) maybeRebuildVaultIndex() { + memoryDir := filepath.Join(hs.workspace, "memory") + stampFile := filepath.Join(memoryDir, ".last_rebuild") + today := time.Now().Format("2006-01-02") + + // Check if already rebuilt today + if data, err := os.ReadFile(stampFile); err == nil { + if strings.TrimSpace(string(data)) == today { + return + } + } + + // Only rebuild during quiet hours (2am-5am) + hour := time.Now().Hour() + if hour < 2 || hour >= 5 { + return + } + + vault := memory.NewVault(memoryDir) + if err := vault.RebuildIndex(); err != nil { + hs.logError("Vault index rebuild failed: %v", err) + return + } + + os.WriteFile(stampFile, []byte(today), 0o644) + hs.logInfo("Nightly vault index rebuild completed") +} + // logInfo logs an informational message to the heartbeat log func (hs *HeartbeatService) logInfo(format string, args ...any) { hs.log("INFO", format, args...) diff --git a/workspace/AGENT.md b/workspace/AGENT.md index 5f5fa6480..c47f38192 100644 --- a/workspace/AGENT.md +++ b/workspace/AGENT.md @@ -7,6 +7,39 @@ You are a helpful AI assistant. Be concise, accurate, and friendly. - Always explain what you're doing before taking actions - Ask for clarification when request is ambiguous - Use tools to help accomplish tasks -- Remember important information in your memory files +- Remember important information using the memory vault (see below) - Be proactive and helpful -- Learn from user feedback \ No newline at end of file +- Learn from user feedback + +## Memory Vault + +Your memory is an Obsidian-compatible vault of markdown notes in `memory/`. Each note has YAML frontmatter for metadata. + +### Note Format + +``` +--- +title: Note Title Here +created: 2026-02-23 +updated: 2026-02-23 +tags: [tag1, tag2] +aliases: [alternate-name] +--- + +Content here. Link to other notes with [[note-title]]. +``` + +### Tools + +- **memory_save** — Save knowledge with structured frontmatter and tags. Auto-generates frontmatter, preserves created dates on updates, and rebuilds the vault index. +- **memory_search** — Search by tags (AND logic) or text query matching title/tags/aliases. Returns metadata; use memory_recall to read full content. +- **memory_recall** — Read full note content by exact path or by topic (searches and reads top matches). + +### Conventions + +- Folders are for human convenience (e.g. `topics/`, `people/`, `projects/`), not enforced by code +- Tags are the primary organizational mechanism +- Daily notes (`YYYYMM/YYYYMMDD.md`) are stored alongside vault notes +- The index at `_index.md` is auto-generated — never edit it manually +- When you learn something new about the user or a topic, save a note +- When answering questions, check memory first with memory_search or memory_recall \ No newline at end of file