MemoryTool
Go · package github.com/muthuishere/toolnexus/golang/agents · SPEC §7E · golang/agents/home.go
func MemoryTool(dir string) tn.ToolMemoryTool returns a single memory tool with three actions — add, replace, remove —
over two files in dir: MEMORY.md (target: "self", the default) and USER.md
(target: "user"). Every action writes to disk immediately. A replace/remove whose
text substring is not found in the target file is a loud IsError: true. Crucially, writing
does not change the current session’s soul — it takes effect at the start of the next
session, when ComposeSoul re-reads the directory. This is
the frozen-snapshot rule that keeps a long-lived persona cache-stable within a run.
When to use it
Section titled “When to use it”Reach for MemoryTool directly when you’re assembling a persona’s tool list by hand (via
agents.New rather than FromDir) and
want the same file-backed memory builtin FromDir wires automatically. It’s also the tool to
point at when writing a HEARTBEAT.md that asks the agent to persist facts — “fold notes into
MEMORY.md via the memory tool” is the entire recipe behind dream/consolidation patterns.
Why this and not the alternative
Section titled “Why this and not the alternative”Examples
Section titled “Examples”1. The smallest useful call — add an entry, read it back from disk
Section titled “1. The smallest useful call — add an entry, read it back from disk”package main
import ( "fmt" "log" "os" "path/filepath"
"github.com/muthuishere/toolnexus/golang/agents")
func main() { dir, err := os.MkdirTemp("", "memtool-*") if err != nil { log.Fatal(err) } defer os.RemoveAll(dir)
mem := agents.MemoryTool(dir) if mem.Name != "memory" { log.Fatalf("expected tool name memory, got %q", mem.Name) }
res, err := mem.Execute(map[string]any{"action": "add", "text": "prefers metric units"}, nil) if err != nil { log.Fatal(err) } if res.IsError { log.Fatalf("unexpected error: %s", res.Output) }
onDisk, err := os.ReadFile(filepath.Join(dir, "MEMORY.md")) if err != nil { log.Fatal(err) } if got := string(onDisk); got != "- prefers metric units\n" { log.Fatalf("unexpected MEMORY.md contents: %q", got) }
fmt.Println("ok:", res.Output)}2. The realistic case — replace, remove, and the loud miss
Section titled “2. The realistic case — replace, remove, and the loud miss”replace/remove on text that isn’t present is a real IsError, not a silent no-op — the
model sees the miss and can retry with the right substring.
package main
import ( "fmt" "log" "os" "path/filepath"
"github.com/muthuishere/toolnexus/golang/agents")
func main() { dir, err := os.MkdirTemp("", "memtool-*") if err != nil { log.Fatal(err) } defer os.RemoveAll(dir)
mem := agents.MemoryTool(dir)
if _, err := mem.Execute(map[string]any{"action": "add", "text": "timezone: UTC"}, nil); err != nil { log.Fatal(err) } replaced, err := mem.Execute(map[string]any{"action": "replace", "text": "UTC", "with": "IST"}, nil) if err != nil || replaced.IsError { log.Fatalf("unexpected replace failure: %+v err=%v", replaced, err) } onDisk, _ := os.ReadFile(filepath.Join(dir, "MEMORY.md")) if got := string(onDisk); got != "- timezone: IST\n" { log.Fatalf("unexpected MEMORY.md after replace: %q", got) }
miss, err := mem.Execute(map[string]any{"action": "remove", "text": "nonexistent phrase"}, nil) if err != nil { log.Fatal(err) } if !miss.IsError { log.Fatal("expected a loud IsError on a remove miss") }
fmt.Println("ok: replace ->", string(onDisk), "| miss ->", miss.Output)}3. The full surface — target: "user" writes USER.md, and next-session-only semantics
Section titled “3. The full surface — target: "user" writes USER.md, and next-session-only semantics”Writing memory during a run does not retroactively change that run’s soul — a fresh
ComposeSoul call (the next session) is what picks it up. This example writes memory mid-run,
then shows ComposeSoul only sees it on a subsequent read.
package main
import ( "fmt" "log" "os" "path/filepath" "strings"
"github.com/muthuishere/toolnexus/golang/agents")
func main() { dir, err := os.MkdirTemp("", "memtool-*") if err != nil { log.Fatal(err) } defer os.RemoveAll(dir) if err := os.WriteFile(filepath.Join(dir, "SOUL.md"), []byte("You are Kip."), 0o644); err != nil { log.Fatal(err) }
// Session 1: compose the soul BEFORE any memory write — MEMORY.md does not exist yet. soulBefore, foundBefore := agents.ComposeSoul(dir) for _, f := range foundBefore { if f == "MEMORY.md" { log.Fatal("MEMORY.md should not exist before any write") } }
mem := agents.MemoryTool(dir) if _, err := mem.Execute(map[string]any{"action": "add", "target": "user", "text": "name is Muthu"}, nil); err != nil { log.Fatal(err) } if _, err := mem.Execute(map[string]any{"action": "add", "text": "always reply tersely"}, nil); err != nil { log.Fatal(err) }
// The write does NOT retroactively change soulBefore — it's a frozen snapshot. if strings.Contains(soulBefore, "MEMORY.md") { log.Fatal("session-1 soul must not gain a MEMORY.md section after a later write") }
userOnDisk, _ := os.ReadFile(filepath.Join(dir, "USER.md")) if string(userOnDisk) != "- name is Muthu\n" { log.Fatalf("unexpected USER.md: %q", userOnDisk) }
// Session 2: composing again (a fresh ComposeSoul call) picks up both new files. soulAfter, foundAfter := agents.ComposeSoul(dir) if len(foundAfter) != 3 { // SOUL.md, USER.md, MEMORY.md log.Fatalf("expected 3 sections next session, got %v", foundAfter) } if !strings.Contains(soulAfter, "always reply tersely") || !strings.Contains(soulAfter, "name is Muthu") { log.Fatal("expected both memory writes to surface in the next session's composed soul") }
fmt.Println("ok: memory writes land on disk immediately, load next session:", foundAfter)}Actions
Section titled “Actions”action |
Effect | Target file (target) |
|---|---|---|
add |
Appends "- <text>\n" to the file. |
self ⇒ MEMORY.md (default); user ⇒ USER.md |
replace |
Swaps the first occurrence of text with with. IsError if text is absent. |
same |
remove |
Deletes the first occurrence of text. IsError if text is absent. |
same |
See also
Section titled “See also”ComposeSoul— Build a persona’s system prompt from its home directory: identity, memory, skills.FromDir— Point at an agent home directory and get a configured agent back — wires this tool for you.