Stores facts about the user that outlive the session: who they are, what they prefer, which hosts and tools they run, what they are working on. Anything true tomorrow but not worth rediscovering belongs here; anything true only for this turn belongs in scratchpad.
Facts are scoped to the user. A save is an upsert on (category, key) — writing the same key again overwrites it rather than adding a second fact, so correct a wrong fact by saving over it.
| Parameter | Used by | Description | ||||
|---|---|---|---|---|---|---|
action |
required | save \ |
search \ |
forget \ |
list. |
|
query |
search |
Keywords describing what to look for. | ||||
category |
save, forget |
profile \ |
preferences \ |
technical \ |
projects \ |
other. |
key |
save, forget |
snake_case identifier, unique within the category. |
||||
value |
save |
The fact, one concise plain-text statement. | ||||
source |
save |
conversation (default) \ |
tool_call \ |
auto_discovery \ |
user_explicit. |
|
confidence |
save |
0–100, default 70. | ||||
expires_days |
save |
Set only when the thing itself ends — a trial, a temporary host. Omit for everything else. | ||||
source_context |
save |
Provenance, e.g. found via ip addr on localhost. |
Categories: profile = who they are, preferences = likes/dislikes, technical = OS/tools/servers, projects = ongoing work, other = everything else.
saveRequires category, key and value; omitting any of the three fails the call with a specific message. An invalid category is rejected; an invalid source is silently coerced to conversation, and confidence is clamped to 0–100.
{"action": "save", "category": "preferences", "key": "response_language",
"value": "Prefers all answers in Russian", "source": "user_explicit",
"confidence": 95, "source_context": "user asked directly in session"}
{"action": "save", "category": "technical", "key": "prod_server_ip",
"value": "Production runs on 192.168.1.168 (branch master, docker postgres)",
"source": "tool_call", "confidence": 95,
"source_context": "found via ssh_exec hostname/ip addr"}
Calibrate the two numbers honestly — they are what makes a fact worth trusting later:
| How you learned it | source |
confidence |
|---|---|---|
| Ran a tool and read the output | tool_call |
95 |
| The user told you | user_explicit |
80–95 |
| Extracted from the conversation | conversation |
70 |
| Read it on the web | auto_discovery |
50 |
| Inferred / guessed | conversation |
30 |
Expiry is for facts with a known end, not for facts you merely doubt. Set expires_days when the thing itself finishes — a trial that ends on a date, a host provisioned for a week. An address or a running service does not expire on a schedule: it is wrong the moment it changes, and only the tool that observes it can know that. A fact with no expiry is corrected by saving over it, which is what search-then-save is for.
searchRequires query. Returns up to 15 matching facts, each with its category, key, value and provenance (src, conf, ctx). This is the read path — search before saving, so you update an existing key instead of inventing a near-duplicate.
{"action": "search", "query": "server ip"}
{"action": "search", "query": "language preference"}
forgetRequires key; category optionally narrows it. Returns how many facts were deleted; deleting nothing is an error (not found), not a silent success.
{"action": "forget", "key": "prod_server_ip", "category": "technical"}
listReturns the categories that hold facts, not the facts themselves:
{"action": "list"}
Use it to see the shape of what is stored; use search to read the content.
home_server_ip and prod_server_ip are different facts; server holding three sentences is one unusable blob.value is a statement, not a field name: "Home server is 192.168.1.168", not "192.168.1.168".