Home.MemoryTool
C# · package Toolnexus · SPEC §7E · Agents/Home.cs
namespace Toolnexus.Agents;
public static class Home{ public static ITool MemoryTool(string dir);}A memory tool over a persona’s home directory — not one of the default builtin tools; it
only exists when a home is wired (Home.FromDir attaches it
automatically, or add it to Uses/ExtraTools yourself). One tool, three actions —
add appends an entry, replace swaps an existing substring, remove deletes one — against
target = "self" (MEMORY.md, the default) or target = "user" (USER.md). Every action
writes to disk. A replace/remove whose substring is not found is a loud IsError result the
model can read and recover from.
When to use it
Section titled “When to use it”You’re giving a long-lived persona somewhere to put durable facts — “the user is in IST”, “prefers
terse replies” — that should survive past the current session. Writes land on disk immediately,
but not in the live prompt: the frozen-snapshot rule (§7E) means a mid-session write is only
visible the next time Home.ComposeSoul runs, at the start
of a new session. That is deliberate — it keeps a long conversation’s system prompt cache-stable.
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
Section titled “1. The smallest useful call — add an entry”using Toolnexus.Agents;
var dir = Directory.CreateTempSubdirectory("toolnexus-memory-").FullName;var memory = Home.MemoryTool(dir);
var result = await memory.ExecuteAsync(new Dictionary<string, object?>{ ["action"] = "add", ["text"] = "the user is in IST",});
if (result.IsError) throw new Exception(result.Output);var memoryFile = Path.Combine(dir, "MEMORY.md");if (!File.ReadAllText(memoryFile).Contains("the user is in IST")) throw new Exception("entry missing");
Console.WriteLine($"ok: {result.Output}");2. replace/remove, target = "user", and the loud error on a miss
Section titled “2. replace/remove, target = "user", and the loud error on a miss”using Toolnexus.Agents;
var dir = Directory.CreateTempSubdirectory("toolnexus-memory-").FullName;var memory = Home.MemoryTool(dir);
await memory.ExecuteAsync(new Dictionary<string, object?> { ["action"] = "add", ["text"] = "prefers Tamil greetings", ["target"] = "user" });
var replaced = await memory.ExecuteAsync(new Dictionary<string, object?>{ ["action"] = "replace", ["text"] = "prefers Tamil greetings", ["with"] = "prefers English greetings", ["target"] = "user",});if (replaced.IsError) throw new Exception(replaced.Output);
var userFile = Path.Combine(dir, "USER.md");var body = File.ReadAllText(userFile);if (!body.Contains("prefers English greetings") || body.Contains("Tamil")) throw new Exception(body);
// A replace/remove of a substring that isn't there is a LOUD error — the model can read this back// and try something else, rather than the write silently no-op'ing.var miss = await memory.ExecuteAsync(new Dictionary<string, object?> { ["action"] = "remove", ["text"] = "not present anywhere", ["target"] = "user" });if (!miss.IsError) throw new Exception("expected a loud error on a missing substring");
Console.WriteLine($"ok: {body.Trim()} | miss={miss.Output}");3. Full surface — writes hit disk, but a live snapshot is frozen until next session
Section titled “3. Full surface — writes hit disk, but a live snapshot is frozen until next session”using Toolnexus.Agents;
var dir = Directory.CreateTempSubdirectory("toolnexus-memory-").FullName;File.WriteAllText(Path.Combine(dir, "SOUL.md"), "You are Kavi.");
// Compose once — this is the "frozen snapshot" a live session's system prompt would carry.var (soulBefore, _) = Home.ComposeSoul(dir);if (soulBefore.Contains("MEMORY.md")) throw new Exception("no MEMORY.md should exist yet");
var memory = Home.MemoryTool(dir);var unknown = await memory.ExecuteAsync(new Dictionary<string, object?> { ["action"] = "bogus", ["text"] = "x" });if (!unknown.IsError) throw new Exception("an unknown action must be a loud error");
var added = await memory.ExecuteAsync(new Dictionary<string, object?> { ["action"] = "add", ["text"] = "loves terse answers" });if (added.IsError) throw new Exception(added.Output);if (!added.Output.Contains("loads next session")) throw new Exception(added.Output); // the tool says so, in its own words
// The write hit disk, but the snapshot taken BEFORE the write can't see it — the cache-stability// rule (SPEC §7E): mid-session writes only take effect on the NEXT ComposeSoul.if (soulBefore.Contains("loves terse answers")) throw new Exception("stale snapshot must not see the write");
var (soulAfter, foundAfter) = Home.ComposeSoul(dir);if (!soulAfter.Contains("loves terse answers")) throw new Exception("a FRESH compose must see the write");if (!foundAfter.Contains("MEMORY.md")) throw new Exception(string.Join(",", foundAfter));
Console.WriteLine($"ok: frozen snapshot unaffected; next session sees {foundAfter[^1]}");Actions
Section titled “Actions”| Action | Effect | Target file |
|---|---|---|
add |
Appends an entry as a new list item. | self → MEMORY.md (default), user → USER.md |
replace |
Swaps an existing substring (text) for with. Loud IsError if text is absent. |
same |
remove |
Deletes an existing substring (text). Loud IsError if text is absent. |
same |
Parameters
Section titled “Parameters”| Parameter | Type | What it is |
|---|---|---|
dir |
string |
The persona’s home directory — the same one passed to ComposeSoul/FromDir. |
See also
Section titled “See also”Home.ComposeSoul— Build a persona’s system prompt from its home directory: identity, memory, skills.Home.FromDir— Point at an agent home directory and get a configured agent back.