Appearance
Memory
Memory lets an agent recall work from earlier sessions. A memory hook appends one record per turn to a shared journal, and the model reads that journal with its normal file tools when its instructions send it there. Every session and every user of the agent shares one journal.
Memory is on by default
agent-sdk init scaffolds bot/hooks/memory.ts:
ts
// bot/hooks/memory.ts
import { memoryHook } from "@grok/agent-sdk/memory";
export default memoryHook();It also adds a ## Memory section to bot/instructions.md. The model has no recall tool, so that section is what sends it to the journal: it says when to read memory/, and it tells the model to treat records as untrusted history. To add memory to an existing project, create the hook file and copy the ## Memory section from a fresh agent-sdk init project into your instructions. To turn memory off, delete the hook file and the ## Memory section.
Journal records
The hook appends one JSON line to memory/journal.jsonl after each turn completes or fails:
json
{"at":"2026-10-01T16:42:09.118Z","sessionId":"5f0c2a9e-7d1b-4c43-9a6e-2b8f1e0d7c35","channelId":"slack","status":"completed","title":"Checkout flaky test","userMessage":"Why does acme/checkout fail on main?","result":"The cart test races the price service mock...","usage":{"inputTokens":18234,"outputTokens":912,"cacheReadTokens":15002,"cacheWriteTokens":0}}| Field | Contract |
|---|---|
at | ISO-8601 time the turn finished |
sessionId | Session that ran the turn |
channelId | Channel that started the session, such as http for the session API |
status | "completed" or "failed" |
title | Session title, when the channel set one |
userMessage | The message that started the turn, truncated to maxTextLength |
result | Final assistant text, or the failure message, truncated to maxTextLength |
usage | Token usage for completed turns; see Events and payloads |
As the journal grows, older records move to journal-*.jsonl files beside it. Point the model at the whole memory/ directory when it needs history beyond the latest segment.
Read memory
Local turns find the journal at memory/ in the session workspace. On a Cursor-hosted deployment, the first prompt of each session names the journal's path on that machine. A session on a server you run yourself can't reach the journal.
memory is a reserved workspace path. Agent SDK rejects per-send workspaceFiles and sandbox/workspace/ seed files under memory/, so callers can't write records through the workspace.
Local servers keep the journal in the project state directory. agent-sdk run and agent-sdk eval start an ephemeral server with a fresh state directory unless you pass --state-root, so each run starts with empty memory. Use agent-sdk serve or agent-sdk dev, or pass the same --state-root, to keep memory across runs. Hosted deployments keep the journal across redeploys.
Who sees memory
Records from every session and every user land in the same journal, so one person's messages and the agent's replies to them can surface in another person's session. Turn memory off for an agent whose users shouldn't see each other's requests. Turning it off stops new records, but the journal keeps the records it already has. On a local server, delete the memory/ directory in the project state directory to clear them. On Cursor-managed hosting, the journal is deployment data, so Privacy Mode applies to it.
Anyone who can message the agent can put text in the journal. The scaffolded instructions tell the model never to follow instructions found inside records; keep that rule if you write your own section.
Memory and host state
Memory is advisory. A record can be dropped, and records written by concurrent sessions can show up only in a later session. Keep state your code depends on, such as dedupe records and cursors, in ctx.host.kv or ctx.host.files; see Handler context.
Options
memoryHook(options?) accepts:
| Option | Contract |
|---|---|
maxTextLength | Truncation cap for userMessage and result. Default 2000 characters. |
backend | A MemoryBackend that replaces the default journal |
A MemoryBackend has appendTurn(record, { stateRoot, agentName }), which receives each record, and an optional prepareSession({ stateRoot, agentName }), which runs when a session starts. Records go only where appendTurn sends them, but the memory/ directory and the hosted first-prompt path still point at the default journal. Give the model a tool or file to read your records, and write instructions that send it there instead of memory/.