# Memory Management

Course: [Context Engineering](https://www.devtutorials.ai/learn/context-engineering) · Section: Memory, Media and Tools
Source: https://www.devtutorials.ai/learn/context-engineering/memory-media-tools/memory-management

> How an app manages memory over time: short-term, long-term and state, extracting decisions and open tasks, compacting at the right time, and the lifecycle and privacy rules for stored memory.

A person talks to a friend on the phone and says, "Remember I told you I was looking for a new job?" That's memory: keeping a fact from earlier in the talk.

An AI chat should work the same way. When a user mentions something, the AI should remember it and use it later, even if the chat is very long.

But the AI can't read the whole chat every time. So you make a **memory**: a short version of what matters. As the chat grows, you keep the memory up to date, and throw away facts that are no longer needed.

**The main idea:** remember what's important from the chat, keep it short, mark when it expires, and follow privacy rules.

## A Real-World Example

Think about a teacher with a student.

- Day 1: Student says "I'm allergic to peanuts." Teacher writes it down.
- Day 10: Teacher sees the note and keeps in mind.
- Day 20: Student says "I'm not allergic any more." Teacher updates the note.
- End of year: Teacher archives the note. The student moved to another school.

The teacher doesn't memorize every chat. She keeps key facts, updates them when they change, and throws them away when they're old.

**In simple words:** write down what matters, update it, and know when to forget it.

## What You Will Learn

In this lesson, you will learn:

1. The three types of memory: short, long, and state.
2. How to pick what to remember and what to forget.
3. How memory changes over time and expires.
4. How to keep memory private and follow rules.
5. How to pull decisions and tasks out of a chat.

## Three Types of Memory

### Short-Term Memory

**Short-term memory** is the current conversation. It holds the last few messages, so the model knows what just happened.

Use short-term memory for:
- The last few question-answer pairs.
- The current task the user is working on.
- Facts just mentioned.

```js runnable title="Short-term memory holds the last few messages"
const conversation = [
  { role: "user", text: "What's the free plan limit?" },
  { role: "model", text: "Free plan: 5,000 rows." },
  { role: "user", text: "Can I export to PDF?" },
  { role: "model", text: "Yes, from File > Export." },
];

const recentWindow = 2;
const shortTerm = conversation.slice(-recentWindow * 2);

console.log("Recent context (last " + recentWindow + " turn(s)):");
for (const m of shortTerm) {
  console.log(m.role + ": " + m.text);
}
```

Expected output:

```text
Recent context (last 2 turn(s)):
user: Can I export to PDF?
model: Yes, from File > Export.
```

The model gets only the last two messages. It knows what the user just asked, but it doesn't remember the first question.

### Long-Term Memory

**Long-Term memory** is facts the user has said that matter for many chats, not just today.

Use long-term memory for:
- The user's name, company, role.
- Big preferences or constraints: "I only use Chrome", "I'm allergic to X."
- Major goals: "I'm looking for a job in sales."
- Decisions that apply to many chats.

```js runnable title="Long-term memory: facts that last"
const memory = [
  { fact: "User name is Alice" },
  { fact: "Works at Brightgrid" },
  { fact: "Has admin role" },
  { fact: "Prefers email over chat" },
];

console.log("User profile for next chats:");
for (const m of memory) {
  console.log("- " + m.fact);
}
```

Expected output:

```text
User profile for next chats:
- User name is Alice
- Works at Brightgrid
- Has admin role
- Prefers email over chat
```

These facts follow Alice from chat to chat. If she switches browsers, the fact "prefers email" still applies.

### State Memory

**State memory** is the current status of something the user is doing: an order being built, a form being filled, a workflow mid-way.

Use state memory for:
- The current order and its items.
- A form with some fields filled.
- A task list and which tasks are done.
- The current step in a multi-step workflow.

```js runnable title="State memory: what's in progress now"
const state = {
  order: { id: "ORD-1024", status: "in progress", items: 2, total: 0 },
  task: { name: "Export data", status: "pending", assigned: "Alice" },
};

console.log("In progress:");
console.log("- Order: " + state.order.items + " items, status: " + state.order.status);
console.log("- Task: " + state.task.name + ", assigned to: " + state.task.assigned);
```

Expected output:

```text
In progress:
- Order: 2 items, status: in progress
- Task: Export data, assigned to: Alice
```

State memory holds the work that's happening right now. When the order is done, the state goes away.

**Easy Way to Remember:** **short-term** is this chat, **long-term** is next week, and **state** is where the task stands now.

## What to Remember and What to Forget

### Decision Extraction

When a user makes a decision in a chat, write it down for later:

**Bad example:** "The user said they wanted the Pro plan."

**Better example:**
```
Decision: Switch to Pro plan
Made at: 2026-01-15 10:30 AM
User: Alice (admin)
Status: pending approval
```

```js runnable title="Pull decisions from a chat"
const chat = [
  "User: I think the Pro plan is better for us.",
  "Model: That's $20 per developer per month.",
  "User: Yes, let's do it.",
];

const decision = {
  what: "Switch to Pro plan",
  when: "2026-01-15",
  who: "Alice",
  status: "pending",
};

console.log("Extracted decision:");
for (const [key, value] of Object.entries(decision)) {
  console.log(key + ": " + value);
}
```

Expected output:

```text
Extracted decision:
what: Switch to Pro plan
when: 2026-01-15
who: Alice
status: pending
```

This decision goes into long-term memory or state memory. The model can use it in the next chat.

### Open Tasks

When the model says "I'll do this and get back to you", pull it into memory:

```js runnable title="Track open tasks from the chat"
const tasks = [
  { task: "Send report to Alice", deadline: "2026-01-20", owner: "system" },
  { task: "Wait for Alice's approval", deadline: "2026-01-15", owner: "user" },
];

for (const t of tasks) {
  console.log(t.task + " (due " + t.deadline + ", " + t.owner + ")");
}
```

Expected output:

```text
Send report to Alice (due 2026-01-20, system)
Wait for Alice's approval (due 2026-01-15, user)
```

These go into state memory and get checked in the next chat.

### What to Throw Away

Forget facts that are:
- **Old**: "The user was looking for a job." (They found one 6 months ago.)
- **Done**: "User wants to fix bug X." (Bug X is fixed.)
- **No longer relevant**: "User asked about pricing." (They already bought.)
- **Private**: See privacy section below.

## Memory Lifecycle

### Create

When a user mentions something important, create a memory entry:

```
User: I'm allergic to peanuts.
Memory created: Allergy_peanuts, trust: high, expires: never
```

### Use

In the next chat, add this memory to the context:

```
MEMORY: User is allergic to peanuts (from chat 5, high trust)
User: What snacks can I bring to the office?
```

### Update

When the user or new facts change it:

```
User: I'm not allergic any more.
Memory updated: Allergy_peanuts changed to: None (expires: never)
```

Or:

```
System: User has not logged in for 90 days.
Memory updated: Inactive_user flag added, expires: 180 days
```

### Expire

When the memory is no longer needed or too old, remove it:

```
User inactive 90 days.
Memory expired: Inactive_user (was 90 days old)
Retention_archive: moved to archive, kept 1 year
```

```js runnable title="The memory lifecycle in order"
const lifecycle = [
  { step: "Create", action: "User says something important" },
  { step: "Use", action: "Add it to the context for the next chat" },
  { step: "Update", action: "User says it changed, update the memory" },
  { step: "Expire", action: "It's old or no longer needed, remove it" },
];

for (const l of lifecycle) {
  console.log(l.step + ": " + l.action);
}
```

Expected output:

```text
Create: User says something important
Use: Add it to the context for the next chat
Update: User says it changed, update the memory
Expire: It's old or no longer needed, remove it
```

## Memory Privacy and Rules

### Data Classification

Mark every memory entry with a privacy level:
- **Public**: can be shown to anyone. Example: "User name is Alice."
- **User-only**: shown only to Alice. Example: "Alice's phone number."
- **System-only**: shown only to the AI system. Example: "Alice uses admin mode."
- **Sensitive**: locked away, shown to nobody. Example: "Credit card number."

```js runnable title="Memory entries with privacy levels"
const memory = [
  { fact: "User name is Alice", privacy: "public" },
  { fact: "Alice's phone: 555-1234", privacy: "user-only" },
  { fact: "Alice uses admin mode", privacy: "system-only" },
  { fact: "Payment method: card ending 5678", privacy: "sensitive" },
];

for (const m of memory) {
  console.log(m.fact + " [" + m.privacy + "]");
}
```

Expected output:

```text
User name is Alice [public]
Alice's phone: 555-1234 [user-only]
Alice uses admin mode [system-only]
Payment method: card ending 5678 [sensitive]
```

### Redaction

Some facts have sensitive parts. Redact (hide) them:

**Bad:** `User phone: 555-1234`

**Better:** `User phone: ***-1234` (last 4 digits only)

**Better:** `User phone: [redacted], on file`

```js runnable title="Redact sensitive parts of facts"
const facts = [
  "Email: alice@company.com",
  "SSN: 123-45-6789",
  "Address: 123 Main St, City",
];

function redact(fact) {
  if (fact.includes("SSN:")) return fact.replace(/\d{3}-\d{2}-/, "***-**-");
  if (fact.includes("Email:")) return fact.replace(/@.*/, "@***");
  return fact;
}

console.log("Original facts:");
for (const f of facts) console.log("  " + f);
console.log("After redaction:");
for (const f of facts) console.log("  " + redact(f));
```

Expected output:

```text
Original facts:
  Email: alice@company.com
  SSN: 123-45-6789
  Address: 123 Main St, City
After redaction:
  Email: alice@***
  SSN: ***-**-6789
  Address: 123 Main St, City
```

Only the last part is kept. Enough to identify it, but the secret is hidden.

Never put one user's memory into another user's context. Filter memory by user in code, before the context is built.

## Optimization: Make Memory Compact

### Session Compression

After a long chat, make a short summary called a **session summary**:

**Wrong:** Save every message from a 100-message chat.

**Better:** Save a 2-sentence summary: "User asked about exporting, decided to stay on Free plan for now."

```js runnable title="Compress a long chat into a summary"
const chatMessages = [
  "Q: Can I export?",
  "A: Yes, to PDF and CSV.",
  "Q: How much does export cost?",
  "A: Free on all plans.",
  "Q: Should I upgrade?",
  "A: Only if you need more rows.",
];

const summary = "User asked about exporting (it's free) and whether to upgrade. Decided to stay on Free plan.";

console.log("Chat: " + chatMessages.length + " messages");
console.log("Summary: " + summary);
```

Expected output:

```text
Chat: 6 messages
Summary: User asked about exporting (it's free) and whether to upgrade. Decided to stay on Free plan.
```

When the next chat happens, add the summary, not all 6 messages. This keeps memory small and fast.

### Expiry Filters

When you pull memory for a new chat, drop facts that have expired:

```js runnable title="Filter out expired memory"
const today = "2026-01-15";
const memory = [
  { fact: "Allergic to peanuts", expires: "never" },
  { fact: "Looking for a job", expires: "2025-12-01" },
  { fact: "Waiting for report", expires: "2026-01-10" },
];

const active = memory.filter((m) => m.expires === "never" || m.expires >= today);

console.log("Still active:");
for (const m of active) {
  console.log("  - " + m.fact);
}
```

Expected output:

```text
Still active:
  - Allergic to peanuts
```

The two facts have expired (they're older than today). Only the allergy stays. This keeps the context clean.

## Common mistakes

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| Save everything | "Keep all messages, maybe we'll need them." | Make a summary, throw away old messages. | Memory gets huge, slow, and expensive. |
| No expiry | "Facts don't change." | Mark when facts expire. | Old facts are used forever. |
| No privacy | "All memory is the same." | Mark privacy: public, user-only, system-only, sensitive. | Private facts shown to the wrong person. |
| Show secrets | "Hide the number in the prompt." | Redact it, or don't store it. | Secret is in the memory anyway. |
| No updates | "Write it once, it's true forever." | Update when facts change. | Wrong or outdated facts are used. |
| Lost decisions | "The user will remember." | Pull decisions into memory as soon as made. | Decisions are forgotten or wrong. |

## Exercises

Each exercise ends with a value on the last line. Press **Check** to compare it with the expected answer.

## What to Remember

- **Short-term memory**: the last few messages, so the model knows what just happened.
- **Long-term memory**: big facts across many chats: name, goals, preferences.
- **State memory**: what's in progress now: order, task, workflow step.
- **Lifecycle**: create, use, update, expire. Mark expiry dates so memory doesn't grow forever.
- **Privacy**: label every fact. Redact sensitive parts. Show only to the right people.
- **Compress**: make session summaries for long chats. Keep memory small and fast.

Next: **Tool Connector Context**, how to pass results from tools and APIs into the context in a way the model understands.
