Skip to content

feat(managed_agents): add Managed Agents Memory Cookbook#573

Merged
gaganb-ant merged 8 commits into
mainfrom
claude/copy-memory-cookbook-pr-ZRx6N
Apr 27, 2026
Merged

feat(managed_agents): add Managed Agents Memory Cookbook#573
gaganb-ant merged 8 commits into
mainfrom
claude/copy-memory-cookbook-pr-ZRx6N

Conversation

@gaganb-ant

@gaganb-ant gaganb-ant commented Apr 25, 2026

Copy link
Copy Markdown
Contributor

Build agents that remember your users

Adds a guided tutorial to managed_agents/ showing how to give a Managed Agent a memory store so it learns and recalls a customer's shopping preferences across separate sessions.

Checkout the full notebook preview here.

A guided tutorial showing how to give a Managed Agent a memory store so
it learns and recalls a customer's shopping preferences across separate
sessions. Covers store creation, the resources attachment with
per-attachment instructions, inspecting and seeding memories from your
own application, and combining a per-customer read-write store with a
brand-wide read-only store.

https://claude.ai/code/session_01VkfVPdiJTWVh6Vj5owqRXH
@github-actions

Copy link
Copy Markdown

Notebook Changes

This PR modifies the following notebooks:

📓 managed_agents/CMA_remember_user_preferences.ipynb

View diff
nbdiff /dev/null managed_agents/CMA_remember_user_preferences.ipynb (3e44f8d442bb0ec5ceda082408cebaa5b94d4155)
--- /dev/null  2026-04-25 00:24:26.563882
+++ managed_agents/CMA_remember_user_preferences.ipynb (3e44f8d442bb0ec5ceda082408cebaa5b94d4155)  (no timestamp)
## added /cells:
+  markdown cell:
+    source:
+      # Build agents that remember your users
+      
+      Most agents start every conversation from scratch. A customer tells your shopping assistant their size, their budget, and which materials they avoid, and the next time they return, the agent has forgotten everything. They have to repeat themselves, and the experience feels transactional rather than personal.
+      
+      We just introduced **Memory** in Claude Managed Agents to solve this. A memory store is a persistent collection of text files that Claude can read and write during a session, and that survives across sessions. Anthropic hosts the storage, mounts it into the agent's environment, and prompts Claude on when and what to save — so there is no database, vector store, or retrieval pipeline for you to stand up. Attach the same store to every session for a given customer, and your agent accumulates knowledge about them over time.
+      
+      In this guide you will build a personal shopping assistant for a retail brand. The agent will learn a customer's preferences during their first visit, save them to a memory store, and recall them automatically on the next visit without being told again.
+      
+      ## What you will build
+      
+      - A **memory store** that holds one customer's shopping preferences
+      - A **shopping agent** configured to check and update that store
+      - **Two separate sessions** that demonstrate memory carrying across visits
+      - A pattern for **inspecting and seeding** memories from your own application
+      
+      ## How memory works
+      
+      When you attach a memory store to a session, it appears as a directory inside the agent's environment at `/mnt/memory/{store-name}`, and Claude reads and writes files there using its standard file tools. Your application has full read and write access to the same files through the REST API, so you can seed a store with known facts, audit what the agent has written, or export everything to your own systems.
+      
+      > **Beta feature.** Memory stores are part of the Claude Managed Agents public beta. The API may change before general availability. The Python SDK adds the required `anthropic-beta` header automatically for every method under `client.beta`.
+  markdown cell:
+    source:
+      ## Prerequisites
+      
+      - An Anthropic API key with access to the Claude Managed Agents beta. Set it as the `ANTHROPIC_API_KEY` environment variable.
+      - Python 3.11 or later.
+      - The Anthropic Python SDK. Memory store methods require a recent release:
+      
+      ```bash
+      pip install -U anthropic
+      ```
+  markdown cell:
+    source:
+      ## Set up the client
+  code cell:
+    source:
+      from anthropic import Anthropic
+      
+      client = Anthropic()
+  markdown cell:
+    source:
+      ### A helper for conversational turns
+      
+      Managed agent sessions are event-driven: you send a user message, then stream events until the session goes idle. To keep the rest of this guide readable, wrap that loop in a helper that prints the agent's replies and the files it touches under `/mnt/memory/`.
+  code cell:
+    source:
+      def run_turn(session_id: str, user_text: str) -> str:
+          """Send one user message and stream the agent's response until it goes idle."""
+          print(f"\n[user]  {user_text}")
+          reply_parts: list[str] = []
+      
+          with client.beta.sessions.events.stream(session_id) as stream:
+              client.beta.sessions.events.send(
+                  session_id,
+                  events=[
+                      {
+                          "type": "user.message",
+                          "content": [{"type": "text", "text": user_text}],
+                      }
+                  ],
+              )
+              for event in stream:
+                  if event.type == "agent.message":
+                      for block in event.content:
+                          if block.type == "text":
+                              reply_parts.append(block.text)
+                              print(f"[agent] {block.text}")
+      
+                  elif event.type == "agent.tool_use":
+                      # Surface reads and writes to the memory mount so you can see
+                      # the agent checking and updating its memory.
+                      target = event.input.get("file_path") or event.input.get("command", "")
+                      if "/mnt/memory/" in str(target):
+                          print(f"  [memory] {event.name}: {target}")
+      
+                  elif event.type == "session.status_idle":
+                      break
+      
+          return "".join(reply_parts)
+  markdown cell:
+    source:
+      ## Step 1: Create a memory store
+      
+      A memory store is a named container for text files, scoped to your workspace. In a production deployment you would typically create one store per end user and keep a mapping from your user IDs to store IDs in your own database.
+      
+      The `description` you set here is rendered into the agent's system prompt whenever the store is attached, so use it to tell Claude what the store is for.
+  code cell:
+    source:
+      store = client.beta.memory_stores.create(
+          name="Shopper Preferences",
+          description=(
+              "Personal shopping preferences for a single customer: "
+              "sizes, style, budget, favorite brands, and materials to avoid."
+          ),
+      )
+      
+      print(store.id)  # memstore_01...
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          memstore_01NKkumXZXY8mEhoRA3xhBvN
+  markdown cell:
+    source:
+      ## Step 2: Define the shopping agent
+      
+      Every managed agent session needs an **agent** (the model, system prompt, and tools) and an **environment** (the container the agent runs in). You can create these once and reuse them across many customers and sessions.
+      
+      Give the agent the built-in `agent_toolset`, which includes the file tools it uses to read and write memory.
+  code cell:
+    source:
+      environment = client.beta.environments.create(
+          name="shopping-demo",
+      )
+      
+      agent = client.beta.agents.create(
+          name="Personal Shopper",
+          model="claude-sonnet-4-6",
+          system=(
+              "You are a personal shopping assistant for a retail brand. "
+              "Help the customer find products that match their taste and budget, "
+              "and remember what you learn about them for future visits."
+          ),
+          tools=[{"type": "agent_toolset_20260401"}],
+      )
+      
+      print(agent.id)  # agent_01...
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          agent_011CaMbJoHMUyR5TjeFDtMG9
+  markdown cell:
+    source:
+      ## Step 3: First visit, where the agent learns preferences
+      
+      Now start a session and attach the memory store through the `resources` array. The `instructions` field is per-attachment guidance that tells Claude how to use this particular store in this particular session.
+      
+      Because this is the customer's first visit, the store is empty. Watch the `[memory]` lines in the output: the agent checks the store, finds nothing yet, and then writes a new file capturing what it learned.
+  code cell:
+    source:
+      memory_resource = {
+          "type": "memory_store",
+          "memory_store_id": store.id,
+          "access": "read_write",
+          "instructions": (
+              "This customer's personal preferences: sizes, style, budget, and "
+              "materials to avoid. Check it at the start of every conversation "
+              "and update it whenever you learn something new."
+          ),
+      }
+      
+      session_one = client.beta.sessions.create(
+          agent=agent.id,
+          environment_id=environment.id,
+          resources=[memory_resource],
+      )
+      
+      # The response tells you where the store is mounted inside the agent's environment.
+      for resource in session_one.resources:
+          if resource.type == "memory_store":
+              print(f"Mounted at {resource.mount_path}")
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          Mounted at /mnt/memory/shopper-preferences
+  code cell:
+    source:
+      run_turn(
+          session_one.id,
+          "Hi! I'm looking for a new jacket. A few things about me: I wear a size "
+          "medium, I only buy vegan leather (no animal leather please), my budget "
+          "is usually under $200, and I love earth tones. What would you suggest?",
+      )
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          [user]  Hi! I'm looking for a new jacket. A few things about me: I wear a size medium, I only buy vegan leather (no animal leather please), my budget is usually under $200, and I love earth tones. What would you suggest?
+          [agent] Let me check if I have any info about you on file, and save your preferences right away!
+            [memory] bash: cat /mnt/memory/shopper-preferences 2>/dev/null || echo "No file found"
+            [memory] write: /mnt/memory/shopper-preferences
+            [memory] write: /mnt/memory/shopper-preferences/preferences.md
+          [agent] I've saved your preferences for future visits! Now, here are some great **vegan leather jacket picks in earth tones under $200** that I'd suggest:
+          
+          ---
+          
+          ### 🌿 Top Picks for You
+          
+          #### 1. **Olive Moto Jacket** — ~$110–$150
+          A classic biker silhouette in a deep **olive green** faux leather. Look for styles with zip-detail sleeves and a fitted waist. Great for casual or edgy looks.
+          - *Why you'll love it:* Earthy tone, very on-trend, versatile for layering.
+          
+          #### 2. **Camel/Tan Oversized Blazer-Jacket** — ~$80–$160
+          A structured vegan leather blazer in **camel or tan** gives a polished, sophisticated vibe. Works dressed up or down.
+          - *Why you'll love it:* Classic earth tone, medium sizing runs well in this style, timeless investment.
+          
+          #### 3. **Rust/Cognac Bomber Jacket** — ~$90–$170
+          A **rust or cognac-toned** vegan leather bomber is a bold earth-tone statement piece with a relaxed, comfortable fit.
+          - *Why you'll love it:* Rich warm color, bomber silhouettes are flattering in medium, cozy feel.
+          
+          #### 4. **Terracotta Cropped Jacket** — ~$75–$140
+          A cropped vegan leather jacket in **terracotta** pairs beautifully with high-waisted jeans or wide-leg trousers.
+          - *Why you'll love it:* Trendy silhouette, stunning earthy hue, well within budget.
+          
+          ---
+          
+          ### 🛍️ Brands to Check Out
+          Here are some brands known for quality vegan leather within your budget:
+          - **ASOS** – Wide range of faux leather styles in various earth tones
+          - **Free People** – Boho-leaning earth tone options
+          - **H&M** – Affordable vegan leather jackets in seasonal colors
+          - **Lulus** – Great selection of trendy vegan styles under $100
+          - **Noize** – A dedicated vegan outerwear brand, often has earthy tones under $200
+          
+          ---
+          
+          Do any of these styles appeal to you? I can help narrow it down further — for example, do you prefer a **fitted or relaxed fit**, or a particular style like moto, bomber, or blazer? 😊
+  markdown cell:
+    source:
+      ## Step 4: Inspect what the agent saved
+      
+      Everything the agent writes to the mount is a regular memory document that your application can read, edit, or delete through the API. List the store's contents with `view="full"` to include file content in the response.
+      
+      This is how you build a "What we know about you" page in your product, sync memories into your own database, or let a human reviewer correct something the agent got wrong.
+  code cell:
+    source:
+      page = client.beta.memory_stores.memories.list(
+          store.id,
+          view="full",
+      )
+      
+      for memory in page.data:
+          if memory.type == "memory":
+              print(f"=== {memory.path} ===")
+              print(memory.content)
+              print()
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          === /preferences.md ===
+          # Shopper Preferences
+          
+          ## Sizes
+          - Tops/Jackets: Medium
+          
+          ## Style
+          - Loves earth tones (browns, tans, olive, terracotta, camel, rust, etc.)
+          - Interested in jackets
+          
+          ## Budget
+          - Usually under $200
+          
+          ## Materials
+          - VEGAN LEATHER ONLY — absolutely no animal leather
+          - (No other material restrictions noted yet)
+          
+          ## Favorite Brands
+          - None noted yet
+          
+          ## Other Notes
+          - First visit; preferences collected 2026-04-23
+  markdown cell:
+    source:
+      You should see a file (typically something like `/preferences.md`) containing the size, budget, material, and color preferences the customer mentioned, organized by topic. Claude chose the filename and structure on its own.
+  markdown cell:
+    source:
+      ## Step 5: Return visit, where the agent recalls on its own
+      
+      This is the moment that matters. Create a **brand new session** and attach the **same memory store**. The customer does not repeat any of their preferences, but the agent reads them from memory and tailors its recommendations accordingly.
+  code cell:
+    source:
+      session_two = client.beta.sessions.create(
+          agent=agent.id,
+          environment_id=environment.id,
+          resources=[memory_resource],  # same store, new session
+      )
+      
+      reply = run_turn(
+          session_two.id,
+          "Hey, I'm back! I need a bag for work. Any recommendations?",
+      )
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          [user]  Hey, I'm back! I need a bag for work. Any recommendations?
+          [agent] Welcome back! Let me check your preferences before making any recommendations!
+            [memory] read: /mnt/memory/shopper-preferences
+            [memory] bash: ls /mnt/memory/shopper-preferences/
+            [memory] read: /mnt/memory/shopper-preferences/preferences.md
+          [agent] Great news — I already have your preferences on file! Here's what I kept in mind for you:
+          
+          - 🎨 **Style:** Earth tones (browns, tans, olive, camel, etc.)
+          - 💰 **Budget:** Under $200
+          - 🌱 **Materials:** Vegan leather only — no animal leather
+          
+          With that in mind, here are some work bag recommendations that would suit you perfectly:
+          
+          ---
+          
+          ### 🛍️ Top Picks for a Work Bag
+          
+          1. **Structured Tote in Camel Vegan Leather** — A polished, professional look in a warm earth tone. Fits a laptop + daily essentials. Great desk-to-commute bag.
+          
+          2. **Olive Canvas & Vegan Leather Trim Laptop Bag** — A sleek crossbody/shoulder bag with a relaxed but put-together vibe. The canvas keeps it lightweight.
+          
+          3. **Tan Vegan Leather Satchel** — A classic work satchel with a top handle and shoulder strap. Timeless silhouette, stays under budget.
+          
+          4. **Terracotta Faux-Leather Tote** — A bold but grounded earth tone that adds personality to a work outfit without being over the top.
+          
+          ---
+          
+          Do any of these styles appeal to you? I can also ask a few quick questions to narrow it down further:
+          
+          - **Do you prefer a tote, backpack, crossbody, or briefcase-style?**
+          - **Do you need to carry a laptop, and if so, what size?**
+          - **Do you lean more structured/professional or relaxed/casual for work?**
+  markdown cell:
+    source:
+      In the output, notice that the agent reads `/mnt/memory/shopper-preferences/` before answering, then recommends bags that are vegan leather, in earth tones, and under $200, even though this message mentioned none of those things. The preferences carried across from the first session.
+  markdown cell:
+    source:
+      ## Going further: patterns for production
+      
+      ### Seed a store from your existing data
+      
+      If you already know things about a customer from their account profile or purchase history, you can write them into the store before the first session so the agent starts informed.
+  code cell:
+    source:
+      client.beta.memory_stores.memories.create(
+          store.id,
+          path="/purchase-history.md",
+          content=(
+              "## Recent purchases\n"
+              "- Canvas tote, olive, $89 (Jan 2026)\n"
+              "- Wool beanie, rust, $34 (Dec 2025)\n"
+          ),
+      )
+  markdown cell:
+    source:
+      ### Combine per-customer and shared stores
+      
+      A session can attach up to eight memory stores, each with its own access level. A common pattern is one read-write store per customer plus a read-only store of brand-wide knowledge that every session shares.
+      
+      ```python
+      catalog = client.beta.memory_stores.create(
+          name="Product Catalog Notes",
+          description="Current promotions, sizing guidance, and stock notes.",
+      )
+      
+      session = client.beta.sessions.create(
+          agent=agent.id,
+          environment_id=environment.id,
+          resources=[
+              {
+                  "type": "memory_store",
+                  "memory_store_id": store.id,
+                  "access": "read_write",
+                  "instructions": "This customer's personal preferences.",
+              },
+              {
+                  "type": "memory_store",
+                  "memory_store_id": catalog.id,
+                  "access": "read_only",
+                  "instructions": "Brand-wide product guidance. Consult before recommending items.",
+              },
+          ],
+      )
+      ```
+      
+      ### Audit and correct
+      
+      Every write to a memory store is recorded as an immutable version with the session that made it. Use `client.beta.memory_stores.memory_versions.list(...)` to review history, and `client.beta.memory_stores.memories.update(...)` to correct a file that the agent got wrong. See the [Memory API reference](https://docs.anthropic.com/en/docs/managed-agents/memory) for the full surface.
+  markdown cell:
+    source:
+      ## Clean up
+      
+      Delete the resources you created while following this guide.
+  code cell:
+    source:
+      client.beta.sessions.archive(session_one.id)
+      client.beta.sessions.archive(session_two.id)
+      client.beta.memory_stores.delete(store.id)
+      client.beta.agents.archive(agent.id)
+      client.beta.environments.delete(environment.id)
+  markdown cell:
+    source:
+      ## Summary
+      
+      You built a shopping agent that remembers its customers across visits by:
+      
+      1. Creating a memory store for the customer
+      2. Attaching it to each session through `resources`
+      3. Letting Claude read and write files under `/mnt/memory/` as it learned new preferences
+      4. Inspecting those files from your own application through the Memory API
+      
+      From here you can map your own user IDs to memory store IDs, seed stores from your existing customer data, and layer shared read-only stores on top for brand-wide knowledge.
+      
+      ### Learn more
+      
+      - [Claude Managed Agents overview](https://docs.anthropic.com/en/docs/managed-agents/overview)
+      - [Memory stores API reference](https://docs.anthropic.com/en/docs/managed-agents/memory)
+      - [Session resources](https://docs.anthropic.com/en/docs/managed-agents/memory#attach-a-memory-store-to-a-session)

Generated by nbdime

@github-actions

github-actions Bot commented Apr 25, 2026

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review

Recommendation: REQUEST_CHANGES

Summary

This adds a well-written memory store cookbook demonstrating a shopping agent that learns and recalls customer preferences across sessions. The writing is clear and pedagogically solid. However, several issues with the event-loop pattern, cleanup, and series conventions need fixing before merge.

Actionable Feedback (5 items)
  • CMA_remember_user_preferences.ipynb (in run_turn, the elif event.type == "session.status_idle": break block) — The idle break omits the stop_reason guard. The correct pattern (used in utilities.py and every other CMA notebook) is if event.stop_reason and event.stop_reason.type == "end_turn": break. Without this, a requires_action idle event (e.g. a tool confirmation) would silently exit the loop mid-turn.

  • CMA_remember_user_preferences.ipynb (cleanup cell with client.beta.sessions.archive(...)) — wait_for_idle_status(client, session.id) must be called before each sessions.archive(). The server-side status can briefly remain running after the stream closes, causing a non-deterministic 400. All other notebooks in the series do this.

  • CMA_remember_user_preferences.ipynb (cleanup cell: client.beta.environments.delete(environment.id)) — Should be client.beta.environments.archive(environment.id) to match the series convention. The operate notebook explicitly explains why archive is preferred over delete for most workflows.

  • CMA_remember_user_preferences.ipynb (Step 2 cell: model="claude-sonnet-4-6") — All other CMA notebooks define MODEL = os.environ.get("COOKBOOK_MODEL", "claude-sonnet-4-6") in the setup cell and reference MODEL inline. This allows CI and users to swap models without hunting through cells. Add import os and the MODEL constant, then replace the inline string.

  • CMA_remember_user_preferences.ipynb (Prerequisites markdown cell: pip install -U anthropic) — The install instruction is in a markdown fenced block rather than a runnable %pip install -q -U anthropic code cell. Either add a runnable cell or remove the instruction and rely on the directory's pyproject.toml (matching CMA_iterate_fix_failing_tests.ipynb).

Detailed Review

Code Quality

The run_turn helper re-implements the event loop from scratch rather than wrapping utilities.stream_until_end_turn. The memory-filter logic is a nice demo touch, but doing a full re-implementation means it won't benefit from future fixes to utilities.py, and it carries the stop_reason bug noted above. Consider either wrapping the utilities helper with the memory filter on top, or at minimum ensure the stop condition matches.

The reply = run_turn(...) assignment in the session two cell is never used afterward. Either drop it or add a brief note explaining what a caller would do with it.

The agent_toolset_20260401 dated type string is correct for the current API, but a one-sentence note explaining that toolset types are versioned differently from model aliases would preempt reader confusion.

Security

No issues. No API keys are hardcoded. The notebook uses Anthropic() (picks up ANTHROPIC_API_KEY from the environment), which is consistent with the rest of the CMA series even if it diverges slightly from the repo-wide load_dotenv() pattern.

Suggestions

  • Drop or explain the unused reply = assignment in the session two cell.
  • Add a brief inline comment on agent_toolset_20260401 noting that toolset types carry explicit version dates (unlike model aliases).

Positive Notes

  • Introduction leads with the user problem, not the machinery — exactly the right structure.
  • The [memory] trace in run_turn output is a great teaching aid; seeing the agent read and write memory paths makes the concept concrete.
  • Model string "claude-sonnet-4-6" is the correct non-dated alias.
  • Registry entry has all required fields and the README table row matches the series style precisely.
  • The "Going further" section (catalog store pattern, audit/correct pointer) is well-scoped and production-relevant without sprawling.
  • Outputs are kept in the notebook per repo convention and the shown outputs are realistic and demonstrate the feature well.
  • The beta callout block is well-placed and accurate.

- Prefer uv add over pip in the prerequisites
- Clarify that the agent_toolset_20260401 type string identifies the
  toolset, not a model, and does not need bumping
- Drop unused reply variable from the second run_turn call

Intentionally skip the dotenv suggestion: the other managed_agents
notebooks set up the client without load_dotenv, so adding it here
would be the inconsistent choice.

https://claude.ai/code/session_01VkfVPdiJTWVh6Vj5owqRXH
@github-actions

Copy link
Copy Markdown

Notebook Changes

This PR modifies the following notebooks:

📓 managed_agents/CMA_remember_user_preferences.ipynb

View diff
nbdiff /dev/null managed_agents/CMA_remember_user_preferences.ipynb (1016f8f3527b8170e68a21c8396a7adfaf88cab2)
--- /dev/null  2026-04-25 00:41:14.552542
+++ managed_agents/CMA_remember_user_preferences.ipynb (1016f8f3527b8170e68a21c8396a7adfaf88cab2)  (no timestamp)
## added /cells:
+  markdown cell:
+    source:
+      # Build agents that remember your users
+      
+      Most agents start every conversation from scratch. A customer tells your shopping assistant their size, their budget, and which materials they avoid, and the next time they return, the agent has forgotten everything. They have to repeat themselves, and the experience feels transactional rather than personal.
+      
+      We just introduced **Memory** in Claude Managed Agents to solve this. A memory store is a persistent collection of text files that Claude can read and write during a session, and that survives across sessions. Anthropic hosts the storage, mounts it into the agent's environment, and prompts Claude on when and what to save — so there is no database, vector store, or retrieval pipeline for you to stand up. Attach the same store to every session for a given customer, and your agent accumulates knowledge about them over time.
+      
+      In this guide you will build a personal shopping assistant for a retail brand. The agent will learn a customer's preferences during their first visit, save them to a memory store, and recall them automatically on the next visit without being told again.
+      
+      ## What you will build
+      
+      - A **memory store** that holds one customer's shopping preferences
+      - A **shopping agent** configured to check and update that store
+      - **Two separate sessions** that demonstrate memory carrying across visits
+      - A pattern for **inspecting and seeding** memories from your own application
+      
+      ## How memory works
+      
+      When you attach a memory store to a session, it appears as a directory inside the agent's environment at `/mnt/memory/{store-name}`, and Claude reads and writes files there using its standard file tools. Your application has full read and write access to the same files through the REST API, so you can seed a store with known facts, audit what the agent has written, or export everything to your own systems.
+      
+      > **Beta feature.** Memory stores are part of the Claude Managed Agents public beta. The API may change before general availability. The Python SDK adds the required `anthropic-beta` header automatically for every method under `client.beta`.
+  markdown cell:
+    source:
+      ## Prerequisites
+      
+      - An Anthropic API key with access to the Claude Managed Agents beta. Set it as the `ANTHROPIC_API_KEY` environment variable.
+      - Python 3.11 or later.
+      - The Anthropic Python SDK. Memory store methods require a recent release:
+      
+      ```bash
+      uv add anthropic
+      # or: pip install -U anthropic
+      ```
+  markdown cell:
+    source:
+      ## Set up the client
+  code cell:
+    source:
+      from anthropic import Anthropic
+      
+      client = Anthropic()
+  markdown cell:
+    source:
+      ### A helper for conversational turns
+      
+      Managed agent sessions are event-driven: you send a user message, then stream events until the session goes idle. To keep the rest of this guide readable, wrap that loop in a helper that prints the agent's replies and the files it touches under `/mnt/memory/`.
+  code cell:
+    source:
+      def run_turn(session_id: str, user_text: str) -> str:
+          """Send one user message and stream the agent's response until it goes idle."""
+          print(f"\n[user]  {user_text}")
+          reply_parts: list[str] = []
+      
+          with client.beta.sessions.events.stream(session_id) as stream:
+              client.beta.sessions.events.send(
+                  session_id,
+                  events=[
+                      {
+                          "type": "user.message",
+                          "content": [{"type": "text", "text": user_text}],
+                      }
+                  ],
+              )
+              for event in stream:
+                  if event.type == "agent.message":
+                      for block in event.content:
+                          if block.type == "text":
+                              reply_parts.append(block.text)
+                              print(f"[agent] {block.text}")
+      
+                  elif event.type == "agent.tool_use":
+                      # Surface reads and writes to the memory mount so you can see
+                      # the agent checking and updating its memory.
+                      target = event.input.get("file_path") or event.input.get("command", "")
+                      if "/mnt/memory/" in str(target):
+                          print(f"  [memory] {event.name}: {target}")
+      
+                  elif event.type == "session.status_idle":
+                      break
+      
+          return "".join(reply_parts)
+  markdown cell:
+    source:
+      ## Step 1: Create a memory store
+      
+      A memory store is a named container for text files, scoped to your workspace. In a production deployment you would typically create one store per end user and keep a mapping from your user IDs to store IDs in your own database.
+      
+      The `description` you set here is rendered into the agent's system prompt whenever the store is attached, so use it to tell Claude what the store is for.
+  code cell:
+    source:
+      store = client.beta.memory_stores.create(
+          name="Shopper Preferences",
+          description=(
+              "Personal shopping preferences for a single customer: "
+              "sizes, style, budget, favorite brands, and materials to avoid."
+          ),
+      )
+      
+      print(store.id)  # memstore_01...
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          memstore_01NKkumXZXY8mEhoRA3xhBvN
+  markdown cell:
+    source:
+      ## Step 2: Define the shopping agent
+      
+      Every managed agent session needs an **agent** (the model, system prompt, and tools) and an **environment** (the container the agent runs in). You can create these once and reuse them across many customers and sessions.
+      
+      Give the agent the built-in `agent_toolset`, which includes the file tools it uses to read and write memory. The `agent_toolset_20260401` type string is the toolset's API identifier, not a model alias, so you do not need to update it when newer models ship.
+  code cell:
+    source:
+      environment = client.beta.environments.create(
+          name="shopping-demo",
+      )
+      
+      agent = client.beta.agents.create(
+          name="Personal Shopper",
+          model="claude-sonnet-4-6",
+          system=(
+              "You are a personal shopping assistant for a retail brand. "
+              "Help the customer find products that match their taste and budget, "
+              "and remember what you learn about them for future visits."
+          ),
+          tools=[{"type": "agent_toolset_20260401"}],
+      )
+      
+      print(agent.id)  # agent_01...
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          agent_011CaMbJoHMUyR5TjeFDtMG9
+  markdown cell:
+    source:
+      ## Step 3: First visit, where the agent learns preferences
+      
+      Now start a session and attach the memory store through the `resources` array. The `instructions` field is per-attachment guidance that tells Claude how to use this particular store in this particular session.
+      
+      Because this is the customer's first visit, the store is empty. Watch the `[memory]` lines in the output: the agent checks the store, finds nothing yet, and then writes a new file capturing what it learned.
+  code cell:
+    source:
+      memory_resource = {
+          "type": "memory_store",
+          "memory_store_id": store.id,
+          "access": "read_write",
+          "instructions": (
+              "This customer's personal preferences: sizes, style, budget, and "
+              "materials to avoid. Check it at the start of every conversation "
+              "and update it whenever you learn something new."
+          ),
+      }
+      
+      session_one = client.beta.sessions.create(
+          agent=agent.id,
+          environment_id=environment.id,
+          resources=[memory_resource],
+      )
+      
+      # The response tells you where the store is mounted inside the agent's environment.
+      for resource in session_one.resources:
+          if resource.type == "memory_store":
+              print(f"Mounted at {resource.mount_path}")
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          Mounted at /mnt/memory/shopper-preferences
+  code cell:
+    source:
+      run_turn(
+          session_one.id,
+          "Hi! I'm looking for a new jacket. A few things about me: I wear a size "
+          "medium, I only buy vegan leather (no animal leather please), my budget "
+          "is usually under $200, and I love earth tones. What would you suggest?",
+      )
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          [user]  Hi! I'm looking for a new jacket. A few things about me: I wear a size medium, I only buy vegan leather (no animal leather please), my budget is usually under $200, and I love earth tones. What would you suggest?
+          [agent] Let me check if I have any info about you on file, and save your preferences right away!
+            [memory] bash: cat /mnt/memory/shopper-preferences 2>/dev/null || echo "No file found"
+            [memory] write: /mnt/memory/shopper-preferences
+            [memory] write: /mnt/memory/shopper-preferences/preferences.md
+          [agent] I've saved your preferences for future visits! Now, here are some great **vegan leather jacket picks in earth tones under $200** that I'd suggest:
+          
+          ---
+          
+          ### 🌿 Top Picks for You
+          
+          #### 1. **Olive Moto Jacket** — ~$110–$150
+          A classic biker silhouette in a deep **olive green** faux leather. Look for styles with zip-detail sleeves and a fitted waist. Great for casual or edgy looks.
+          - *Why you'll love it:* Earthy tone, very on-trend, versatile for layering.
+          
+          #### 2. **Camel/Tan Oversized Blazer-Jacket** — ~$80–$160
+          A structured vegan leather blazer in **camel or tan** gives a polished, sophisticated vibe. Works dressed up or down.
+          - *Why you'll love it:* Classic earth tone, medium sizing runs well in this style, timeless investment.
+          
+          #### 3. **Rust/Cognac Bomber Jacket** — ~$90–$170
+          A **rust or cognac-toned** vegan leather bomber is a bold earth-tone statement piece with a relaxed, comfortable fit.
+          - *Why you'll love it:* Rich warm color, bomber silhouettes are flattering in medium, cozy feel.
+          
+          #### 4. **Terracotta Cropped Jacket** — ~$75–$140
+          A cropped vegan leather jacket in **terracotta** pairs beautifully with high-waisted jeans or wide-leg trousers.
+          - *Why you'll love it:* Trendy silhouette, stunning earthy hue, well within budget.
+          
+          ---
+          
+          ### 🛍️ Brands to Check Out
+          Here are some brands known for quality vegan leather within your budget:
+          - **ASOS** – Wide range of faux leather styles in various earth tones
+          - **Free People** – Boho-leaning earth tone options
+          - **H&M** – Affordable vegan leather jackets in seasonal colors
+          - **Lulus** – Great selection of trendy vegan styles under $100
+          - **Noize** – A dedicated vegan outerwear brand, often has earthy tones under $200
+          
+          ---
+          
+          Do any of these styles appeal to you? I can help narrow it down further — for example, do you prefer a **fitted or relaxed fit**, or a particular style like moto, bomber, or blazer? 😊
+  markdown cell:
+    source:
+      ## Step 4: Inspect what the agent saved
+      
+      Everything the agent writes to the mount is a regular memory document that your application can read, edit, or delete through the API. List the store's contents with `view="full"` to include file content in the response.
+      
+      This is how you build a "What we know about you" page in your product, sync memories into your own database, or let a human reviewer correct something the agent got wrong.
+  code cell:
+    source:
+      page = client.beta.memory_stores.memories.list(
+          store.id,
+          view="full",
+      )
+      
+      for memory in page.data:
+          if memory.type == "memory":
+              print(f"=== {memory.path} ===")
+              print(memory.content)
+              print()
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          === /preferences.md ===
+          # Shopper Preferences
+          
+          ## Sizes
+          - Tops/Jackets: Medium
+          
+          ## Style
+          - Loves earth tones (browns, tans, olive, terracotta, camel, rust, etc.)
+          - Interested in jackets
+          
+          ## Budget
+          - Usually under $200
+          
+          ## Materials
+          - VEGAN LEATHER ONLY — absolutely no animal leather
+          - (No other material restrictions noted yet)
+          
+          ## Favorite Brands
+          - None noted yet
+          
+          ## Other Notes
+          - First visit; preferences collected 2026-04-23
+  markdown cell:
+    source:
+      You should see a file (typically something like `/preferences.md`) containing the size, budget, material, and color preferences the customer mentioned, organized by topic. Claude chose the filename and structure on its own.
+  markdown cell:
+    source:
+      ## Step 5: Return visit, where the agent recalls on its own
+      
+      This is the moment that matters. Create a **brand new session** and attach the **same memory store**. The customer does not repeat any of their preferences, but the agent reads them from memory and tailors its recommendations accordingly.
+  code cell:
+    source:
+      session_two = client.beta.sessions.create(
+          agent=agent.id,
+          environment_id=environment.id,
+          resources=[memory_resource],  # same store, new session
+      )
+      
+      run_turn(
+          session_two.id,
+          "Hey, I'm back! I need a bag for work. Any recommendations?",
+      )
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          [user]  Hey, I'm back! I need a bag for work. Any recommendations?
+          [agent] Welcome back! Let me check your preferences before making any recommendations!
+            [memory] read: /mnt/memory/shopper-preferences
+            [memory] bash: ls /mnt/memory/shopper-preferences/
+            [memory] read: /mnt/memory/shopper-preferences/preferences.md
+          [agent] Great news — I already have your preferences on file! Here's what I kept in mind for you:
+          
+          - 🎨 **Style:** Earth tones (browns, tans, olive, camel, etc.)
+          - 💰 **Budget:** Under $200
+          - 🌱 **Materials:** Vegan leather only — no animal leather
+          
+          With that in mind, here are some work bag recommendations that would suit you perfectly:
+          
+          ---
+          
+          ### 🛍️ Top Picks for a Work Bag
+          
+          1. **Structured Tote in Camel Vegan Leather** — A polished, professional look in a warm earth tone. Fits a laptop + daily essentials. Great desk-to-commute bag.
+          
+          2. **Olive Canvas & Vegan Leather Trim Laptop Bag** — A sleek crossbody/shoulder bag with a relaxed but put-together vibe. The canvas keeps it lightweight.
+          
+          3. **Tan Vegan Leather Satchel** — A classic work satchel with a top handle and shoulder strap. Timeless silhouette, stays under budget.
+          
+          4. **Terracotta Faux-Leather Tote** — A bold but grounded earth tone that adds personality to a work outfit without being over the top.
+          
+          ---
+          
+          Do any of these styles appeal to you? I can also ask a few quick questions to narrow it down further:
+          
+          - **Do you prefer a tote, backpack, crossbody, or briefcase-style?**
+          - **Do you need to carry a laptop, and if so, what size?**
+          - **Do you lean more structured/professional or relaxed/casual for work?**
+  markdown cell:
+    source:
+      In the output, notice that the agent reads `/mnt/memory/shopper-preferences/` before answering, then recommends bags that are vegan leather, in earth tones, and under $200, even though this message mentioned none of those things. The preferences carried across from the first session.
+  markdown cell:
+    source:
+      ## Going further: patterns for production
+      
+      ### Seed a store from your existing data
+      
+      If you already know things about a customer from their account profile or purchase history, you can write them into the store before the first session so the agent starts informed.
+  code cell:
+    source:
+      client.beta.memory_stores.memories.create(
+          store.id,
+          path="/purchase-history.md",
+          content=(
+              "## Recent purchases\n"
+              "- Canvas tote, olive, $89 (Jan 2026)\n"
+              "- Wool beanie, rust, $34 (Dec 2025)\n"
+          ),
+      )
+  markdown cell:
+    source:
+      ### Combine per-customer and shared stores
+      
+      A session can attach up to eight memory stores, each with its own access level. A common pattern is one read-write store per customer plus a read-only store of brand-wide knowledge that every session shares.
+      
+      ```python
+      catalog = client.beta.memory_stores.create(
+          name="Product Catalog Notes",
+          description="Current promotions, sizing guidance, and stock notes.",
+      )
+      
+      session = client.beta.sessions.create(
+          agent=agent.id,
+          environment_id=environment.id,
+          resources=[
+              {
+                  "type": "memory_store",
+                  "memory_store_id": store.id,
+                  "access": "read_write",
+                  "instructions": "This customer's personal preferences.",
+              },
+              {
+                  "type": "memory_store",
+                  "memory_store_id": catalog.id,
+                  "access": "read_only",
+                  "instructions": "Brand-wide product guidance. Consult before recommending items.",
+              },
+          ],
+      )
+      ```
+      
+      ### Audit and correct
+      
+      Every write to a memory store is recorded as an immutable version with the session that made it. Use `client.beta.memory_stores.memory_versions.list(...)` to review history, and `client.beta.memory_stores.memories.update(...)` to correct a file that the agent got wrong. See the [Memory API reference](https://docs.anthropic.com/en/docs/managed-agents/memory) for the full surface.
+  markdown cell:
+    source:
+      ## Clean up
+      
+      Delete the resources you created while following this guide.
+  code cell:
+    source:
+      client.beta.sessions.archive(session_one.id)
+      client.beta.sessions.archive(session_two.id)
+      client.beta.memory_stores.delete(store.id)
+      client.beta.agents.archive(agent.id)
+      client.beta.environments.delete(environment.id)
+  markdown cell:
+    source:
+      ## Summary
+      
+      You built a shopping agent that remembers its customers across visits by:
+      
+      1. Creating a memory store for the customer
+      2. Attaching it to each session through `resources`
+      3. Letting Claude read and write files under `/mnt/memory/` as it learned new preferences
+      4. Inspecting those files from your own application through the Memory API
+      
+      From here you can map your own user IDs to memory store IDs, seed stores from your existing customer data, and layer shared read-only stores on top for brand-wide knowledge.
+      
+      ### Learn more
+      
+      - [Claude Managed Agents overview](https://docs.anthropic.com/en/docs/managed-agents/overview)
+      - [Memory stores API reference](https://docs.anthropic.com/en/docs/managed-agents/memory)
+      - [Session resources](https://docs.anthropic.com/en/docs/managed-agents/memory#attach-a-memory-store-to-a-session)

Generated by nbdime

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review

Recommendation: REQUEST_CHANGES

Summary

This notebook adds a clear, well-motivated tutorial for the Managed Agents memory store feature. The prose is excellent, the scenario is concrete, and the [memory] trace lines in the event loop are a great pedagogical touch. A few functional and pedagogical issues need to be addressed before merge.

Actionable Feedback (6 items)
  • CMA_remember_user_preferences.ipynb (in run_turn, elif event.type == "session.status_idle": break) — The unconditional break on session.status_idle is a pedagogical hazard: readers will copy this pattern into code that uses custom tools, where the same event fires on requires_action mid-turn, and the loop will exit prematurely. The established idiom across the CMA series is to check stop_reason.type == "end_turn" before exiting. Even though agent_toolset can't emit requires_action here, the pattern taught matters.

  • CMA_remember_user_preferences.ipynb (cleanup cell with archive() calls) — There is a known race between the session.status_idle SSE event and the server-side status field settling; calling archive() immediately after streaming can 400 with "cannot be archived while status is running." The sibling notebooks use a wait_for_idle_status helper for exactly this reason. Add that helper call or a short time.sleep(1.0) with a comment before each archive().

  • CMA_remember_user_preferences.ipynb (seed cell, "Seed a store from your existing data") — The seed cell writes into store, which is the same store that the cleanup cell deletes. On a second run (or after cleanup), store.id is stale and the call 404s. Either move the seed cell before session creation (which is the correct production workflow anyway — seed before first session, not after), or add a comment warning that it must run before cleanup.

  • CMA_remember_user_preferences.ipynb (agent creation cell) — Hard-codes model="claude-sonnet-4-6" instead of the MODEL = os.environ.get("COOKBOOK_MODEL", "claude-sonnet-4-6") constant used by every other CMA notebook in this folder. Add import os and define/use the constant so users can override the model without editing the notebook body.

  • CMA_remember_user_preferences.ipynb (seed cell output) — The memories.create() call produces no output (cell shows empty outputs), making it look like it failed after all the verbose feedback in earlier cells. Add print(f"Seeded {memory.path}") using the returned memory object.

  • CMA_remember_user_preferences.ipynb (run_turn signature is -> str but both call sites discard the return value) — Either change to -> None to accurately reflect the demo's printing-as-side-effect design, or capture the result at the call sites to show programmatic usage. The current -> str signature implies value consumers that are never shown.

Detailed Review

Code Quality

The helper function structure is clean and the memory_resource dict being defined once and reused across both sessions is a good pattern. The narrative flow (learn → inspect → recall) is logical and progressive. The markdown-only "Combine stores" section is fine as illustrative pseudocode — just add a note that it should not be run as-is since the catalog object has no cleanup path.

Security

No concerns. The notebook correctly relies on the ANTHROPIC_API_KEY environment variable via the implicit Anthropic() constructor.

Suggestions

  • The ## What you will build intro section would be stronger as outcome-focused bullets ("what the reader can do after") rather than artefact-focused bullets ("what gets created"), which matches the TLO format used elsewhere in the cookbook.
  • The new README table row is significantly longer than adjacent entries; trimming the description would match the existing style better.

Positive Notes

  • The introduction hook ("they have to repeat themselves, and the experience feels transactional rather than personal") is excellent — it opens with a concrete pain point before introducing the API machinery.
  • The [memory] prefix lines in run_turn make otherwise-invisible file I/O visible inline, which is the exact insight the notebook is teaching.
  • The "How memory works" conceptual section before any code gives readers a mental model first — exactly right for a tutorial.
  • The beta feature callout block sets reader expectations appropriately.
  • Model ID claude-sonnet-4-6 is the correct non-dated alias per CLAUDE.md.
  • The registry.yaml entry is correctly formatted with appropriate categories.
  • The Summary maps cleanly back to the numbered steps.

- Guard the session.status_idle break on stop_reason.type == "end_turn"
  so the pattern is safe to copy into custom-tool agents
- Import wait_for_idle_status from utilities and call it before each
  sessions.archive() to absorb the SSE-vs-status race
- Use environments.archive instead of environments.delete
- Define MODEL via the COOKBOOK_MODEL env var like the other CMA
  notebooks and pass it to agents.create
- Seed cell: capture the return, print the seeded path, and note that
  it must run before the cleanup cell that deletes the store

https://claude.ai/code/session_01VkfVPdiJTWVh6Vj5owqRXH
@github-actions

Copy link
Copy Markdown

Notebook Changes

This PR modifies the following notebooks:

📓 managed_agents/CMA_remember_user_preferences.ipynb

View diff
nbdiff /dev/null managed_agents/CMA_remember_user_preferences.ipynb (892f846d1a5e6034e3c1db5075e0837e193b955b)
--- /dev/null  2026-04-25 01:10:33.600732
+++ managed_agents/CMA_remember_user_preferences.ipynb (892f846d1a5e6034e3c1db5075e0837e193b955b)  (no timestamp)
## added /cells:
+  markdown cell:
+    source:
+      # Build agents that remember your users
+      
+      Most agents start every conversation from scratch. A customer tells your shopping assistant their size, their budget, and which materials they avoid, and the next time they return, the agent has forgotten everything. They have to repeat themselves, and the experience feels transactional rather than personal.
+      
+      We just introduced **Memory** in Claude Managed Agents to solve this. A memory store is a persistent collection of text files that Claude can read and write during a session, and that survives across sessions. Anthropic hosts the storage, mounts it into the agent's environment, and prompts Claude on when and what to save — so there is no database, vector store, or retrieval pipeline for you to stand up. Attach the same store to every session for a given customer, and your agent accumulates knowledge about them over time.
+      
+      In this guide you will build a personal shopping assistant for a retail brand. The agent will learn a customer's preferences during their first visit, save them to a memory store, and recall them automatically on the next visit without being told again.
+      
+      ## What you will build
+      
+      - A **memory store** that holds one customer's shopping preferences
+      - A **shopping agent** configured to check and update that store
+      - **Two separate sessions** that demonstrate memory carrying across visits
+      - A pattern for **inspecting and seeding** memories from your own application
+      
+      ## How memory works
+      
+      When you attach a memory store to a session, it appears as a directory inside the agent's environment at `/mnt/memory/{store-name}`, and Claude reads and writes files there using its standard file tools. Your application has full read and write access to the same files through the REST API, so you can seed a store with known facts, audit what the agent has written, or export everything to your own systems.
+      
+      > **Beta feature.** Memory stores are part of the Claude Managed Agents public beta. The API may change before general availability. The Python SDK adds the required `anthropic-beta` header automatically for every method under `client.beta`.
+  markdown cell:
+    source:
+      ## Prerequisites
+      
+      - An Anthropic API key with access to the Claude Managed Agents beta. Set it as the `ANTHROPIC_API_KEY` environment variable.
+      - Python 3.11 or later.
+      - The Anthropic Python SDK. Memory store methods require a recent release:
+      
+      ```bash
+      uv add anthropic
+      # or: pip install -U anthropic
+      ```
+  markdown cell:
+    source:
+      ## Set up the client
+  code cell:
+    source:
+      import os
+      
+      from anthropic import Anthropic
+      from utilities import wait_for_idle_status
+      
+      MODEL = os.environ.get("COOKBOOK_MODEL", "claude-sonnet-4-6")
+      
+      client = Anthropic()
+  markdown cell:
+    source:
+      ### A helper for conversational turns
+      
+      Managed agent sessions are event-driven: you send a user message, then stream events until the session goes idle. To keep the rest of this guide readable, wrap that loop in a helper that prints the agent's replies and the files it touches under `/mnt/memory/`.
+  code cell:
+    source:
+      def run_turn(session_id: str, user_text: str) -> str:
+          """Send one user message and stream the agent's response until it goes idle."""
+          print(f"\n[user]  {user_text}")
+          reply_parts: list[str] = []
+      
+          with client.beta.sessions.events.stream(session_id) as stream:
+              client.beta.sessions.events.send(
+                  session_id,
+                  events=[
+                      {
+                          "type": "user.message",
+                          "content": [{"type": "text", "text": user_text}],
+                      }
+                  ],
+              )
+              for event in stream:
+                  if event.type == "agent.message":
+                      for block in event.content:
+                          if block.type == "text":
+                              reply_parts.append(block.text)
+                              print(f"[agent] {block.text}")
+      
+                  elif event.type == "agent.tool_use":
+                      # Surface reads and writes to the memory mount so you can see
+                      # the agent checking and updating its memory.
+                      target = event.input.get("file_path") or event.input.get("command", "")
+                      if "/mnt/memory/" in str(target):
+                          print(f"  [memory] {event.name}: {target}")
+      
+                  elif event.type == "session.status_idle":
+                      if event.stop_reason and event.stop_reason.type == "end_turn":
+                          break
+      
+          return "".join(reply_parts)
+  markdown cell:
+    source:
+      ## Step 1: Create a memory store
+      
+      A memory store is a named container for text files, scoped to your workspace. In a production deployment you would typically create one store per end user and keep a mapping from your user IDs to store IDs in your own database.
+      
+      The `description` you set here is rendered into the agent's system prompt whenever the store is attached, so use it to tell Claude what the store is for.
+  code cell:
+    source:
+      store = client.beta.memory_stores.create(
+          name="Shopper Preferences",
+          description=(
+              "Personal shopping preferences for a single customer: "
+              "sizes, style, budget, favorite brands, and materials to avoid."
+          ),
+      )
+      
+      print(store.id)  # memstore_01...
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          memstore_01NKkumXZXY8mEhoRA3xhBvN
+  markdown cell:
+    source:
+      ## Step 2: Define the shopping agent
+      
+      Every managed agent session needs an **agent** (the model, system prompt, and tools) and an **environment** (the container the agent runs in). You can create these once and reuse them across many customers and sessions.
+      
+      Give the agent the built-in `agent_toolset`, which includes the file tools it uses to read and write memory. The `agent_toolset_20260401` type string is the toolset's API identifier, not a model alias, so you do not need to update it when newer models ship.
+  code cell:
+    source:
+      environment = client.beta.environments.create(
+          name="shopping-demo",
+      )
+      
+      agent = client.beta.agents.create(
+          name="Personal Shopper",
+          model=MODEL,
+          system=(
+              "You are a personal shopping assistant for a retail brand. "
+              "Help the customer find products that match their taste and budget, "
+              "and remember what you learn about them for future visits."
+          ),
+          tools=[{"type": "agent_toolset_20260401"}],
+      )
+      
+      print(agent.id)  # agent_01...
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          agent_011CaMbJoHMUyR5TjeFDtMG9
+  markdown cell:
+    source:
+      ## Step 3: First visit, where the agent learns preferences
+      
+      Now start a session and attach the memory store through the `resources` array. The `instructions` field is per-attachment guidance that tells Claude how to use this particular store in this particular session.
+      
+      Because this is the customer's first visit, the store is empty. Watch the `[memory]` lines in the output: the agent checks the store, finds nothing yet, and then writes a new file capturing what it learned.
+  code cell:
+    source:
+      memory_resource = {
+          "type": "memory_store",
+          "memory_store_id": store.id,
+          "access": "read_write",
+          "instructions": (
+              "This customer's personal preferences: sizes, style, budget, and "
+              "materials to avoid. Check it at the start of every conversation "
+              "and update it whenever you learn something new."
+          ),
+      }
+      
+      session_one = client.beta.sessions.create(
+          agent=agent.id,
+          environment_id=environment.id,
+          resources=[memory_resource],
+      )
+      
+      # The response tells you where the store is mounted inside the agent's environment.
+      for resource in session_one.resources:
+          if resource.type == "memory_store":
+              print(f"Mounted at {resource.mount_path}")
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          Mounted at /mnt/memory/shopper-preferences
+  code cell:
+    source:
+      run_turn(
+          session_one.id,
+          "Hi! I'm looking for a new jacket. A few things about me: I wear a size "
+          "medium, I only buy vegan leather (no animal leather please), my budget "
+          "is usually under $200, and I love earth tones. What would you suggest?",
+      )
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          [user]  Hi! I'm looking for a new jacket. A few things about me: I wear a size medium, I only buy vegan leather (no animal leather please), my budget is usually under $200, and I love earth tones. What would you suggest?
+          [agent] Let me check if I have any info about you on file, and save your preferences right away!
+            [memory] bash: cat /mnt/memory/shopper-preferences 2>/dev/null || echo "No file found"
+            [memory] write: /mnt/memory/shopper-preferences
+            [memory] write: /mnt/memory/shopper-preferences/preferences.md
+          [agent] I've saved your preferences for future visits! Now, here are some great **vegan leather jacket picks in earth tones under $200** that I'd suggest:
+          
+          ---
+          
+          ### 🌿 Top Picks for You
+          
+          #### 1. **Olive Moto Jacket** — ~$110–$150
+          A classic biker silhouette in a deep **olive green** faux leather. Look for styles with zip-detail sleeves and a fitted waist. Great for casual or edgy looks.
+          - *Why you'll love it:* Earthy tone, very on-trend, versatile for layering.
+          
+          #### 2. **Camel/Tan Oversized Blazer-Jacket** — ~$80–$160
+          A structured vegan leather blazer in **camel or tan** gives a polished, sophisticated vibe. Works dressed up or down.
+          - *Why you'll love it:* Classic earth tone, medium sizing runs well in this style, timeless investment.
+          
+          #### 3. **Rust/Cognac Bomber Jacket** — ~$90–$170
+          A **rust or cognac-toned** vegan leather bomber is a bold earth-tone statement piece with a relaxed, comfortable fit.
+          - *Why you'll love it:* Rich warm color, bomber silhouettes are flattering in medium, cozy feel.
+          
+          #### 4. **Terracotta Cropped Jacket** — ~$75–$140
+          A cropped vegan leather jacket in **terracotta** pairs beautifully with high-waisted jeans or wide-leg trousers.
+          - *Why you'll love it:* Trendy silhouette, stunning earthy hue, well within budget.
+          
+          ---
+          
+          ### 🛍️ Brands to Check Out
+          Here are some brands known for quality vegan leather within your budget:
+          - **ASOS** – Wide range of faux leather styles in various earth tones
+          - **Free People** – Boho-leaning earth tone options
+          - **H&M** – Affordable vegan leather jackets in seasonal colors
+          - **Lulus** – Great selection of trendy vegan styles under $100
+          - **Noize** – A dedicated vegan outerwear brand, often has earthy tones under $200
+          
+          ---
+          
+          Do any of these styles appeal to you? I can help narrow it down further — for example, do you prefer a **fitted or relaxed fit**, or a particular style like moto, bomber, or blazer? 😊
+  markdown cell:
+    source:
+      ## Step 4: Inspect what the agent saved
+      
+      Everything the agent writes to the mount is a regular memory document that your application can read, edit, or delete through the API. List the store's contents with `view="full"` to include file content in the response.
+      
+      This is how you build a "What we know about you" page in your product, sync memories into your own database, or let a human reviewer correct something the agent got wrong.
+  code cell:
+    source:
+      page = client.beta.memory_stores.memories.list(
+          store.id,
+          view="full",
+      )
+      
+      for memory in page.data:
+          if memory.type == "memory":
+              print(f"=== {memory.path} ===")
+              print(memory.content)
+              print()
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          === /preferences.md ===
+          # Shopper Preferences
+          
+          ## Sizes
+          - Tops/Jackets: Medium
+          
+          ## Style
+          - Loves earth tones (browns, tans, olive, terracotta, camel, rust, etc.)
+          - Interested in jackets
+          
+          ## Budget
+          - Usually under $200
+          
+          ## Materials
+          - VEGAN LEATHER ONLY — absolutely no animal leather
+          - (No other material restrictions noted yet)
+          
+          ## Favorite Brands
+          - None noted yet
+          
+          ## Other Notes
+          - First visit; preferences collected 2026-04-23
+  markdown cell:
+    source:
+      You should see a file (typically something like `/preferences.md`) containing the size, budget, material, and color preferences the customer mentioned, organized by topic. Claude chose the filename and structure on its own.
+  markdown cell:
+    source:
+      ## Step 5: Return visit, where the agent recalls on its own
+      
+      This is the moment that matters. Create a **brand new session** and attach the **same memory store**. The customer does not repeat any of their preferences, but the agent reads them from memory and tailors its recommendations accordingly.
+  code cell:
+    source:
+      session_two = client.beta.sessions.create(
+          agent=agent.id,
+          environment_id=environment.id,
+          resources=[memory_resource],  # same store, new session
+      )
+      
+      run_turn(
+          session_two.id,
+          "Hey, I'm back! I need a bag for work. Any recommendations?",
+      )
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          [user]  Hey, I'm back! I need a bag for work. Any recommendations?
+          [agent] Welcome back! Let me check your preferences before making any recommendations!
+            [memory] read: /mnt/memory/shopper-preferences
+            [memory] bash: ls /mnt/memory/shopper-preferences/
+            [memory] read: /mnt/memory/shopper-preferences/preferences.md
+          [agent] Great news — I already have your preferences on file! Here's what I kept in mind for you:
+          
+          - 🎨 **Style:** Earth tones (browns, tans, olive, camel, etc.)
+          - 💰 **Budget:** Under $200
+          - 🌱 **Materials:** Vegan leather only — no animal leather
+          
+          With that in mind, here are some work bag recommendations that would suit you perfectly:
+          
+          ---
+          
+          ### 🛍️ Top Picks for a Work Bag
+          
+          1. **Structured Tote in Camel Vegan Leather** — A polished, professional look in a warm earth tone. Fits a laptop + daily essentials. Great desk-to-commute bag.
+          
+          2. **Olive Canvas & Vegan Leather Trim Laptop Bag** — A sleek crossbody/shoulder bag with a relaxed but put-together vibe. The canvas keeps it lightweight.
+          
+          3. **Tan Vegan Leather Satchel** — A classic work satchel with a top handle and shoulder strap. Timeless silhouette, stays under budget.
+          
+          4. **Terracotta Faux-Leather Tote** — A bold but grounded earth tone that adds personality to a work outfit without being over the top.
+          
+          ---
+          
+          Do any of these styles appeal to you? I can also ask a few quick questions to narrow it down further:
+          
+          - **Do you prefer a tote, backpack, crossbody, or briefcase-style?**
+          - **Do you need to carry a laptop, and if so, what size?**
+          - **Do you lean more structured/professional or relaxed/casual for work?**
+  markdown cell:
+    source:
+      In the output, notice that the agent reads `/mnt/memory/shopper-preferences/` before answering, then recommends bags that are vegan leather, in earth tones, and under $200, even though this message mentioned none of those things. The preferences carried across from the first session.
+  markdown cell:
+    source:
+      ## Going further: patterns for production
+      
+      ### Seed a store from your existing data
+      
+      If you already know things about a customer from their account profile or purchase history, you can write them into the store before the first session so the agent starts informed.
+  code cell:
+    source:
+      # Run before the cleanup cell below; `store` is deleted there.
+      seeded = client.beta.memory_stores.memories.create(
+          store.id,
+          path="/purchase-history.md",
+          content=(
+              "## Recent purchases\n"
+              "- Canvas tote, olive, $89 (Jan 2026)\n"
+              "- Wool beanie, rust, $34 (Dec 2025)\n"
+          ),
+      )
+      print(f"Seeded {seeded.path}")
+  markdown cell:
+    source:
+      ### Combine per-customer and shared stores
+      
+      A session can attach up to eight memory stores, each with its own access level. A common pattern is one read-write store per customer plus a read-only store of brand-wide knowledge that every session shares.
+      
+      ```python
+      catalog = client.beta.memory_stores.create(
+          name="Product Catalog Notes",
+          description="Current promotions, sizing guidance, and stock notes.",
+      )
+      
+      session = client.beta.sessions.create(
+          agent=agent.id,
+          environment_id=environment.id,
+          resources=[
+              {
+                  "type": "memory_store",
+                  "memory_store_id": store.id,
+                  "access": "read_write",
+                  "instructions": "This customer's personal preferences.",
+              },
+              {
+                  "type": "memory_store",
+                  "memory_store_id": catalog.id,
+                  "access": "read_only",
+                  "instructions": "Brand-wide product guidance. Consult before recommending items.",
+              },
+          ],
+      )
+      ```
+      
+      ### Audit and correct
+      
+      Every write to a memory store is recorded as an immutable version with the session that made it. Use `client.beta.memory_stores.memory_versions.list(...)` to review history, and `client.beta.memory_stores.memories.update(...)` to correct a file that the agent got wrong. See the [Memory API reference](https://docs.anthropic.com/en/docs/managed-agents/memory) for the full surface.
+  markdown cell:
+    source:
+      ## Clean up
+      
+      Delete the resources you created while following this guide.
+  code cell:
+    source:
+      wait_for_idle_status(client, session_one.id)
+      client.beta.sessions.archive(session_one.id)
+      wait_for_idle_status(client, session_two.id)
+      client.beta.sessions.archive(session_two.id)
+      client.beta.memory_stores.delete(store.id)
+      client.beta.agents.archive(agent.id)
+      client.beta.environments.archive(environment.id)
+  markdown cell:
+    source:
+      ## Summary
+      
+      You built a shopping agent that remembers its customers across visits by:
+      
+      1. Creating a memory store for the customer
+      2. Attaching it to each session through `resources`
+      3. Letting Claude read and write files under `/mnt/memory/` as it learned new preferences
+      4. Inspecting those files from your own application through the Memory API
+      
+      From here you can map your own user IDs to memory store IDs, seed stores from your existing customer data, and layer shared read-only stores on top for brand-wide knowledge.
+      
+      ### Learn more
+      
+      - [Claude Managed Agents overview](https://docs.anthropic.com/en/docs/managed-agents/overview)
+      - [Memory stores API reference](https://docs.anthropic.com/en/docs/managed-agents/memory)
+      - [Session resources](https://docs.anthropic.com/en/docs/managed-agents/memory#attach-a-memory-store-to-a-session)

Generated by nbdime

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review

Recommendation: REQUEST_CHANGES

Summary

Adds a well-structured memory stores cookbook showing a shopping agent that learns and recalls customer preferences across sessions. The narrative flow and API usage are sound, but the run_turn streaming helper has robustness gaps that need fixing before merge.

Actionable Feedback (7 items)

Blocking

  • CMA_remember_user_preferences.ipynb (in cell with def run_turn) — No session.status_terminated guard: if a session terminates unexpectedly the event loop will spin indefinitely. Every other CMA notebook's streaming loop includes elif event.type == "session.status_terminated": break. Add the same guard here.

  • CMA_remember_user_preferences.ipynb (in cell with def run_turn) — The helper returns without calling wait_for_idle_status. The cleanup cell compensates by calling it manually, but any reader who adapts run_turn and immediately calls archive() will hit a 400 "cannot archive while running" race. Either call wait_for_idle_status(client, session_id) before returning, or add an inline comment explaining why the cleanup cell handles it externally.

  • CMA_remember_user_preferences.ipynb (in cell with def run_turn) — No requires_action / non-end_turn stop guard: the loop only breaks on stop_reason.type == "end_turn". Any other idle reason leaves the stream open indefinitely. A safe fallback is elif event.type == "session.status_idle": break that exits on any idle, plus a comment noting the helper is only safe when the agent has no custom tools.

Important

  • CMA_remember_user_preferences.ipynb (in cell with seeded = client.beta.memory_stores.memories.create(...)) — The seeding cell runs after both sessions have already completed, so the seeded purchase history has no visible effect on any notebook output. Either move the seeding step before session one (making the demo richer) or add prose explicitly noting that in a real application seeding would happen before the first session — and explain why the example is placed here.

  • CMA_remember_user_preferences.ipynb (in cell with seeded = client.beta.memory_stores.memories.create(...)) — The saved cell output is empty ([]) despite containing print(f"Seeded {seeded.path}"). Per the repo convention of keeping outputs for demonstration, this looks like a failed or un-run cell. Re-execute and commit the output (Seeded /purchase-history.md), or remove the print.

  • CMA_remember_user_preferences.ipynb (in cell with agent = client.beta.agents.create(...)) — agent_toolset_20260401 is missing default_config / permission_policy. All other CMA series notebooks that use this toolset include "default_config": {"enabled": True, "permission_policy": {"type": "always_allow"}}. Add it for consistency and to make the required config explicit for readers.

  • CMA_remember_user_preferences.ipynb (in cell with environment = client.beta.environments.create(...)) — environments.create omits config (networking type). All other CMA series notebooks pass an explicit config={"type": "cloud", "networking": {"type": "..."}}. Add the config to match series conventions and clarify the networking policy.

Detailed Review

Code Quality

The notebook structure is clean and the step-by-step prose before each cell is well-calibrated — each explanation answers "why this call, why now" rather than just narrating the code. The stream-open-before-send ordering is correct (the with block opens the SSE connection before events.send), which is a common footgun the notebook avoids correctly.

The run_turn helper diverges from the series utility stream_until_end_turn without explanation. Since this notebook has no custom tools, delegating to stream_until_end_turn (with thin wrapper for the [memory] logging) would be cleaner and would inherit the series' error handling for free. If the inline loop is intentional for pedagogy, a comment should say so.

Security

No concerns — no secrets, keys, or sensitive data. ANTHROPIC_API_KEY is loaded from the environment correctly via the SDK default.

Suggestions

  • memory_resource dict is reused across both sessions with only a # same store, new session comment. A brief note that the dict is a value object and store.id is captured at construction time would address the implied shared-state question.
  • The README.md table entry is noticeably longer than all other rows. Consider a shorter description in the table and a prose detail paragraph below.
  • The registry.yaml entry uses Agent Patterns and Tools — worth checking whether other CMA entries use a Managed Agents category tag for better discoverability.

Positive Notes

  • Introduction follows the problem-first structure perfectly: user pain → solution → learning objectives.
  • The "How memory works" callout and beta-feature disclaimer are exactly right for an API-in-preview feature.
  • MODEL constant uses the non-dated alias claude-sonnet-4-6 with COOKBOOK_MODEL env override — correct per CLAUDE.md.
  • "Going further" section (seeding, combining stores, auditing) provides genuinely useful production patterns.
  • wait_for_idle_status usage in the cleanup cell is correct and consistent with the series.
  • The summary cell maps back to all stated learning objectives — closes the pedagogical loop correctly.

- run_turn: handle session.status_terminated and call
  wait_for_idle_status before returning, mirroring
  utilities.stream_until_end_turn; drop the now-redundant
  wait_for_idle_status calls from the cleanup cell
- environments.create: pass an explicit cloud/limited config
- agent_toolset_20260401: include the default_config block with
  permission_policy always_allow, matching every other CMA notebook
- Seed section: explain that production seeding happens before any
  sessions, and add the cell's stdout so it doesn't look un-run

Not changed: the end_turn-guarded idle break stays as-is. Reverting it
to an unconditional break would undo the previous round's fix and
diverge from utilities.stream_until_end_turn.

https://claude.ai/code/session_01VkfVPdiJTWVh6Vj5owqRXH

Copy link
Copy Markdown
Contributor Author

Addressed 6 of the 7 review items in 6c32b29.

Intentionally not changing the session.status_idle handling: the previous review round asked for the stop_reason.type == "end_turn" guard (which 892f846 added), and reverting to an unconditional break would also diverge from utilities.stream_until_end_turn, which uses the same guarded pattern. This agent has no custom tools, so requires_action cannot fire here.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown

Notebook Changes

This PR modifies the following notebooks:

📓 managed_agents/CMA_remember_user_preferences.ipynb

View diff
nbdiff /dev/null managed_agents/CMA_remember_user_preferences.ipynb (6c32b29f7453a25835029d786962466789dd15e3)
--- /dev/null  2026-04-25 01:16:00.836906
+++ managed_agents/CMA_remember_user_preferences.ipynb (6c32b29f7453a25835029d786962466789dd15e3)  (no timestamp)
## added /cells:
+  markdown cell:
+    source:
+      # Build agents that remember your users
+      
+      Most agents start every conversation from scratch. A customer tells your shopping assistant their size, their budget, and which materials they avoid, and the next time they return, the agent has forgotten everything. They have to repeat themselves, and the experience feels transactional rather than personal.
+      
+      We just introduced **Memory** in Claude Managed Agents to solve this. A memory store is a persistent collection of text files that Claude can read and write during a session, and that survives across sessions. Anthropic hosts the storage, mounts it into the agent's environment, and prompts Claude on when and what to save — so there is no database, vector store, or retrieval pipeline for you to stand up. Attach the same store to every session for a given customer, and your agent accumulates knowledge about them over time.
+      
+      In this guide you will build a personal shopping assistant for a retail brand. The agent will learn a customer's preferences during their first visit, save them to a memory store, and recall them automatically on the next visit without being told again.
+      
+      ## What you will build
+      
+      - A **memory store** that holds one customer's shopping preferences
+      - A **shopping agent** configured to check and update that store
+      - **Two separate sessions** that demonstrate memory carrying across visits
+      - A pattern for **inspecting and seeding** memories from your own application
+      
+      ## How memory works
+      
+      When you attach a memory store to a session, it appears as a directory inside the agent's environment at `/mnt/memory/{store-name}`, and Claude reads and writes files there using its standard file tools. Your application has full read and write access to the same files through the REST API, so you can seed a store with known facts, audit what the agent has written, or export everything to your own systems.
+      
+      > **Beta feature.** Memory stores are part of the Claude Managed Agents public beta. The API may change before general availability. The Python SDK adds the required `anthropic-beta` header automatically for every method under `client.beta`.
+  markdown cell:
+    source:
+      ## Prerequisites
+      
+      - An Anthropic API key with access to the Claude Managed Agents beta. Set it as the `ANTHROPIC_API_KEY` environment variable.
+      - Python 3.11 or later.
+      - The Anthropic Python SDK. Memory store methods require a recent release:
+      
+      ```bash
+      uv add anthropic
+      # or: pip install -U anthropic
+      ```
+  markdown cell:
+    source:
+      ## Set up the client
+  code cell:
+    source:
+      import os
+      
+      from anthropic import Anthropic
+      from utilities import wait_for_idle_status
+      
+      MODEL = os.environ.get("COOKBOOK_MODEL", "claude-sonnet-4-6")
+      
+      client = Anthropic()
+  markdown cell:
+    source:
+      ### A helper for conversational turns
+      
+      Managed agent sessions are event-driven: you send a user message, then stream events until the session goes idle. To keep the rest of this guide readable, wrap that loop in a helper that prints the agent's replies and the files it touches under `/mnt/memory/`.
+  code cell:
+    source:
+      def run_turn(session_id: str, user_text: str) -> str:
+          """Send one user message and stream the agent's response until it goes idle."""
+          print(f"\n[user]  {user_text}")
+          reply_parts: list[str] = []
+      
+          with client.beta.sessions.events.stream(session_id) as stream:
+              client.beta.sessions.events.send(
+                  session_id,
+                  events=[
+                      {
+                          "type": "user.message",
+                          "content": [{"type": "text", "text": user_text}],
+                      }
+                  ],
+              )
+              for event in stream:
+                  if event.type == "agent.message":
+                      for block in event.content:
+                          if block.type == "text":
+                              reply_parts.append(block.text)
+                              print(f"[agent] {block.text}")
+      
+                  elif event.type == "agent.tool_use":
+                      # Surface reads and writes to the memory mount so you can see
+                      # the agent checking and updating its memory.
+                      target = event.input.get("file_path") or event.input.get("command", "")
+                      if "/mnt/memory/" in str(target):
+                          print(f"  [memory] {event.name}: {target}")
+      
+                  elif event.type == "session.status_idle":
+                      if event.stop_reason and event.stop_reason.type == "end_turn":
+                          break
+      
+                  elif event.type == "session.status_terminated":
+                      break
+      
+          wait_for_idle_status(client, session_id)
+          return "".join(reply_parts)
+  markdown cell:
+    source:
+      ## Step 1: Create a memory store
+      
+      A memory store is a named container for text files, scoped to your workspace. In a production deployment you would typically create one store per end user and keep a mapping from your user IDs to store IDs in your own database.
+      
+      The `description` you set here is rendered into the agent's system prompt whenever the store is attached, so use it to tell Claude what the store is for.
+  code cell:
+    source:
+      store = client.beta.memory_stores.create(
+          name="Shopper Preferences",
+          description=(
+              "Personal shopping preferences for a single customer: "
+              "sizes, style, budget, favorite brands, and materials to avoid."
+          ),
+      )
+      
+      print(store.id)  # memstore_01...
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          memstore_01NKkumXZXY8mEhoRA3xhBvN
+  markdown cell:
+    source:
+      ## Step 2: Define the shopping agent
+      
+      Every managed agent session needs an **agent** (the model, system prompt, and tools) and an **environment** (the container the agent runs in). You can create these once and reuse them across many customers and sessions.
+      
+      Give the agent the built-in `agent_toolset`, which includes the file tools it uses to read and write memory. The `agent_toolset_20260401` type string is the toolset's API identifier, not a model alias, so you do not need to update it when newer models ship.
+  code cell:
+    source:
+      environment = client.beta.environments.create(
+          name="shopping-demo",
+          config={"type": "cloud", "networking": {"type": "limited"}},
+      )
+      
+      agent = client.beta.agents.create(
+          name="Personal Shopper",
+          model=MODEL,
+          system=(
+              "You are a personal shopping assistant for a retail brand. "
+              "Help the customer find products that match their taste and budget, "
+              "and remember what you learn about them for future visits."
+          ),
+          tools=[
+              {
+                  "type": "agent_toolset_20260401",
+                  "default_config": {
+                      "enabled": True,
+                      "permission_policy": {"type": "always_allow"},
+                  },
+              }
+          ],
+      )
+      
+      print(agent.id)  # agent_01...
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          agent_011CaMbJoHMUyR5TjeFDtMG9
+  markdown cell:
+    source:
+      ## Step 3: First visit, where the agent learns preferences
+      
+      Now start a session and attach the memory store through the `resources` array. The `instructions` field is per-attachment guidance that tells Claude how to use this particular store in this particular session.
+      
+      Because this is the customer's first visit, the store is empty. Watch the `[memory]` lines in the output: the agent checks the store, finds nothing yet, and then writes a new file capturing what it learned.
+  code cell:
+    source:
+      memory_resource = {
+          "type": "memory_store",
+          "memory_store_id": store.id,
+          "access": "read_write",
+          "instructions": (
+              "This customer's personal preferences: sizes, style, budget, and "
+              "materials to avoid. Check it at the start of every conversation "
+              "and update it whenever you learn something new."
+          ),
+      }
+      
+      session_one = client.beta.sessions.create(
+          agent=agent.id,
+          environment_id=environment.id,
+          resources=[memory_resource],
+      )
+      
+      # The response tells you where the store is mounted inside the agent's environment.
+      for resource in session_one.resources:
+          if resource.type == "memory_store":
+              print(f"Mounted at {resource.mount_path}")
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          Mounted at /mnt/memory/shopper-preferences
+  code cell:
+    source:
+      run_turn(
+          session_one.id,
+          "Hi! I'm looking for a new jacket. A few things about me: I wear a size "
+          "medium, I only buy vegan leather (no animal leather please), my budget "
+          "is usually under $200, and I love earth tones. What would you suggest?",
+      )
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          [user]  Hi! I'm looking for a new jacket. A few things about me: I wear a size medium, I only buy vegan leather (no animal leather please), my budget is usually under $200, and I love earth tones. What would you suggest?
+          [agent] Let me check if I have any info about you on file, and save your preferences right away!
+            [memory] bash: cat /mnt/memory/shopper-preferences 2>/dev/null || echo "No file found"
+            [memory] write: /mnt/memory/shopper-preferences
+            [memory] write: /mnt/memory/shopper-preferences/preferences.md
+          [agent] I've saved your preferences for future visits! Now, here are some great **vegan leather jacket picks in earth tones under $200** that I'd suggest:
+          
+          ---
+          
+          ### 🌿 Top Picks for You
+          
+          #### 1. **Olive Moto Jacket** — ~$110–$150
+          A classic biker silhouette in a deep **olive green** faux leather. Look for styles with zip-detail sleeves and a fitted waist. Great for casual or edgy looks.
+          - *Why you'll love it:* Earthy tone, very on-trend, versatile for layering.
+          
+          #### 2. **Camel/Tan Oversized Blazer-Jacket** — ~$80–$160
+          A structured vegan leather blazer in **camel or tan** gives a polished, sophisticated vibe. Works dressed up or down.
+          - *Why you'll love it:* Classic earth tone, medium sizing runs well in this style, timeless investment.
+          
+          #### 3. **Rust/Cognac Bomber Jacket** — ~$90–$170
+          A **rust or cognac-toned** vegan leather bomber is a bold earth-tone statement piece with a relaxed, comfortable fit.
+          - *Why you'll love it:* Rich warm color, bomber silhouettes are flattering in medium, cozy feel.
+          
+          #### 4. **Terracotta Cropped Jacket** — ~$75–$140
+          A cropped vegan leather jacket in **terracotta** pairs beautifully with high-waisted jeans or wide-leg trousers.
+          - *Why you'll love it:* Trendy silhouette, stunning earthy hue, well within budget.
+          
+          ---
+          
+          ### 🛍️ Brands to Check Out
+          Here are some brands known for quality vegan leather within your budget:
+          - **ASOS** – Wide range of faux leather styles in various earth tones
+          - **Free People** – Boho-leaning earth tone options
+          - **H&M** – Affordable vegan leather jackets in seasonal colors
+          - **Lulus** – Great selection of trendy vegan styles under $100
+          - **Noize** – A dedicated vegan outerwear brand, often has earthy tones under $200
+          
+          ---
+          
+          Do any of these styles appeal to you? I can help narrow it down further — for example, do you prefer a **fitted or relaxed fit**, or a particular style like moto, bomber, or blazer? 😊
+  markdown cell:
+    source:
+      ## Step 4: Inspect what the agent saved
+      
+      Everything the agent writes to the mount is a regular memory document that your application can read, edit, or delete through the API. List the store's contents with `view="full"` to include file content in the response.
+      
+      This is how you build a "What we know about you" page in your product, sync memories into your own database, or let a human reviewer correct something the agent got wrong.
+  code cell:
+    source:
+      page = client.beta.memory_stores.memories.list(
+          store.id,
+          view="full",
+      )
+      
+      for memory in page.data:
+          if memory.type == "memory":
+              print(f"=== {memory.path} ===")
+              print(memory.content)
+              print()
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          === /preferences.md ===
+          # Shopper Preferences
+          
+          ## Sizes
+          - Tops/Jackets: Medium
+          
+          ## Style
+          - Loves earth tones (browns, tans, olive, terracotta, camel, rust, etc.)
+          - Interested in jackets
+          
+          ## Budget
+          - Usually under $200
+          
+          ## Materials
+          - VEGAN LEATHER ONLY — absolutely no animal leather
+          - (No other material restrictions noted yet)
+          
+          ## Favorite Brands
+          - None noted yet
+          
+          ## Other Notes
+          - First visit; preferences collected 2026-04-23
+  markdown cell:
+    source:
+      You should see a file (typically something like `/preferences.md`) containing the size, budget, material, and color preferences the customer mentioned, organized by topic. Claude chose the filename and structure on its own.
+  markdown cell:
+    source:
+      ## Step 5: Return visit, where the agent recalls on its own
+      
+      This is the moment that matters. Create a **brand new session** and attach the **same memory store**. The customer does not repeat any of their preferences, but the agent reads them from memory and tailors its recommendations accordingly.
+  code cell:
+    source:
+      session_two = client.beta.sessions.create(
+          agent=agent.id,
+          environment_id=environment.id,
+          resources=[memory_resource],  # same store, new session
+      )
+      
+      run_turn(
+          session_two.id,
+          "Hey, I'm back! I need a bag for work. Any recommendations?",
+      )
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          [user]  Hey, I'm back! I need a bag for work. Any recommendations?
+          [agent] Welcome back! Let me check your preferences before making any recommendations!
+            [memory] read: /mnt/memory/shopper-preferences
+            [memory] bash: ls /mnt/memory/shopper-preferences/
+            [memory] read: /mnt/memory/shopper-preferences/preferences.md
+          [agent] Great news — I already have your preferences on file! Here's what I kept in mind for you:
+          
+          - 🎨 **Style:** Earth tones (browns, tans, olive, camel, etc.)
+          - 💰 **Budget:** Under $200
+          - 🌱 **Materials:** Vegan leather only — no animal leather
+          
+          With that in mind, here are some work bag recommendations that would suit you perfectly:
+          
+          ---
+          
+          ### 🛍️ Top Picks for a Work Bag
+          
+          1. **Structured Tote in Camel Vegan Leather** — A polished, professional look in a warm earth tone. Fits a laptop + daily essentials. Great desk-to-commute bag.
+          
+          2. **Olive Canvas & Vegan Leather Trim Laptop Bag** — A sleek crossbody/shoulder bag with a relaxed but put-together vibe. The canvas keeps it lightweight.
+          
+          3. **Tan Vegan Leather Satchel** — A classic work satchel with a top handle and shoulder strap. Timeless silhouette, stays under budget.
+          
+          4. **Terracotta Faux-Leather Tote** — A bold but grounded earth tone that adds personality to a work outfit without being over the top.
+          
+          ---
+          
+          Do any of these styles appeal to you? I can also ask a few quick questions to narrow it down further:
+          
+          - **Do you prefer a tote, backpack, crossbody, or briefcase-style?**
+          - **Do you need to carry a laptop, and if so, what size?**
+          - **Do you lean more structured/professional or relaxed/casual for work?**
+  markdown cell:
+    source:
+      In the output, notice that the agent reads `/mnt/memory/shopper-preferences/` before answering, then recommends bags that are vegan leather, in earth tones, and under $200, even though this message mentioned none of those things. The preferences carried across from the first session.
+  markdown cell:
+    source:
+      ## Going further: patterns for production
+      
+      ### Seed a store from your existing data
+      
+      If you already know things about a customer from their account profile or purchase history, you can write them into the store before the first session so the agent starts informed. In a real application you would run this seeding step before any sessions are created; it appears here, after the demo, only so the main learn-then-recall flow above stays focused.
+  code cell:
+    source:
+      # Run before the cleanup cell below; `store` is deleted there.
+      seeded = client.beta.memory_stores.memories.create(
+          store.id,
+          path="/purchase-history.md",
+          content=(
+              "## Recent purchases\n"
+              "- Canvas tote, olive, $89 (Jan 2026)\n"
+              "- Wool beanie, rust, $34 (Dec 2025)\n"
+          ),
+      )
+      print(f"Seeded {seeded.path}")
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          Seeded /purchase-history.md
+  markdown cell:
+    source:
+      ### Combine per-customer and shared stores
+      
+      A session can attach up to eight memory stores, each with its own access level. A common pattern is one read-write store per customer plus a read-only store of brand-wide knowledge that every session shares.
+      
+      ```python
+      catalog = client.beta.memory_stores.create(
+          name="Product Catalog Notes",
+          description="Current promotions, sizing guidance, and stock notes.",
+      )
+      
+      session = client.beta.sessions.create(
+          agent=agent.id,
+          environment_id=environment.id,
+          resources=[
+              {
+                  "type": "memory_store",
+                  "memory_store_id": store.id,
+                  "access": "read_write",
+                  "instructions": "This customer's personal preferences.",
+              },
+              {
+                  "type": "memory_store",
+                  "memory_store_id": catalog.id,
+                  "access": "read_only",
+                  "instructions": "Brand-wide product guidance. Consult before recommending items.",
+              },
+          ],
+      )
+      ```
+      
+      ### Audit and correct
+      
+      Every write to a memory store is recorded as an immutable version with the session that made it. Use `client.beta.memory_stores.memory_versions.list(...)` to review history, and `client.beta.memory_stores.memories.update(...)` to correct a file that the agent got wrong. See the [Memory API reference](https://docs.anthropic.com/en/docs/managed-agents/memory) for the full surface.
+  markdown cell:
+    source:
+      ## Clean up
+      
+      Delete the resources you created while following this guide.
+  code cell:
+    source:
+      client.beta.sessions.archive(session_one.id)
+      client.beta.sessions.archive(session_two.id)
+      client.beta.memory_stores.delete(store.id)
+      client.beta.agents.archive(agent.id)
+      client.beta.environments.archive(environment.id)
+  markdown cell:
+    source:
+      ## Summary
+      
+      You built a shopping agent that remembers its customers across visits by:
+      
+      1. Creating a memory store for the customer
+      2. Attaching it to each session through `resources`
+      3. Letting Claude read and write files under `/mnt/memory/` as it learned new preferences
+      4. Inspecting those files from your own application through the Memory API
+      
+      From here you can map your own user IDs to memory store IDs, seed stores from your existing customer data, and layer shared read-only stores on top for brand-wide knowledge.
+      
+      ### Learn more
+      
+      - [Claude Managed Agents overview](https://docs.anthropic.com/en/docs/managed-agents/overview)
+      - [Memory stores API reference](https://docs.anthropic.com/en/docs/managed-agents/memory)
+      - [Session resources](https://docs.anthropic.com/en/docs/managed-agents/memory#attach-a-memory-store-to-a-session)

Generated by nbdime

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review

Recommendation: REQUEST_CHANGES

Summary

This notebook introduces memory stores for Claude Managed Agents through a well-paced, narrative-driven shopping assistant tutorial. The prose, code quality, and pedagogy are strong. Two issues diverge from established series conventions and should be fixed before merging.

Actionable Feedback (4 items)
  • CMA_remember_user_preferences.ipynb (in cells with session_one = client.beta.sessions.create(...) and session_two = client.beta.sessions.create(...)) — Pass a pinned agent version dict instead of the bare string agent.id. Every other CMA notebook uses agent={"type": "agent", "id": agent.id, "version": agent.version} to prevent sessions from silently picking up prompt changes if the agent is updated concurrently. This is a correctness concern for readers copying the pattern into production.

  • CMA_remember_user_preferences.ipynb (in cell with def run_turn(session_id: str, user_text: str) -> str:) — The event loop only breaks when stop_reason.type == "end_turn". If the session idles with stop_reason.type == "requires_action" (possible with agent_toolset in edge cases), the loop never exits and the stream blocks until a server-side timeout. Either handle the requires_action case explicitly or add a comment explaining the limitation, so readers who copy this helper into their own code aren't surprised.

  • CMA_remember_user_preferences.ipynb (in cell with def run_turn(session_id: str, user_text: str) -> str:) — The return value ("".join(reply_parts)) is never used at either call site. Either assign it in one of the example calls (e.g., reply = run_turn(...)) to show readers it is capturable, or change the return type to None to remove the confusion.

  • CMA_remember_user_preferences.ipynb (cleanup cell with client.beta.memory_stores.delete(store.id)) — All other CMA notebooks use archive() for cleanup. If delete() is correct for memory stores (no archive method exists), add a one-line comment explaining why memory stores use delete while sessions/agents/environments use archive, to avoid reader confusion.

Detailed Review

Code Quality

The Python is clean and idiomatic. run_turn uses modern list[str] annotation, opens the stream before sending (matching the canonical pattern in CMA_iterate_fix_failing_tests.ipynb), and clearly surfaces memory tool calls. The event.input.get("file_path") or event.input.get("command", "") idiom is slightly surprising — an empty file_path string would fall through to command — but this is unlikely in practice with the memory toolset.

The seeding cell has a useful comment noting it should run before cleanup, but the ordering could be made even clearer by adding a reminder in the cleanup cell's markdown header.

Security

No issues. client = Anthropic() picks up ANTHROPIC_API_KEY from the environment correctly. No credentials are hardcoded.

Model IDs

MODEL = os.environ.get("COOKBOOK_MODEL", "claude-sonnet-4-6") uses the correct non-dated alias. The agent_toolset_20260401 string is correctly identified in the prose as an API toolset identifier, not a model alias.

Suggestions

  • The "Going further: patterns for production" section is well-placed but the Summary at the end doesn't mention the multi-store pattern — a brief reference there would help readers who skim to the bottom.
  • The README table entry accurately describes the notebook. A brief note that this is the only CMA notebook covering the Memory API surface would help readers scanning the table.

Positive Notes

  • The introduction hooks immediately on real user pain (agents that forget) and the "What you will build" bullets are concrete and honest.
  • The [memory] lines surfaced in run_turn output make memory I/O visible without cluttering the helper — excellent pedagogy.
  • The "In a real application you would run this seeding step before any sessions are created; it appears here, after the demo, only so the main learn-then-recall flow above stays focused" comment in the seeding cell is exactly the right thing to say and models good notebook authorship.
  • Registry entry follows the established format; gaganb-ant is already in authors.yaml.

…ookbook

Pass agent={"type": "agent", "id": agent.id, "version": agent.version}
instead of the bare agent.id, matching every other notebook in the
managed_agents series.

https://claude.ai/code/session_01VkfVPdiJTWVh6Vj5owqRXH
@github-actions

Copy link
Copy Markdown

Notebook Changes

This PR modifies the following notebooks:

📓 managed_agents/CMA_remember_user_preferences.ipynb

View diff
nbdiff /dev/null managed_agents/CMA_remember_user_preferences.ipynb (534389445fbac11079b2336929bd8eb4cf811350)
--- /dev/null  2026-04-25 01:21:44.464732
+++ managed_agents/CMA_remember_user_preferences.ipynb (534389445fbac11079b2336929bd8eb4cf811350)  (no timestamp)
## added /cells:
+  markdown cell:
+    source:
+      # Build agents that remember your users
+      
+      Most agents start every conversation from scratch. A customer tells your shopping assistant their size, their budget, and which materials they avoid, and the next time they return, the agent has forgotten everything. They have to repeat themselves, and the experience feels transactional rather than personal.
+      
+      We just introduced **Memory** in Claude Managed Agents to solve this. A memory store is a persistent collection of text files that Claude can read and write during a session, and that survives across sessions. Anthropic hosts the storage, mounts it into the agent's environment, and prompts Claude on when and what to save — so there is no database, vector store, or retrieval pipeline for you to stand up. Attach the same store to every session for a given customer, and your agent accumulates knowledge about them over time.
+      
+      In this guide you will build a personal shopping assistant for a retail brand. The agent will learn a customer's preferences during their first visit, save them to a memory store, and recall them automatically on the next visit without being told again.
+      
+      ## What you will build
+      
+      - A **memory store** that holds one customer's shopping preferences
+      - A **shopping agent** configured to check and update that store
+      - **Two separate sessions** that demonstrate memory carrying across visits
+      - A pattern for **inspecting and seeding** memories from your own application
+      
+      ## How memory works
+      
+      When you attach a memory store to a session, it appears as a directory inside the agent's environment at `/mnt/memory/{store-name}`, and Claude reads and writes files there using its standard file tools. Your application has full read and write access to the same files through the REST API, so you can seed a store with known facts, audit what the agent has written, or export everything to your own systems.
+      
+      > **Beta feature.** Memory stores are part of the Claude Managed Agents public beta. The API may change before general availability. The Python SDK adds the required `anthropic-beta` header automatically for every method under `client.beta`.
+  markdown cell:
+    source:
+      ## Prerequisites
+      
+      - An Anthropic API key with access to the Claude Managed Agents beta. Set it as the `ANTHROPIC_API_KEY` environment variable.
+      - Python 3.11 or later.
+      - The Anthropic Python SDK. Memory store methods require a recent release:
+      
+      ```bash
+      uv add anthropic
+      # or: pip install -U anthropic
+      ```
+  markdown cell:
+    source:
+      ## Set up the client
+  code cell:
+    source:
+      import os
+      
+      from anthropic import Anthropic
+      from utilities import wait_for_idle_status
+      
+      MODEL = os.environ.get("COOKBOOK_MODEL", "claude-sonnet-4-6")
+      
+      client = Anthropic()
+  markdown cell:
+    source:
+      ### A helper for conversational turns
+      
+      Managed agent sessions are event-driven: you send a user message, then stream events until the session goes idle. To keep the rest of this guide readable, wrap that loop in a helper that prints the agent's replies and the files it touches under `/mnt/memory/`.
+  code cell:
+    source:
+      def run_turn(session_id: str, user_text: str) -> str:
+          """Send one user message and stream the agent's response until it goes idle."""
+          print(f"\n[user]  {user_text}")
+          reply_parts: list[str] = []
+      
+          with client.beta.sessions.events.stream(session_id) as stream:
+              client.beta.sessions.events.send(
+                  session_id,
+                  events=[
+                      {
+                          "type": "user.message",
+                          "content": [{"type": "text", "text": user_text}],
+                      }
+                  ],
+              )
+              for event in stream:
+                  if event.type == "agent.message":
+                      for block in event.content:
+                          if block.type == "text":
+                              reply_parts.append(block.text)
+                              print(f"[agent] {block.text}")
+      
+                  elif event.type == "agent.tool_use":
+                      # Surface reads and writes to the memory mount so you can see
+                      # the agent checking and updating its memory.
+                      target = event.input.get("file_path") or event.input.get("command", "")
+                      if "/mnt/memory/" in str(target):
+                          print(f"  [memory] {event.name}: {target}")
+      
+                  elif event.type == "session.status_idle":
+                      if event.stop_reason and event.stop_reason.type == "end_turn":
+                          break
+      
+                  elif event.type == "session.status_terminated":
+                      break
+      
+          wait_for_idle_status(client, session_id)
+          return "".join(reply_parts)
+  markdown cell:
+    source:
+      ## Step 1: Create a memory store
+      
+      A memory store is a named container for text files, scoped to your workspace. In a production deployment you would typically create one store per end user and keep a mapping from your user IDs to store IDs in your own database.
+      
+      The `description` you set here is rendered into the agent's system prompt whenever the store is attached, so use it to tell Claude what the store is for.
+  code cell:
+    source:
+      store = client.beta.memory_stores.create(
+          name="Shopper Preferences",
+          description=(
+              "Personal shopping preferences for a single customer: "
+              "sizes, style, budget, favorite brands, and materials to avoid."
+          ),
+      )
+      
+      print(store.id)  # memstore_01...
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          memstore_01NKkumXZXY8mEhoRA3xhBvN
+  markdown cell:
+    source:
+      ## Step 2: Define the shopping agent
+      
+      Every managed agent session needs an **agent** (the model, system prompt, and tools) and an **environment** (the container the agent runs in). You can create these once and reuse them across many customers and sessions.
+      
+      Give the agent the built-in `agent_toolset`, which includes the file tools it uses to read and write memory. The `agent_toolset_20260401` type string is the toolset's API identifier, not a model alias, so you do not need to update it when newer models ship.
+  code cell:
+    source:
+      environment = client.beta.environments.create(
+          name="shopping-demo",
+          config={"type": "cloud", "networking": {"type": "limited"}},
+      )
+      
+      agent = client.beta.agents.create(
+          name="Personal Shopper",
+          model=MODEL,
+          system=(
+              "You are a personal shopping assistant for a retail brand. "
+              "Help the customer find products that match their taste and budget, "
+              "and remember what you learn about them for future visits."
+          ),
+          tools=[
+              {
+                  "type": "agent_toolset_20260401",
+                  "default_config": {
+                      "enabled": True,
+                      "permission_policy": {"type": "always_allow"},
+                  },
+              }
+          ],
+      )
+      
+      print(agent.id)  # agent_01...
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          agent_011CaMbJoHMUyR5TjeFDtMG9
+  markdown cell:
+    source:
+      ## Step 3: First visit, where the agent learns preferences
+      
+      Now start a session and attach the memory store through the `resources` array. The `instructions` field is per-attachment guidance that tells Claude how to use this particular store in this particular session.
+      
+      Because this is the customer's first visit, the store is empty. Watch the `[memory]` lines in the output: the agent checks the store, finds nothing yet, and then writes a new file capturing what it learned.
+  code cell:
+    source:
+      memory_resource = {
+          "type": "memory_store",
+          "memory_store_id": store.id,
+          "access": "read_write",
+          "instructions": (
+              "This customer's personal preferences: sizes, style, budget, and "
+              "materials to avoid. Check it at the start of every conversation "
+              "and update it whenever you learn something new."
+          ),
+      }
+      
+      session_one = client.beta.sessions.create(
+          agent={"type": "agent", "id": agent.id, "version": agent.version},
+          environment_id=environment.id,
+          resources=[memory_resource],
+      )
+      
+      # The response tells you where the store is mounted inside the agent's environment.
+      for resource in session_one.resources:
+          if resource.type == "memory_store":
+              print(f"Mounted at {resource.mount_path}")
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          Mounted at /mnt/memory/shopper-preferences
+  code cell:
+    source:
+      run_turn(
+          session_one.id,
+          "Hi! I'm looking for a new jacket. A few things about me: I wear a size "
+          "medium, I only buy vegan leather (no animal leather please), my budget "
+          "is usually under $200, and I love earth tones. What would you suggest?",
+      )
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          [user]  Hi! I'm looking for a new jacket. A few things about me: I wear a size medium, I only buy vegan leather (no animal leather please), my budget is usually under $200, and I love earth tones. What would you suggest?
+          [agent] Let me check if I have any info about you on file, and save your preferences right away!
+            [memory] bash: cat /mnt/memory/shopper-preferences 2>/dev/null || echo "No file found"
+            [memory] write: /mnt/memory/shopper-preferences
+            [memory] write: /mnt/memory/shopper-preferences/preferences.md
+          [agent] I've saved your preferences for future visits! Now, here are some great **vegan leather jacket picks in earth tones under $200** that I'd suggest:
+          
+          ---
+          
+          ### 🌿 Top Picks for You
+          
+          #### 1. **Olive Moto Jacket** — ~$110–$150
+          A classic biker silhouette in a deep **olive green** faux leather. Look for styles with zip-detail sleeves and a fitted waist. Great for casual or edgy looks.
+          - *Why you'll love it:* Earthy tone, very on-trend, versatile for layering.
+          
+          #### 2. **Camel/Tan Oversized Blazer-Jacket** — ~$80–$160
+          A structured vegan leather blazer in **camel or tan** gives a polished, sophisticated vibe. Works dressed up or down.
+          - *Why you'll love it:* Classic earth tone, medium sizing runs well in this style, timeless investment.
+          
+          #### 3. **Rust/Cognac Bomber Jacket** — ~$90–$170
+          A **rust or cognac-toned** vegan leather bomber is a bold earth-tone statement piece with a relaxed, comfortable fit.
+          - *Why you'll love it:* Rich warm color, bomber silhouettes are flattering in medium, cozy feel.
+          
+          #### 4. **Terracotta Cropped Jacket** — ~$75–$140
+          A cropped vegan leather jacket in **terracotta** pairs beautifully with high-waisted jeans or wide-leg trousers.
+          - *Why you'll love it:* Trendy silhouette, stunning earthy hue, well within budget.
+          
+          ---
+          
+          ### 🛍️ Brands to Check Out
+          Here are some brands known for quality vegan leather within your budget:
+          - **ASOS** – Wide range of faux leather styles in various earth tones
+          - **Free People** – Boho-leaning earth tone options
+          - **H&M** – Affordable vegan leather jackets in seasonal colors
+          - **Lulus** – Great selection of trendy vegan styles under $100
+          - **Noize** – A dedicated vegan outerwear brand, often has earthy tones under $200
+          
+          ---
+          
+          Do any of these styles appeal to you? I can help narrow it down further — for example, do you prefer a **fitted or relaxed fit**, or a particular style like moto, bomber, or blazer? 😊
+  markdown cell:
+    source:
+      ## Step 4: Inspect what the agent saved
+      
+      Everything the agent writes to the mount is a regular memory document that your application can read, edit, or delete through the API. List the store's contents with `view="full"` to include file content in the response.
+      
+      This is how you build a "What we know about you" page in your product, sync memories into your own database, or let a human reviewer correct something the agent got wrong.
+  code cell:
+    source:
+      page = client.beta.memory_stores.memories.list(
+          store.id,
+          view="full",
+      )
+      
+      for memory in page.data:
+          if memory.type == "memory":
+              print(f"=== {memory.path} ===")
+              print(memory.content)
+              print()
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          === /preferences.md ===
+          # Shopper Preferences
+          
+          ## Sizes
+          - Tops/Jackets: Medium
+          
+          ## Style
+          - Loves earth tones (browns, tans, olive, terracotta, camel, rust, etc.)
+          - Interested in jackets
+          
+          ## Budget
+          - Usually under $200
+          
+          ## Materials
+          - VEGAN LEATHER ONLY — absolutely no animal leather
+          - (No other material restrictions noted yet)
+          
+          ## Favorite Brands
+          - None noted yet
+          
+          ## Other Notes
+          - First visit; preferences collected 2026-04-23
+  markdown cell:
+    source:
+      You should see a file (typically something like `/preferences.md`) containing the size, budget, material, and color preferences the customer mentioned, organized by topic. Claude chose the filename and structure on its own.
+  markdown cell:
+    source:
+      ## Step 5: Return visit, where the agent recalls on its own
+      
+      This is the moment that matters. Create a **brand new session** and attach the **same memory store**. The customer does not repeat any of their preferences, but the agent reads them from memory and tailors its recommendations accordingly.
+  code cell:
+    source:
+      session_two = client.beta.sessions.create(
+          agent={"type": "agent", "id": agent.id, "version": agent.version},
+          environment_id=environment.id,
+          resources=[memory_resource],  # same store, new session
+      )
+      
+      run_turn(
+          session_two.id,
+          "Hey, I'm back! I need a bag for work. Any recommendations?",
+      )
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          [user]  Hey, I'm back! I need a bag for work. Any recommendations?
+          [agent] Welcome back! Let me check your preferences before making any recommendations!
+            [memory] read: /mnt/memory/shopper-preferences
+            [memory] bash: ls /mnt/memory/shopper-preferences/
+            [memory] read: /mnt/memory/shopper-preferences/preferences.md
+          [agent] Great news — I already have your preferences on file! Here's what I kept in mind for you:
+          
+          - 🎨 **Style:** Earth tones (browns, tans, olive, camel, etc.)
+          - 💰 **Budget:** Under $200
+          - 🌱 **Materials:** Vegan leather only — no animal leather
+          
+          With that in mind, here are some work bag recommendations that would suit you perfectly:
+          
+          ---
+          
+          ### 🛍️ Top Picks for a Work Bag
+          
+          1. **Structured Tote in Camel Vegan Leather** — A polished, professional look in a warm earth tone. Fits a laptop + daily essentials. Great desk-to-commute bag.
+          
+          2. **Olive Canvas & Vegan Leather Trim Laptop Bag** — A sleek crossbody/shoulder bag with a relaxed but put-together vibe. The canvas keeps it lightweight.
+          
+          3. **Tan Vegan Leather Satchel** — A classic work satchel with a top handle and shoulder strap. Timeless silhouette, stays under budget.
+          
+          4. **Terracotta Faux-Leather Tote** — A bold but grounded earth tone that adds personality to a work outfit without being over the top.
+          
+          ---
+          
+          Do any of these styles appeal to you? I can also ask a few quick questions to narrow it down further:
+          
+          - **Do you prefer a tote, backpack, crossbody, or briefcase-style?**
+          - **Do you need to carry a laptop, and if so, what size?**
+          - **Do you lean more structured/professional or relaxed/casual for work?**
+  markdown cell:
+    source:
+      In the output, notice that the agent reads `/mnt/memory/shopper-preferences/` before answering, then recommends bags that are vegan leather, in earth tones, and under $200, even though this message mentioned none of those things. The preferences carried across from the first session.
+  markdown cell:
+    source:
+      ## Going further: patterns for production
+      
+      ### Seed a store from your existing data
+      
+      If you already know things about a customer from their account profile or purchase history, you can write them into the store before the first session so the agent starts informed. In a real application you would run this seeding step before any sessions are created; it appears here, after the demo, only so the main learn-then-recall flow above stays focused.
+  code cell:
+    source:
+      # Run before the cleanup cell below; `store` is deleted there.
+      seeded = client.beta.memory_stores.memories.create(
+          store.id,
+          path="/purchase-history.md",
+          content=(
+              "## Recent purchases\n"
+              "- Canvas tote, olive, $89 (Jan 2026)\n"
+              "- Wool beanie, rust, $34 (Dec 2025)\n"
+          ),
+      )
+      print(f"Seeded {seeded.path}")
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          Seeded /purchase-history.md
+  markdown cell:
+    source:
+      ### Combine per-customer and shared stores
+      
+      A session can attach up to eight memory stores, each with its own access level. A common pattern is one read-write store per customer plus a read-only store of brand-wide knowledge that every session shares.
+      
+      ```python
+      catalog = client.beta.memory_stores.create(
+          name="Product Catalog Notes",
+          description="Current promotions, sizing guidance, and stock notes.",
+      )
+      
+      session = client.beta.sessions.create(
+          agent=agent.id,
+          environment_id=environment.id,
+          resources=[
+              {
+                  "type": "memory_store",
+                  "memory_store_id": store.id,
+                  "access": "read_write",
+                  "instructions": "This customer's personal preferences.",
+              },
+              {
+                  "type": "memory_store",
+                  "memory_store_id": catalog.id,
+                  "access": "read_only",
+                  "instructions": "Brand-wide product guidance. Consult before recommending items.",
+              },
+          ],
+      )
+      ```
+      
+      ### Audit and correct
+      
+      Every write to a memory store is recorded as an immutable version with the session that made it. Use `client.beta.memory_stores.memory_versions.list(...)` to review history, and `client.beta.memory_stores.memories.update(...)` to correct a file that the agent got wrong. See the [Memory API reference](https://docs.anthropic.com/en/docs/managed-agents/memory) for the full surface.
+  markdown cell:
+    source:
+      ## Clean up
+      
+      Delete the resources you created while following this guide.
+  code cell:
+    source:
+      client.beta.sessions.archive(session_one.id)
+      client.beta.sessions.archive(session_two.id)
+      client.beta.memory_stores.delete(store.id)
+      client.beta.agents.archive(agent.id)
+      client.beta.environments.archive(environment.id)
+  markdown cell:
+    source:
+      ## Summary
+      
+      You built a shopping agent that remembers its customers across visits by:
+      
+      1. Creating a memory store for the customer
+      2. Attaching it to each session through `resources`
+      3. Letting Claude read and write files under `/mnt/memory/` as it learned new preferences
+      4. Inspecting those files from your own application through the Memory API
+      
+      From here you can map your own user IDs to memory store IDs, seed stores from your existing customer data, and layer shared read-only stores on top for brand-wide knowledge.
+      
+      ### Learn more
+      
+      - [Claude Managed Agents overview](https://docs.anthropic.com/en/docs/managed-agents/overview)
+      - [Memory stores API reference](https://docs.anthropic.com/en/docs/managed-agents/memory)
+      - [Session resources](https://docs.anthropic.com/en/docs/managed-agents/memory#attach-a-memory-store-to-a-session)

Generated by nbdime

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review

Recommendation: REQUEST_CHANGES

Summary

This notebook adds a well-structured shopping agent demo that demonstrates the Claude Managed Agents Memory Store feature. The run-to-completion flow is solid, resources are correctly cleaned up, and the model ID usage is correct. One inconsistency in a code snippet needs fixing before merge.

Actionable Feedback (1 item)
  • CMA_remember_user_preferences.ipynb (in the "Combine per-customer and shared stores" markdown cell) — The code snippet uses agent=agent.id (a plain string), but every runnable cell in this notebook uses the dict form agent={"type": "agent", "id": agent.id, "version": agent.version}. A reader copy-pasting this "production" pattern would silently get the latest agent version rather than a pinned one — the exact footgun that CMA_prompt_versioning_and_rollback.ipynb warns against. Change to the dict form to stay consistent.
Detailed Review

Code Quality

The cell flow is clean: every variable (store, environment, agent, memory_resource, session_one, session_two) is defined before it is used. A clean top-to-bottom run produces no undefined-variable errors. The run_turn helper is readable and correctly mirrors the event-loop pattern established in earlier CMA notebooks.

The seeding cell in "Going further" appears after both session demos, so neither demonstrated session ever sees the seeded file. The surrounding comment (# Run before the cleanup cell below) acknowledges only the cleanup ordering, not that the seeding has no visible effect on the sessions shown. A brief prose note that a third session would be needed to observe the seeded data would close the pedagogical gap. This is a suggestion, not a blocker.

Security

No secrets or API keys appear anywhere in the notebook source or saved outputs. No credentials are logged. Clean.

Suggestions

  • The run_turn stream loop exits silently if neither session.status_idle nor session.status_terminated fires before the stream closes (e.g., on a network drop). The returned string may be partial with no indication of the failure. This matches the pattern in the shared utilities.py, so it is consistent with the series — but a one-line comment noting the partial-response risk would help readers adapting this for production.
  • event.input.get("file_path") or event.input.get("command", "") falls back to "" for non-memory tools, which the guard then correctly suppresses. A short inline comment would make the intentional suppression of non-memory tool calls explicit.

Positive Notes

  • claude-sonnet-4-6 (non-dated alias) is used via the COOKBOOK_MODEL env var with fallback — correct per CLAUDE.md.
  • Both runnable sessions.create calls correctly pass agent={"type": "agent", "id": agent.id, "version": agent.version}.
  • Cleanup cell archives all five resources (session_one, session_two, store, agent, environment) — no leaks.
  • The beta callout in the introduction is exactly right: it flags the feature as public beta and notes that the SDK handles the required header automatically.
  • README table entry and registry.yaml addition are well-formed and consistent with existing entries.
  • Notebook outputs are retained, consistent with the repo convention for demo notebooks.

The illustrative markdown snippet under "Combine per-customer and
shared stores" still passed agent=agent.id while the runnable cells
were already using the pinned dict form. Align the snippet so readers
copying the production pattern get the same version-pinned shape.

https://claude.ai/code/session_01VkfVPdiJTWVh6Vj5owqRXH
@github-actions

Copy link
Copy Markdown

Notebook Changes

This PR modifies the following notebooks:

📓 managed_agents/CMA_remember_user_preferences.ipynb

View diff
nbdiff /dev/null managed_agents/CMA_remember_user_preferences.ipynb (fd673f83bb8b0d293e8f3aed6f2e95de55349b33)
--- /dev/null  2026-04-25 01:48:59.490838
+++ managed_agents/CMA_remember_user_preferences.ipynb (fd673f83bb8b0d293e8f3aed6f2e95de55349b33)  (no timestamp)
## added /cells:
+  markdown cell:
+    source:
+      # Build agents that remember your users
+      
+      Most agents start every conversation from scratch. A customer tells your shopping assistant their size, their budget, and which materials they avoid, and the next time they return, the agent has forgotten everything. They have to repeat themselves, and the experience feels transactional rather than personal.
+      
+      We just introduced **Memory** in Claude Managed Agents to solve this. A memory store is a persistent collection of text files that Claude can read and write during a session, and that survives across sessions. Anthropic hosts the storage, mounts it into the agent's environment, and prompts Claude on when and what to save — so there is no database, vector store, or retrieval pipeline for you to stand up. Attach the same store to every session for a given customer, and your agent accumulates knowledge about them over time.
+      
+      In this guide you will build a personal shopping assistant for a retail brand. The agent will learn a customer's preferences during their first visit, save them to a memory store, and recall them automatically on the next visit without being told again.
+      
+      ## What you will build
+      
+      - A **memory store** that holds one customer's shopping preferences
+      - A **shopping agent** configured to check and update that store
+      - **Two separate sessions** that demonstrate memory carrying across visits
+      - A pattern for **inspecting and seeding** memories from your own application
+      
+      ## How memory works
+      
+      When you attach a memory store to a session, it appears as a directory inside the agent's environment at `/mnt/memory/{store-name}`, and Claude reads and writes files there using its standard file tools. Your application has full read and write access to the same files through the REST API, so you can seed a store with known facts, audit what the agent has written, or export everything to your own systems.
+      
+      > **Beta feature.** Memory stores are part of the Claude Managed Agents public beta. The API may change before general availability. The Python SDK adds the required `anthropic-beta` header automatically for every method under `client.beta`.
+  markdown cell:
+    source:
+      ## Prerequisites
+      
+      - An Anthropic API key with access to the Claude Managed Agents beta. Set it as the `ANTHROPIC_API_KEY` environment variable.
+      - Python 3.11 or later.
+      - The Anthropic Python SDK. Memory store methods require a recent release:
+      
+      ```bash
+      uv add anthropic
+      # or: pip install -U anthropic
+      ```
+  markdown cell:
+    source:
+      ## Set up the client
+  code cell:
+    source:
+      import os
+      
+      from anthropic import Anthropic
+      from utilities import wait_for_idle_status
+      
+      MODEL = os.environ.get("COOKBOOK_MODEL", "claude-sonnet-4-6")
+      
+      client = Anthropic()
+  markdown cell:
+    source:
+      ### A helper for conversational turns
+      
+      Managed agent sessions are event-driven: you send a user message, then stream events until the session goes idle. To keep the rest of this guide readable, wrap that loop in a helper that prints the agent's replies and the files it touches under `/mnt/memory/`.
+  code cell:
+    source:
+      def run_turn(session_id: str, user_text: str) -> str:
+          """Send one user message and stream the agent's response until it goes idle."""
+          print(f"\n[user]  {user_text}")
+          reply_parts: list[str] = []
+      
+          with client.beta.sessions.events.stream(session_id) as stream:
+              client.beta.sessions.events.send(
+                  session_id,
+                  events=[
+                      {
+                          "type": "user.message",
+                          "content": [{"type": "text", "text": user_text}],
+                      }
+                  ],
+              )
+              for event in stream:
+                  if event.type == "agent.message":
+                      for block in event.content:
+                          if block.type == "text":
+                              reply_parts.append(block.text)
+                              print(f"[agent] {block.text}")
+      
+                  elif event.type == "agent.tool_use":
+                      # Surface reads and writes to the memory mount so you can see
+                      # the agent checking and updating its memory.
+                      target = event.input.get("file_path") or event.input.get("command", "")
+                      if "/mnt/memory/" in str(target):
+                          print(f"  [memory] {event.name}: {target}")
+      
+                  elif event.type == "session.status_idle":
+                      if event.stop_reason and event.stop_reason.type == "end_turn":
+                          break
+      
+                  elif event.type == "session.status_terminated":
+                      break
+      
+          wait_for_idle_status(client, session_id)
+          return "".join(reply_parts)
+  markdown cell:
+    source:
+      ## Step 1: Create a memory store
+      
+      A memory store is a named container for text files, scoped to your workspace. In a production deployment you would typically create one store per end user and keep a mapping from your user IDs to store IDs in your own database.
+      
+      The `description` you set here is rendered into the agent's system prompt whenever the store is attached, so use it to tell Claude what the store is for.
+  code cell:
+    source:
+      store = client.beta.memory_stores.create(
+          name="Shopper Preferences",
+          description=(
+              "Personal shopping preferences for a single customer: "
+              "sizes, style, budget, favorite brands, and materials to avoid."
+          ),
+      )
+      
+      print(store.id)  # memstore_01...
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          memstore_01NKkumXZXY8mEhoRA3xhBvN
+  markdown cell:
+    source:
+      ## Step 2: Define the shopping agent
+      
+      Every managed agent session needs an **agent** (the model, system prompt, and tools) and an **environment** (the container the agent runs in). You can create these once and reuse them across many customers and sessions.
+      
+      Give the agent the built-in `agent_toolset`, which includes the file tools it uses to read and write memory. The `agent_toolset_20260401` type string is the toolset's API identifier, not a model alias, so you do not need to update it when newer models ship.
+  code cell:
+    source:
+      environment = client.beta.environments.create(
+          name="shopping-demo",
+          config={"type": "cloud", "networking": {"type": "limited"}},
+      )
+      
+      agent = client.beta.agents.create(
+          name="Personal Shopper",
+          model=MODEL,
+          system=(
+              "You are a personal shopping assistant for a retail brand. "
+              "Help the customer find products that match their taste and budget, "
+              "and remember what you learn about them for future visits."
+          ),
+          tools=[
+              {
+                  "type": "agent_toolset_20260401",
+                  "default_config": {
+                      "enabled": True,
+                      "permission_policy": {"type": "always_allow"},
+                  },
+              }
+          ],
+      )
+      
+      print(agent.id)  # agent_01...
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          agent_011CaMbJoHMUyR5TjeFDtMG9
+  markdown cell:
+    source:
+      ## Step 3: First visit, where the agent learns preferences
+      
+      Now start a session and attach the memory store through the `resources` array. The `instructions` field is per-attachment guidance that tells Claude how to use this particular store in this particular session.
+      
+      Because this is the customer's first visit, the store is empty. Watch the `[memory]` lines in the output: the agent checks the store, finds nothing yet, and then writes a new file capturing what it learned.
+  code cell:
+    source:
+      memory_resource = {
+          "type": "memory_store",
+          "memory_store_id": store.id,
+          "access": "read_write",
+          "instructions": (
+              "This customer's personal preferences: sizes, style, budget, and "
+              "materials to avoid. Check it at the start of every conversation "
+              "and update it whenever you learn something new."
+          ),
+      }
+      
+      session_one = client.beta.sessions.create(
+          agent={"type": "agent", "id": agent.id, "version": agent.version},
+          environment_id=environment.id,
+          resources=[memory_resource],
+      )
+      
+      # The response tells you where the store is mounted inside the agent's environment.
+      for resource in session_one.resources:
+          if resource.type == "memory_store":
+              print(f"Mounted at {resource.mount_path}")
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          Mounted at /mnt/memory/shopper-preferences
+  code cell:
+    source:
+      run_turn(
+          session_one.id,
+          "Hi! I'm looking for a new jacket. A few things about me: I wear a size "
+          "medium, I only buy vegan leather (no animal leather please), my budget "
+          "is usually under $200, and I love earth tones. What would you suggest?",
+      )
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          [user]  Hi! I'm looking for a new jacket. A few things about me: I wear a size medium, I only buy vegan leather (no animal leather please), my budget is usually under $200, and I love earth tones. What would you suggest?
+          [agent] Let me check if I have any info about you on file, and save your preferences right away!
+            [memory] bash: cat /mnt/memory/shopper-preferences 2>/dev/null || echo "No file found"
+            [memory] write: /mnt/memory/shopper-preferences
+            [memory] write: /mnt/memory/shopper-preferences/preferences.md
+          [agent] I've saved your preferences for future visits! Now, here are some great **vegan leather jacket picks in earth tones under $200** that I'd suggest:
+          
+          ---
+          
+          ### 🌿 Top Picks for You
+          
+          #### 1. **Olive Moto Jacket** — ~$110–$150
+          A classic biker silhouette in a deep **olive green** faux leather. Look for styles with zip-detail sleeves and a fitted waist. Great for casual or edgy looks.
+          - *Why you'll love it:* Earthy tone, very on-trend, versatile for layering.
+          
+          #### 2. **Camel/Tan Oversized Blazer-Jacket** — ~$80–$160
+          A structured vegan leather blazer in **camel or tan** gives a polished, sophisticated vibe. Works dressed up or down.
+          - *Why you'll love it:* Classic earth tone, medium sizing runs well in this style, timeless investment.
+          
+          #### 3. **Rust/Cognac Bomber Jacket** — ~$90–$170
+          A **rust or cognac-toned** vegan leather bomber is a bold earth-tone statement piece with a relaxed, comfortable fit.
+          - *Why you'll love it:* Rich warm color, bomber silhouettes are flattering in medium, cozy feel.
+          
+          #### 4. **Terracotta Cropped Jacket** — ~$75–$140
+          A cropped vegan leather jacket in **terracotta** pairs beautifully with high-waisted jeans or wide-leg trousers.
+          - *Why you'll love it:* Trendy silhouette, stunning earthy hue, well within budget.
+          
+          ---
+          
+          ### 🛍️ Brands to Check Out
+          Here are some brands known for quality vegan leather within your budget:
+          - **ASOS** – Wide range of faux leather styles in various earth tones
+          - **Free People** – Boho-leaning earth tone options
+          - **H&M** – Affordable vegan leather jackets in seasonal colors
+          - **Lulus** – Great selection of trendy vegan styles under $100
+          - **Noize** – A dedicated vegan outerwear brand, often has earthy tones under $200
+          
+          ---
+          
+          Do any of these styles appeal to you? I can help narrow it down further — for example, do you prefer a **fitted or relaxed fit**, or a particular style like moto, bomber, or blazer? 😊
+  markdown cell:
+    source:
+      ## Step 4: Inspect what the agent saved
+      
+      Everything the agent writes to the mount is a regular memory document that your application can read, edit, or delete through the API. List the store's contents with `view="full"` to include file content in the response.
+      
+      This is how you build a "What we know about you" page in your product, sync memories into your own database, or let a human reviewer correct something the agent got wrong.
+  code cell:
+    source:
+      page = client.beta.memory_stores.memories.list(
+          store.id,
+          view="full",
+      )
+      
+      for memory in page.data:
+          if memory.type == "memory":
+              print(f"=== {memory.path} ===")
+              print(memory.content)
+              print()
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          === /preferences.md ===
+          # Shopper Preferences
+          
+          ## Sizes
+          - Tops/Jackets: Medium
+          
+          ## Style
+          - Loves earth tones (browns, tans, olive, terracotta, camel, rust, etc.)
+          - Interested in jackets
+          
+          ## Budget
+          - Usually under $200
+          
+          ## Materials
+          - VEGAN LEATHER ONLY — absolutely no animal leather
+          - (No other material restrictions noted yet)
+          
+          ## Favorite Brands
+          - None noted yet
+          
+          ## Other Notes
+          - First visit; preferences collected 2026-04-23
+  markdown cell:
+    source:
+      You should see a file (typically something like `/preferences.md`) containing the size, budget, material, and color preferences the customer mentioned, organized by topic. Claude chose the filename and structure on its own.
+  markdown cell:
+    source:
+      ## Step 5: Return visit, where the agent recalls on its own
+      
+      This is the moment that matters. Create a **brand new session** and attach the **same memory store**. The customer does not repeat any of their preferences, but the agent reads them from memory and tailors its recommendations accordingly.
+  code cell:
+    source:
+      session_two = client.beta.sessions.create(
+          agent={"type": "agent", "id": agent.id, "version": agent.version},
+          environment_id=environment.id,
+          resources=[memory_resource],  # same store, new session
+      )
+      
+      run_turn(
+          session_two.id,
+          "Hey, I'm back! I need a bag for work. Any recommendations?",
+      )
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          [user]  Hey, I'm back! I need a bag for work. Any recommendations?
+          [agent] Welcome back! Let me check your preferences before making any recommendations!
+            [memory] read: /mnt/memory/shopper-preferences
+            [memory] bash: ls /mnt/memory/shopper-preferences/
+            [memory] read: /mnt/memory/shopper-preferences/preferences.md
+          [agent] Great news — I already have your preferences on file! Here's what I kept in mind for you:
+          
+          - 🎨 **Style:** Earth tones (browns, tans, olive, camel, etc.)
+          - 💰 **Budget:** Under $200
+          - 🌱 **Materials:** Vegan leather only — no animal leather
+          
+          With that in mind, here are some work bag recommendations that would suit you perfectly:
+          
+          ---
+          
+          ### 🛍️ Top Picks for a Work Bag
+          
+          1. **Structured Tote in Camel Vegan Leather** — A polished, professional look in a warm earth tone. Fits a laptop + daily essentials. Great desk-to-commute bag.
+          
+          2. **Olive Canvas & Vegan Leather Trim Laptop Bag** — A sleek crossbody/shoulder bag with a relaxed but put-together vibe. The canvas keeps it lightweight.
+          
+          3. **Tan Vegan Leather Satchel** — A classic work satchel with a top handle and shoulder strap. Timeless silhouette, stays under budget.
+          
+          4. **Terracotta Faux-Leather Tote** — A bold but grounded earth tone that adds personality to a work outfit without being over the top.
+          
+          ---
+          
+          Do any of these styles appeal to you? I can also ask a few quick questions to narrow it down further:
+          
+          - **Do you prefer a tote, backpack, crossbody, or briefcase-style?**
+          - **Do you need to carry a laptop, and if so, what size?**
+          - **Do you lean more structured/professional or relaxed/casual for work?**
+  markdown cell:
+    source:
+      In the output, notice that the agent reads `/mnt/memory/shopper-preferences/` before answering, then recommends bags that are vegan leather, in earth tones, and under $200, even though this message mentioned none of those things. The preferences carried across from the first session.
+  markdown cell:
+    source:
+      ## Going further: patterns for production
+      
+      ### Seed a store from your existing data
+      
+      If you already know things about a customer from their account profile or purchase history, you can write them into the store before the first session so the agent starts informed. In a real application you would run this seeding step before any sessions are created; it appears here, after the demo, only so the main learn-then-recall flow above stays focused.
+  code cell:
+    source:
+      # Run before the cleanup cell below; `store` is deleted there.
+      seeded = client.beta.memory_stores.memories.create(
+          store.id,
+          path="/purchase-history.md",
+          content=(
+              "## Recent purchases\n"
+              "- Canvas tote, olive, $89 (Jan 2026)\n"
+              "- Wool beanie, rust, $34 (Dec 2025)\n"
+          ),
+      )
+      print(f"Seeded {seeded.path}")
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          Seeded /purchase-history.md
+  markdown cell:
+    source:
+      ### Combine per-customer and shared stores
+      
+      A session can attach up to eight memory stores, each with its own access level. A common pattern is one read-write store per customer plus a read-only store of brand-wide knowledge that every session shares.
+      
+      ```python
+      catalog = client.beta.memory_stores.create(
+          name="Product Catalog Notes",
+          description="Current promotions, sizing guidance, and stock notes.",
+      )
+      
+      session = client.beta.sessions.create(
+          agent={"type": "agent", "id": agent.id, "version": agent.version},
+          environment_id=environment.id,
+          resources=[
+              {
+                  "type": "memory_store",
+                  "memory_store_id": store.id,
+                  "access": "read_write",
+                  "instructions": "This customer's personal preferences.",
+              },
+              {
+                  "type": "memory_store",
+                  "memory_store_id": catalog.id,
+                  "access": "read_only",
+                  "instructions": "Brand-wide product guidance. Consult before recommending items.",
+              },
+          ],
+      )
+      ```
+      
+      ### Audit and correct
+      
+      Every write to a memory store is recorded as an immutable version with the session that made it. Use `client.beta.memory_stores.memory_versions.list(...)` to review history, and `client.beta.memory_stores.memories.update(...)` to correct a file that the agent got wrong. See the [Memory API reference](https://docs.anthropic.com/en/docs/managed-agents/memory) for the full surface.
+  markdown cell:
+    source:
+      ## Clean up
+      
+      Delete the resources you created while following this guide.
+  code cell:
+    source:
+      client.beta.sessions.archive(session_one.id)
+      client.beta.sessions.archive(session_two.id)
+      client.beta.memory_stores.delete(store.id)
+      client.beta.agents.archive(agent.id)
+      client.beta.environments.archive(environment.id)
+  markdown cell:
+    source:
+      ## Summary
+      
+      You built a shopping agent that remembers its customers across visits by:
+      
+      1. Creating a memory store for the customer
+      2. Attaching it to each session through `resources`
+      3. Letting Claude read and write files under `/mnt/memory/` as it learned new preferences
+      4. Inspecting those files from your own application through the Memory API
+      
+      From here you can map your own user IDs to memory store IDs, seed stores from your existing customer data, and layer shared read-only stores on top for brand-wide knowledge.
+      
+      ### Learn more
+      
+      - [Claude Managed Agents overview](https://docs.anthropic.com/en/docs/managed-agents/overview)
+      - [Memory stores API reference](https://docs.anthropic.com/en/docs/managed-agents/memory)
+      - [Session resources](https://docs.anthropic.com/en/docs/managed-agents/memory#attach-a-memory-store-to-a-session)

Generated by nbdime

@github-actions

Copy link
Copy Markdown

Model Check Results ✅

Files reviewed: managed_agents/CMA_remember_user_preferences.ipynb, managed_agents/README.md

Findings

File Line Model ID Status
managed_agents/CMA_remember_user_preferences.ipynb 67 claude-sonnet-4-6 ✅ Valid
managed_agents/README.md (no model references) ✅ N/A

Summary

No model issues found. The notebook uses claude-sonnet-4-6 as the default value for COOKBOOK_MODEL, which is:

  • A current, non-deprecated Claude model (Claude Sonnet 4.6)
  • Correctly using the non-dated alias format (not claude-sonnet-4-6-20250514)
  • Consistent with the project's CLAUDE.md guidelines

🤖 Generated with Claude Code

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review

Recommendation: REQUEST_CHANGES

Summary

Adds a well-structured memory stores tutorial for Claude Managed Agents, showing a shopping assistant that learns customer preferences in one session and recalls them in the next. The notebook is clear and educationally sound, but two code correctness issues need to be fixed before merging.

Actionable Feedback (6 items)
  • CMA_remember_user_preferences.ipynb (cleanup cell) — Add wait_for_idle_status calls before both archive calls, matching the series pattern. The current code could cause a 400 error if copied closer in time to the last run_turn. Example fix:

    wait_for_idle_status(client, session_one.id)
    wait_for_idle_status(client, session_two.id)
    client.beta.sessions.archive(session_one.id)
    client.beta.sessions.archive(session_two.id)
    ...
  • CMA_remember_user_preferences.ipynb (in cell with def run_turn) — The session.status_idle handler only breaks on end_turn; any other stop reason (e.g. requires_action) silently continues the loop indefinitely. Add a comment (or an else: break) explaining the intent, as CMA_gate_human_in_the_loop.ipynb does for the same pattern.

  • CMA_remember_user_preferences.ipynb (in cell with target = event.input.get) — Guard against event.input being None: inp = event.input or {}; target = inp.get("file_path") or inp.get("command", "").

  • CMA_remember_user_preferences.ipynb (seeding cell) — Convert the inline comment # Run before the cleanup cell below to a markdown > **Note:** callout before the cell so it's more visible in notebook rendering.

  • CMA_remember_user_preferences.ipynb — Add a %%capture\n%pip install -q "anthropic>=0.91.0" cell before the imports, matching the pattern in other CMA notebooks that require a minimum SDK version for beta API surfaces.

  • CMA_remember_user_preferences.ipynb (Summary cell) — Add pointers to sibling CMA notebooks (e.g. CMA_iterate_fix_failing_tests.ipynb as the series entry point, CMA_operate_in_production.ipynb for production patterns) — every other CMA tutorial does this.

Detailed Review

Code Quality

The run_turn helper correctly uses the "open stream first, then send" ordering that the series establishes as the canonical pattern, and the memory-specific [memory] surface logging is a nice touch that makes the notebook's core mechanic visible without being noisy. The memory_resource dict is appropriately defined once and reused for session_two, teaching the reuse pattern rather than hiding it.

The two blocking issues are:

  1. Missing wait_for_idle_status in cleanup — Every other CMA notebook places explicit idle waits before archive calls. This notebook skips them. In a live demo it's unlikely to fail because session_one went idle many cells earlier, but as a copy-paste pattern it is dangerous.
  2. Partial session.status_idle handling — The if stop_reason.type == "end_turn": break branch is correct, but if any other idle reason arrives the stream loop hangs silently. A comment or else branch is needed.

Security

No secrets hardcoded. client = Anthropic() reads ANTHROPIC_API_KEY from the environment correctly. No injection risks.

Suggestions

  • dotenv: The other standalone CMA tutorials (CMA_prompt_versioning_and_rollback.ipynb) include a load_dotenv() call for readers with a .env file. The five earlier CMA series notebooks don't use it either, so this is consistent within the series — but inconsistent with CLAUDE.md guidance. Worth a deliberate choice.
  • Cross-notebook links in Summary: Every other CMA tutorial links to siblings. Adding a "See also" list would help readers navigate the series.
  • The agent_toolset_20260401 note ("not a model alias, so you don't need to update it when newer models ship") is an excellent call-out that other notebooks don't have.

Positive Notes

  • Model ID is claude-sonnet-4-6 (non-dated alias) with COOKBOOK_MODEL env override — exactly correct per CLAUDE.md.
  • Beta feature callout block at the top is clear and appropriately scoped.
  • Cleanup cell covers all five created resources (session_one, session_two, store, agent, environment) — nothing is orphaned.
  • Registry and README entries are both present, correctly formatted, and consistent with other CMA entries.
  • Cell outputs are preserved per project convention.

rlancemartin
rlancemartin previously approved these changes Apr 25, 2026
- Add wait_for_idle_status calls before session archive in cleanup
- Break out of run_turn on any status_idle, not just end_turn
- Guard event.input against None in tool_use handler
- Move seeding-order warning to a markdown Note callout
- Add %pip install cell for minimum SDK version
- Link to sibling CMA notebooks from the summary
@github-actions

Copy link
Copy Markdown

Notebook Changes

This PR modifies the following notebooks:

📓 managed_agents/CMA_remember_user_preferences.ipynb

View diff
nbdiff /dev/null managed_agents/CMA_remember_user_preferences.ipynb (293bd2a7734aa69f693e826a9268d96838577c02)
--- /dev/null  2026-04-25 22:02:03.158655
+++ managed_agents/CMA_remember_user_preferences.ipynb (293bd2a7734aa69f693e826a9268d96838577c02)  (no timestamp)
## added /cells:
+  markdown cell:
+    source:
+      # Build agents that remember your users
+      
+      Most agents start every conversation from scratch. A customer tells your shopping assistant their size, their budget, and which materials they avoid, and the next time they return, the agent has forgotten everything. They have to repeat themselves, and the experience feels transactional rather than personal.
+      
+      We just introduced **Memory** in Claude Managed Agents to solve this. A memory store is a persistent collection of text files that Claude can read and write during a session, and that survives across sessions. Anthropic hosts the storage, mounts it into the agent's environment, and prompts Claude on when and what to save — so there is no database, vector store, or retrieval pipeline for you to stand up. Attach the same store to every session for a given customer, and your agent accumulates knowledge about them over time.
+      
+      In this guide you will build a personal shopping assistant for a retail brand. The agent will learn a customer's preferences during their first visit, save them to a memory store, and recall them automatically on the next visit without being told again.
+      
+      ## What you will build
+      
+      - A **memory store** that holds one customer's shopping preferences
+      - A **shopping agent** configured to check and update that store
+      - **Two separate sessions** that demonstrate memory carrying across visits
+      - A pattern for **inspecting and seeding** memories from your own application
+      
+      ## How memory works
+      
+      When you attach a memory store to a session, it appears as a directory inside the agent's environment at `/mnt/memory/{store-name}`, and Claude reads and writes files there using its standard file tools. Your application has full read and write access to the same files through the REST API, so you can seed a store with known facts, audit what the agent has written, or export everything to your own systems.
+      
+      > **Beta feature.** Memory stores are part of the Claude Managed Agents public beta. The API may change before general availability. The Python SDK adds the required `anthropic-beta` header automatically for every method under `client.beta`.
+  markdown cell:
+    source:
+      ## Prerequisites
+      
+      - An Anthropic API key with access to the Claude Managed Agents beta. Set it as the `ANTHROPIC_API_KEY` environment variable.
+      - Python 3.11 or later.
+      - The Anthropic Python SDK. Memory store methods require a recent release:
+      
+      ```bash
+      uv add anthropic
+      # or: pip install -U anthropic
+      ```
+  markdown cell:
+    source:
+      ## Set up the client
+  code cell:
+    source:
+      %%capture
+      %pip install -q "anthropic>=0.91.0"
+  code cell:
+    source:
+      import os
+      
+      from anthropic import Anthropic
+      from utilities import wait_for_idle_status
+      
+      MODEL = os.environ.get("COOKBOOK_MODEL", "claude-sonnet-4-6")
+      
+      client = Anthropic()
+  markdown cell:
+    source:
+      ### A helper for conversational turns
+      
+      Managed agent sessions are event-driven: you send a user message, then stream events until the session goes idle. To keep the rest of this guide readable, wrap that loop in a helper that prints the agent's replies and the files it touches under `/mnt/memory/`.
+  code cell:
+    source:
+      def run_turn(session_id: str, user_text: str) -> str:
+          """Send one user message and stream the agent's response until it goes idle."""
+          print(f"\n[user]  {user_text}")
+          reply_parts: list[str] = []
+      
+          with client.beta.sessions.events.stream(session_id) as stream:
+              client.beta.sessions.events.send(
+                  session_id,
+                  events=[
+                      {
+                          "type": "user.message",
+                          "content": [{"type": "text", "text": user_text}],
+                      }
+                  ],
+              )
+              for event in stream:
+                  if event.type == "agent.message":
+                      for block in event.content:
+                          if block.type == "text":
+                              reply_parts.append(block.text)
+                              print(f"[agent] {block.text}")
+      
+                  elif event.type == "agent.tool_use":
+                      # Surface reads and writes to the memory mount so you can see
+                      # the agent checking and updating its memory.
+                      inp = event.input or {}
+                      target = inp.get("file_path") or inp.get("command", "")
+                      if "/mnt/memory/" in str(target):
+                          print(f"  [memory] {event.name}: {target}")
+      
+                  elif event.type == "session.status_idle":
+                      if event.stop_reason and event.stop_reason.type == "end_turn":
+                          break
+                      # This notebook only sends user.message events, so end_turn is the
+                      # only idle reason we expect. Break on anything else as well so an
+                      # unexpected stop_reason (such as requires_action) cannot hang the loop.
+                      break
+      
+                  elif event.type == "session.status_terminated":
+                      break
+      
+          wait_for_idle_status(client, session_id)
+          return "".join(reply_parts)
+  markdown cell:
+    source:
+      ## Step 1: Create a memory store
+      
+      A memory store is a named container for text files, scoped to your workspace. In a production deployment you would typically create one store per end user and keep a mapping from your user IDs to store IDs in your own database.
+      
+      The `description` you set here is rendered into the agent's system prompt whenever the store is attached, so use it to tell Claude what the store is for.
+  code cell:
+    source:
+      store = client.beta.memory_stores.create(
+          name="Shopper Preferences",
+          description=(
+              "Personal shopping preferences for a single customer: "
+              "sizes, style, budget, favorite brands, and materials to avoid."
+          ),
+      )
+      
+      print(store.id)  # memstore_01...
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          memstore_01NKkumXZXY8mEhoRA3xhBvN
+  markdown cell:
+    source:
+      ## Step 2: Define the shopping agent
+      
+      Every managed agent session needs an **agent** (the model, system prompt, and tools) and an **environment** (the container the agent runs in). You can create these once and reuse them across many customers and sessions.
+      
+      Give the agent the built-in `agent_toolset`, which includes the file tools it uses to read and write memory. The `agent_toolset_20260401` type string is the toolset's API identifier, not a model alias, so you do not need to update it when newer models ship.
+  code cell:
+    source:
+      environment = client.beta.environments.create(
+          name="shopping-demo",
+          config={"type": "cloud", "networking": {"type": "limited"}},
+      )
+      
+      agent = client.beta.agents.create(
+          name="Personal Shopper",
+          model=MODEL,
+          system=(
+              "You are a personal shopping assistant for a retail brand. "
+              "Help the customer find products that match their taste and budget, "
+              "and remember what you learn about them for future visits."
+          ),
+          tools=[
+              {
+                  "type": "agent_toolset_20260401",
+                  "default_config": {
+                      "enabled": True,
+                      "permission_policy": {"type": "always_allow"},
+                  },
+              }
+          ],
+      )
+      
+      print(agent.id)  # agent_01...
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          agent_011CaMbJoHMUyR5TjeFDtMG9
+  markdown cell:
+    source:
+      ## Step 3: First visit, where the agent learns preferences
+      
+      Now start a session and attach the memory store through the `resources` array. The `instructions` field is per-attachment guidance that tells Claude how to use this particular store in this particular session.
+      
+      Because this is the customer's first visit, the store is empty. Watch the `[memory]` lines in the output: the agent checks the store, finds nothing yet, and then writes a new file capturing what it learned.
+  code cell:
+    source:
+      memory_resource = {
+          "type": "memory_store",
+          "memory_store_id": store.id,
+          "access": "read_write",
+          "instructions": (
+              "This customer's personal preferences: sizes, style, budget, and "
+              "materials to avoid. Check it at the start of every conversation "
+              "and update it whenever you learn something new."
+          ),
+      }
+      
+      session_one = client.beta.sessions.create(
+          agent={"type": "agent", "id": agent.id, "version": agent.version},
+          environment_id=environment.id,
+          resources=[memory_resource],
+      )
+      
+      # The response tells you where the store is mounted inside the agent's environment.
+      for resource in session_one.resources:
+          if resource.type == "memory_store":
+              print(f"Mounted at {resource.mount_path}")
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          Mounted at /mnt/memory/shopper-preferences
+  code cell:
+    source:
+      run_turn(
+          session_one.id,
+          "Hi! I'm looking for a new jacket. A few things about me: I wear a size "
+          "medium, I only buy vegan leather (no animal leather please), my budget "
+          "is usually under $200, and I love earth tones. What would you suggest?",
+      )
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          [user]  Hi! I'm looking for a new jacket. A few things about me: I wear a size medium, I only buy vegan leather (no animal leather please), my budget is usually under $200, and I love earth tones. What would you suggest?
+          [agent] Let me check if I have any info about you on file, and save your preferences right away!
+            [memory] bash: cat /mnt/memory/shopper-preferences 2>/dev/null || echo "No file found"
+            [memory] write: /mnt/memory/shopper-preferences
+            [memory] write: /mnt/memory/shopper-preferences/preferences.md
+          [agent] I've saved your preferences for future visits! Now, here are some great **vegan leather jacket picks in earth tones under $200** that I'd suggest:
+          
+          ---
+          
+          ### 🌿 Top Picks for You
+          
+          #### 1. **Olive Moto Jacket** — ~$110–$150
+          A classic biker silhouette in a deep **olive green** faux leather. Look for styles with zip-detail sleeves and a fitted waist. Great for casual or edgy looks.
+          - *Why you'll love it:* Earthy tone, very on-trend, versatile for layering.
+          
+          #### 2. **Camel/Tan Oversized Blazer-Jacket** — ~$80–$160
+          A structured vegan leather blazer in **camel or tan** gives a polished, sophisticated vibe. Works dressed up or down.
+          - *Why you'll love it:* Classic earth tone, medium sizing runs well in this style, timeless investment.
+          
+          #### 3. **Rust/Cognac Bomber Jacket** — ~$90–$170
+          A **rust or cognac-toned** vegan leather bomber is a bold earth-tone statement piece with a relaxed, comfortable fit.
+          - *Why you'll love it:* Rich warm color, bomber silhouettes are flattering in medium, cozy feel.
+          
+          #### 4. **Terracotta Cropped Jacket** — ~$75–$140
+          A cropped vegan leather jacket in **terracotta** pairs beautifully with high-waisted jeans or wide-leg trousers.
+          - *Why you'll love it:* Trendy silhouette, stunning earthy hue, well within budget.
+          
+          ---
+          
+          ### 🛍️ Brands to Check Out
+          Here are some brands known for quality vegan leather within your budget:
+          - **ASOS** – Wide range of faux leather styles in various earth tones
+          - **Free People** – Boho-leaning earth tone options
+          - **H&M** – Affordable vegan leather jackets in seasonal colors
+          - **Lulus** – Great selection of trendy vegan styles under $100
+          - **Noize** – A dedicated vegan outerwear brand, often has earthy tones under $200
+          
+          ---
+          
+          Do any of these styles appeal to you? I can help narrow it down further — for example, do you prefer a **fitted or relaxed fit**, or a particular style like moto, bomber, or blazer? 😊
+  markdown cell:
+    source:
+      ## Step 4: Inspect what the agent saved
+      
+      Everything the agent writes to the mount is a regular memory document that your application can read, edit, or delete through the API. List the store's contents with `view="full"` to include file content in the response.
+      
+      This is how you build a "What we know about you" page in your product, sync memories into your own database, or let a human reviewer correct something the agent got wrong.
+  code cell:
+    source:
+      page = client.beta.memory_stores.memories.list(
+          store.id,
+          view="full",
+      )
+      
+      for memory in page.data:
+          if memory.type == "memory":
+              print(f"=== {memory.path} ===")
+              print(memory.content)
+              print()
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          === /preferences.md ===
+          # Shopper Preferences
+          
+          ## Sizes
+          - Tops/Jackets: Medium
+          
+          ## Style
+          - Loves earth tones (browns, tans, olive, terracotta, camel, rust, etc.)
+          - Interested in jackets
+          
+          ## Budget
+          - Usually under $200
+          
+          ## Materials
+          - VEGAN LEATHER ONLY — absolutely no animal leather
+          - (No other material restrictions noted yet)
+          
+          ## Favorite Brands
+          - None noted yet
+          
+          ## Other Notes
+          - First visit; preferences collected 2026-04-23
+  markdown cell:
+    source:
+      You should see a file (typically something like `/preferences.md`) containing the size, budget, material, and color preferences the customer mentioned, organized by topic. Claude chose the filename and structure on its own.
+  markdown cell:
+    source:
+      ## Step 5: Return visit, where the agent recalls on its own
+      
+      This is the moment that matters. Create a **brand new session** and attach the **same memory store**. The customer does not repeat any of their preferences, but the agent reads them from memory and tailors its recommendations accordingly.
+  code cell:
+    source:
+      session_two = client.beta.sessions.create(
+          agent={"type": "agent", "id": agent.id, "version": agent.version},
+          environment_id=environment.id,
+          resources=[memory_resource],  # same store, new session
+      )
+      
+      run_turn(
+          session_two.id,
+          "Hey, I'm back! I need a bag for work. Any recommendations?",
+      )
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          [user]  Hey, I'm back! I need a bag for work. Any recommendations?
+          [agent] Welcome back! Let me check your preferences before making any recommendations!
+            [memory] read: /mnt/memory/shopper-preferences
+            [memory] bash: ls /mnt/memory/shopper-preferences/
+            [memory] read: /mnt/memory/shopper-preferences/preferences.md
+          [agent] Great news — I already have your preferences on file! Here's what I kept in mind for you:
+          
+          - 🎨 **Style:** Earth tones (browns, tans, olive, camel, etc.)
+          - 💰 **Budget:** Under $200
+          - 🌱 **Materials:** Vegan leather only — no animal leather
+          
+          With that in mind, here are some work bag recommendations that would suit you perfectly:
+          
+          ---
+          
+          ### 🛍️ Top Picks for a Work Bag
+          
+          1. **Structured Tote in Camel Vegan Leather** — A polished, professional look in a warm earth tone. Fits a laptop + daily essentials. Great desk-to-commute bag.
+          
+          2. **Olive Canvas & Vegan Leather Trim Laptop Bag** — A sleek crossbody/shoulder bag with a relaxed but put-together vibe. The canvas keeps it lightweight.
+          
+          3. **Tan Vegan Leather Satchel** — A classic work satchel with a top handle and shoulder strap. Timeless silhouette, stays under budget.
+          
+          4. **Terracotta Faux-Leather Tote** — A bold but grounded earth tone that adds personality to a work outfit without being over the top.
+          
+          ---
+          
+          Do any of these styles appeal to you? I can also ask a few quick questions to narrow it down further:
+          
+          - **Do you prefer a tote, backpack, crossbody, or briefcase-style?**
+          - **Do you need to carry a laptop, and if so, what size?**
+          - **Do you lean more structured/professional or relaxed/casual for work?**
+  markdown cell:
+    source:
+      In the output, notice that the agent reads `/mnt/memory/shopper-preferences/` before answering, then recommends bags that are vegan leather, in earth tones, and under $200, even though this message mentioned none of those things. The preferences carried across from the first session.
+  markdown cell:
+    source:
+      ## Going further: patterns for production
+      
+      ### Seed a store from your existing data
+      
+      If you already know things about a customer from their account profile or purchase history, you can write them into the store before the first session so the agent starts informed. In a real application you would run this seeding step before any sessions are created; it appears here, after the demo, only so the main learn-then-recall flow above stays focused.
+      
+      > **Note:** Run this cell before the cleanup cell at the end of the notebook, since `store` is deleted there.
+  code cell:
+    source:
+      seeded = client.beta.memory_stores.memories.create(
+          store.id,
+          path="/purchase-history.md",
+          content=(
+              "## Recent purchases\n"
+              "- Canvas tote, olive, $89 (Jan 2026)\n"
+              "- Wool beanie, rust, $34 (Dec 2025)\n"
+          ),
+      )
+      print(f"Seeded {seeded.path}")
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          Seeded /purchase-history.md
+  markdown cell:
+    source:
+      ### Combine per-customer and shared stores
+      
+      A session can attach up to eight memory stores, each with its own access level. A common pattern is one read-write store per customer plus a read-only store of brand-wide knowledge that every session shares.
+      
+      ```python
+      catalog = client.beta.memory_stores.create(
+          name="Product Catalog Notes",
+          description="Current promotions, sizing guidance, and stock notes.",
+      )
+      
+      session = client.beta.sessions.create(
+          agent={"type": "agent", "id": agent.id, "version": agent.version},
+          environment_id=environment.id,
+          resources=[
+              {
+                  "type": "memory_store",
+                  "memory_store_id": store.id,
+                  "access": "read_write",
+                  "instructions": "This customer's personal preferences.",
+              },
+              {
+                  "type": "memory_store",
+                  "memory_store_id": catalog.id,
+                  "access": "read_only",
+                  "instructions": "Brand-wide product guidance. Consult before recommending items.",
+              },
+          ],
+      )
+      ```
+      
+      ### Audit and correct
+      
+      Every write to a memory store is recorded as an immutable version with the session that made it. Use `client.beta.memory_stores.memory_versions.list(...)` to review history, and `client.beta.memory_stores.memories.update(...)` to correct a file that the agent got wrong. See the [Memory API reference](https://docs.anthropic.com/en/docs/managed-agents/memory) for the full surface.
+  markdown cell:
+    source:
+      ## Clean up
+      
+      Delete the resources you created while following this guide.
+  code cell:
+    source:
+      wait_for_idle_status(client, session_one.id)
+      wait_for_idle_status(client, session_two.id)
+      
+      client.beta.sessions.archive(session_one.id)
+      client.beta.sessions.archive(session_two.id)
+      client.beta.memory_stores.delete(store.id)
+      client.beta.agents.archive(agent.id)
+      client.beta.environments.archive(environment.id)
+  markdown cell:
+    source:
+      ## Summary
+      
+      You built a shopping agent that remembers its customers across visits by:
+      
+      1. Creating a memory store for the customer
+      2. Attaching it to each session through `resources`
+      3. Letting Claude read and write files under `/mnt/memory/` as it learned new preferences
+      4. Inspecting those files from your own application through the Memory API
+      
+      From here you can map your own user IDs to memory store IDs, seed stores from your existing customer data, and layer shared read-only stores on top for brand-wide knowledge.
+      
+      ### Other notebooks in this series
+      
+      - [`CMA_iterate_fix_failing_tests.ipynb`](CMA_iterate_fix_failing_tests.ipynb) — the entry-point notebook. Introduces agents, environments, sessions, file mounts, and the streaming event loop through a do-observe-fix loop on a failing test suite.
+      - [`CMA_operate_in_production.ipynb`](CMA_operate_in_production.ipynb) — production setup story: vault-backed MCP credentials, the `session.status_idled` webhook for HITL without long-lived connections, and the resource lifecycle CRUD verbs.
+      
+      ### Learn more
+      
+      - [Claude Managed Agents overview](https://docs.anthropic.com/en/docs/managed-agents/overview)
+      - [Memory stores API reference](https://docs.anthropic.com/en/docs/managed-agents/memory)
+      - [Session resources](https://docs.anthropic.com/en/docs/managed-agents/memory#attach-a-memory-store-to-a-session)

Generated by nbdime

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review

Recommendation: COMMENT

Summary

Adds a well-structured memory store tutorial showing a shopping agent that learns customer preferences in one session and recalls them in the next. The core flow, API usage, cleanup, and registry entries are all correct.

Actionable Feedback (4 items)
  • CMA_remember_user_preferences.ipynb (in cell with def run_turn(session_id: str, user_text: str)) — The session.status_idle handler has dead code: both the if event.stop_reason and event.stop_reason.type == "end_turn": break branch and the bare break do the same thing. The comment implies two distinct behaviors but only one exists. Simplify to a single unconditional break with a comment explaining why: # Break on any idle reason to prevent unexpected stop_reason from hanging the loop.
  • CMA_remember_user_preferences.ipynb (in cell with # Optional: "Seed a store from your existing data") — The warning about running the seeding cell before cleanup is easy to miss in markdown. Add a # Optional: run before the cleanup cell comment directly in the code cell source so it's visible even when markdown is collapsed.
  • CMA_remember_user_preferences.ipynb (in markdown cell with catalog = client.beta.memory_stores.create(...) snippet) — The "Combine per-customer and shared stores" snippet creates a catalog store but doesn't show cleanup. Readers who run this verbatim will leak a resource. Add a commented cleanup line: # client.beta.memory_stores.delete(catalog.id)
  • CMA_remember_user_preferences.ipynb (in cell with def run_turn) — The return value (-> str) is never used by either call site. Either drop the accumulation and annotate -> None, or note in the docstring that the return value is available for callers who want the reply programmatically.
Detailed Review

Code Quality

The notebook follows the CMA series conventions closely. The [memory] prefix in run_turn that surfaces file reads/writes is an excellent pedagogical choice — it makes the agent's internal behavior visible without requiring readers to understand the full event structure. The wait_for_idle_status calls are placed correctly both inside run_turn and at the top of the cleanup cell.

The dead branch in the session.status_idle handler is the main code quality issue. Compared to utilities.py and CMA_iterate_fix_failing_tests.ipynb which only break on end_turn, this notebook breaks on any idle reason — which is a valid deliberate choice, but the dual-branch structure obscures that intent. A single break with a clear comment would be cleaner.

Security

No issues. No hardcoded keys. API key is picked up via Anthropic() from the environment, consistent with the rest of the CMA series (which doesn't use dotenv — this is an accepted convention for this sub-series given the prerequisites cell says to set ANTHROPIC_API_KEY as an environment variable).

Suggestions

  • The cleanup cell correctly tears down all five resources (both sessions, the memory store, the agent, and the environment) in the right order — good.
  • agent.version pinning on all sessions.create calls (including the markdown snippet) is consistent with the series and prevents silent prompt drift.
  • Model ID claude-sonnet-4-6 is the correct non-dated alias per CLAUDE.md.
  • registry.yaml and README.md entries are accurate and concise.

Positive Notes

The problem framing in the introduction is strong — it opens with the concrete customer pain ("the agent has forgotten everything") before introducing the solution. The step numbering (Steps 1–5) creates a clear narrative arc, and the "Going further" section appropriately separates the core demo from production patterns without cluttering the main flow.

- Simplify status_idle handler to a single unconditional break
- Document run_turn return value in the docstring
- Restore inline 'Optional: run before cleanup' hint in the seeding cell
- Add catalog store cleanup reminder to the combine-stores snippet
- Tighten registry description and intro phrasing
@github-actions

Copy link
Copy Markdown

Notebook Changes

This PR modifies the following notebooks:

📓 managed_agents/CMA_remember_user_preferences.ipynb

View diff
nbdiff /dev/null managed_agents/CMA_remember_user_preferences.ipynb (869f7e76d9c2539a0831cf610fc85fbed41a88bb)
--- /dev/null  2026-04-25 22:49:39.354393
+++ managed_agents/CMA_remember_user_preferences.ipynb (869f7e76d9c2539a0831cf610fc85fbed41a88bb)  (no timestamp)
## added /cells:
+  markdown cell:
+    source:
+      # Build agents that remember your users
+      
+      Most agents start every conversation from scratch. A customer tells your shopping assistant their size, their budget, and which materials they avoid, and the next time they return, the agent has forgotten everything. They have to repeat themselves, and the experience feels generic rather than personal.
+      
+      We just introduced Memory in Claude Managed Agents to solve this. Think of it as a shared notebook your agent gets for each customer: Claude jots relevant things down during a session, and those notes are still there the next time the same customer comes back. Setup is a simple API call away.
+      
+      In this guide you will build an example shopping assistant for a retail brand. The agent will learn a customer's preferences during their first visit, save them to a user-specific memory store, and recall them automatically on the next visit without being told again.
+      
+      ## What you will build
+      
+      - A **memory store** that holds one customer's shopping preferences
+      - A **shopping agent** configured to check and update that store
+      - **Two separate sessions** that demonstrate memory carrying across visits
+      - A pattern for **inspecting and seeding** memories from your own application
+      
+      ## How memory works
+      
+      When you attach a memory store to a session, it appears as a directory inside the agent's environment at `/mnt/memory/{store-name}`, and Claude reads and writes files there using its standard file tools. Your application has full read and write access to the same files through the REST API, so you can seed a store with known facts, audit what the agent has written, or export everything to your own systems.
+      
+      > **Beta feature.** Memory stores are part of the Claude Managed Agents public beta. The API may change before general availability. The Python SDK adds the required `anthropic-beta` header automatically for every method under `client.beta`.
+  markdown cell:
+    source:
+      ## Prerequisites
+      
+      - An Anthropic API key with access to the Claude Managed Agents beta. Set it as the `ANTHROPIC_API_KEY` environment variable.
+      - Python 3.11 or later.
+      - The Anthropic Python SDK. Memory store methods require a recent release:
+      
+      ```bash
+      uv add anthropic
+      # or: pip install -U anthropic
+      ```
+  markdown cell:
+    source:
+      ## Set up the client
+  code cell:
+    source:
+      %%capture
+      %pip install -q "anthropic>=0.91.0"
+  code cell:
+    source:
+      import os
+      
+      from anthropic import Anthropic
+      from utilities import wait_for_idle_status
+      
+      MODEL = os.environ.get("COOKBOOK_MODEL", "claude-sonnet-4-6")
+      
+      client = Anthropic()
+  markdown cell:
+    source:
+      ### A helper for conversational turns
+      
+      Managed agent sessions are event-driven: you send a user message, then stream events until the session goes idle. To keep the rest of this guide readable, wrap that loop in a helper that prints the agent's replies and the files it touches under `/mnt/memory/`.
+  code cell:
+    source:
+      def run_turn(session_id: str, user_text: str) -> str:
+          """Send one user message and stream the agent's response until it goes idle.
+      
+          Returns the agent's full text reply for callers who want it programmatically.
+          """
+          print(f"\n[user]  {user_text}")
+          reply_parts: list[str] = []
+      
+          with client.beta.sessions.events.stream(session_id) as stream:
+              client.beta.sessions.events.send(
+                  session_id,
+                  events=[
+                      {
+                          "type": "user.message",
+                          "content": [{"type": "text", "text": user_text}],
+                      }
+                  ],
+              )
+              for event in stream:
+                  if event.type == "agent.message":
+                      for block in event.content:
+                          if block.type == "text":
+                              reply_parts.append(block.text)
+                              print(f"[agent] {block.text}")
+      
+                  elif event.type == "agent.tool_use":
+                      # Surface reads and writes to the memory mount so you can see
+                      # the agent checking and updating its memory.
+                      inp = event.input or {}
+                      target = inp.get("file_path") or inp.get("command", "")
+                      if "/mnt/memory/" in str(target):
+                          print(f"  [memory] {event.name}: {target}")
+      
+                  elif event.type == "session.status_idle":
+                      # Break on any idle reason so an unexpected stop_reason
+                      # (such as requires_action) cannot hang the loop.
+                      break
+      
+                  elif event.type == "session.status_terminated":
+                      break
+      
+          wait_for_idle_status(client, session_id)
+          return "".join(reply_parts)
+  markdown cell:
+    source:
+      ## Step 1: Create a memory store
+      
+      A memory store is a named container for text files, scoped to your workspace. In a production deployment you would typically create one store per end user and keep a mapping from your user IDs to store IDs in your own database.
+      
+      The `description` you set here is rendered into the agent's system prompt whenever the store is attached, so use it to tell Claude what the store is for.
+  code cell:
+    source:
+      store = client.beta.memory_stores.create(
+          name="Shopper Preferences",
+          description=(
+              "Personal shopping preferences for a single customer: "
+              "sizes, style, budget, favorite brands, and materials to avoid."
+          ),
+      )
+      
+      print(store.id)  # memstore_01...
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          memstore_01NKkumXZXY8mEhoRA3xhBvN
+  markdown cell:
+    source:
+      ## Step 2: Define the shopping agent
+      
+      Every managed agent session needs an **agent** (the model, system prompt, and tools) and an **environment** (the container the agent runs in). You can create these once and reuse them across many customers and sessions.
+      
+      Give the agent the built-in `agent_toolset`, which includes the file tools it uses to read and write memory. The `agent_toolset_20260401` type string is the toolset's API identifier, not a model alias, so you do not need to update it when newer models ship.
+  code cell:
+    source:
+      environment = client.beta.environments.create(
+          name="shopping-demo",
+          config={"type": "cloud", "networking": {"type": "limited"}},
+      )
+      
+      agent = client.beta.agents.create(
+          name="Personal Shopper",
+          model=MODEL,
+          system=(
+              "You are a personal shopping assistant for a retail brand. "
+              "Help the customer find products that match their taste and budget, "
+              "and remember what you learn about them for future visits."
+          ),
+          tools=[
+              {
+                  "type": "agent_toolset_20260401",
+                  "default_config": {
+                      "enabled": True,
+                      "permission_policy": {"type": "always_allow"},
+                  },
+              }
+          ],
+      )
+      
+      print(agent.id)  # agent_01...
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          agent_011CaMbJoHMUyR5TjeFDtMG9
+  markdown cell:
+    source:
+      ## Step 3: First visit, where the agent learns preferences
+      
+      Now start a session and attach the memory store through the `resources` array. The `instructions` field is per-attachment guidance that tells Claude how to use this particular store in this particular session.
+      
+      Because this is the customer's first visit, the store is empty. Watch the `[memory]` lines in the output: the agent checks the store, finds nothing yet, and then writes a new file capturing what it learned.
+  code cell:
+    source:
+      memory_resource = {
+          "type": "memory_store",
+          "memory_store_id": store.id,
+          "access": "read_write",
+          "instructions": (
+              "This customer's personal preferences: sizes, style, budget, and "
+              "materials to avoid. Check it at the start of every conversation "
+              "and update it whenever you learn something new."
+          ),
+      }
+      
+      session_one = client.beta.sessions.create(
+          agent={"type": "agent", "id": agent.id, "version": agent.version},
+          environment_id=environment.id,
+          resources=[memory_resource],
+      )
+      
+      # The response tells you where the store is mounted inside the agent's environment.
+      for resource in session_one.resources:
+          if resource.type == "memory_store":
+              print(f"Mounted at {resource.mount_path}")
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          Mounted at /mnt/memory/shopper-preferences
+  code cell:
+    source:
+      run_turn(
+          session_one.id,
+          "Hi! I'm looking for a new jacket. A few things about me: I wear a size "
+          "medium, I only buy vegan leather (no animal leather please), my budget "
+          "is usually under $200, and I love earth tones. What would you suggest?",
+      )
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          [user]  Hi! I'm looking for a new jacket. A few things about me: I wear a size medium, I only buy vegan leather (no animal leather please), my budget is usually under $200, and I love earth tones. What would you suggest?
+          [agent] Let me check if I have any info about you on file, and save your preferences right away!
+            [memory] bash: cat /mnt/memory/shopper-preferences 2>/dev/null || echo "No file found"
+            [memory] write: /mnt/memory/shopper-preferences
+            [memory] write: /mnt/memory/shopper-preferences/preferences.md
+          [agent] I've saved your preferences for future visits! Now, here are some great **vegan leather jacket picks in earth tones under $200** that I'd suggest:
+          
+          ---
+          
+          ### 🌿 Top Picks for You
+          
+          #### 1. **Olive Moto Jacket** — ~$110–$150
+          A classic biker silhouette in a deep **olive green** faux leather. Look for styles with zip-detail sleeves and a fitted waist. Great for casual or edgy looks.
+          - *Why you'll love it:* Earthy tone, very on-trend, versatile for layering.
+          
+          #### 2. **Camel/Tan Oversized Blazer-Jacket** — ~$80–$160
+          A structured vegan leather blazer in **camel or tan** gives a polished, sophisticated vibe. Works dressed up or down.
+          - *Why you'll love it:* Classic earth tone, medium sizing runs well in this style, timeless investment.
+          
+          #### 3. **Rust/Cognac Bomber Jacket** — ~$90–$170
+          A **rust or cognac-toned** vegan leather bomber is a bold earth-tone statement piece with a relaxed, comfortable fit.
+          - *Why you'll love it:* Rich warm color, bomber silhouettes are flattering in medium, cozy feel.
+          
+          #### 4. **Terracotta Cropped Jacket** — ~$75–$140
+          A cropped vegan leather jacket in **terracotta** pairs beautifully with high-waisted jeans or wide-leg trousers.
+          - *Why you'll love it:* Trendy silhouette, stunning earthy hue, well within budget.
+          
+          ---
+          
+          ### 🛍️ Brands to Check Out
+          Here are some brands known for quality vegan leather within your budget:
+          - **ASOS** – Wide range of faux leather styles in various earth tones
+          - **Free People** – Boho-leaning earth tone options
+          - **H&M** – Affordable vegan leather jackets in seasonal colors
+          - **Lulus** – Great selection of trendy vegan styles under $100
+          - **Noize** – A dedicated vegan outerwear brand, often has earthy tones under $200
+          
+          ---
+          
+          Do any of these styles appeal to you? I can help narrow it down further — for example, do you prefer a **fitted or relaxed fit**, or a particular style like moto, bomber, or blazer? 😊
+  markdown cell:
+    source:
+      ## Step 4: Inspect what the agent saved
+      
+      Everything the agent writes to the mount is a regular memory document that your application can read, edit, or delete through the API. List the store's contents with `view="full"` to include file content in the response.
+      
+      This is how you build a "What we know about you" page in your product, sync memories into your own database, or let a human reviewer correct something the agent got wrong.
+  code cell:
+    source:
+      page = client.beta.memory_stores.memories.list(
+          store.id,
+          view="full",
+      )
+      
+      for memory in page.data:
+          if memory.type == "memory":
+              print(f"=== {memory.path} ===")
+              print(memory.content)
+              print()
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          === /preferences.md ===
+          # Shopper Preferences
+          
+          ## Sizes
+          - Tops/Jackets: Medium
+          
+          ## Style
+          - Loves earth tones (browns, tans, olive, terracotta, camel, rust, etc.)
+          - Interested in jackets
+          
+          ## Budget
+          - Usually under $200
+          
+          ## Materials
+          - VEGAN LEATHER ONLY — absolutely no animal leather
+          - (No other material restrictions noted yet)
+          
+          ## Favorite Brands
+          - None noted yet
+          
+          ## Other Notes
+          - First visit; preferences collected 2026-04-23
+  markdown cell:
+    source:
+      You should see a file (typically something like `/preferences.md`) containing the size, budget, material, and color preferences the customer mentioned, organized by topic. Claude chose the filename and structure on its own.
+  markdown cell:
+    source:
+      ## Step 5: Return visit, where the agent recalls on its own
+      
+      This is the moment that matters. Create a **brand new session** and attach the **same memory store**. The customer does not repeat any of their preferences, but the agent reads them from memory and tailors its recommendations accordingly.
+  code cell:
+    source:
+      session_two = client.beta.sessions.create(
+          agent={"type": "agent", "id": agent.id, "version": agent.version},
+          environment_id=environment.id,
+          resources=[memory_resource],  # same store, new session
+      )
+      
+      run_turn(
+          session_two.id,
+          "Hey, I'm back! I need a bag for work. Any recommendations?",
+      )
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          [user]  Hey, I'm back! I need a bag for work. Any recommendations?
+          [agent] Welcome back! Let me check your preferences before making any recommendations!
+            [memory] read: /mnt/memory/shopper-preferences
+            [memory] bash: ls /mnt/memory/shopper-preferences/
+            [memory] read: /mnt/memory/shopper-preferences/preferences.md
+          [agent] Great news — I already have your preferences on file! Here's what I kept in mind for you:
+          
+          - 🎨 **Style:** Earth tones (browns, tans, olive, camel, etc.)
+          - 💰 **Budget:** Under $200
+          - 🌱 **Materials:** Vegan leather only — no animal leather
+          
+          With that in mind, here are some work bag recommendations that would suit you perfectly:
+          
+          ---
+          
+          ### 🛍️ Top Picks for a Work Bag
+          
+          1. **Structured Tote in Camel Vegan Leather** — A polished, professional look in a warm earth tone. Fits a laptop + daily essentials. Great desk-to-commute bag.
+          
+          2. **Olive Canvas & Vegan Leather Trim Laptop Bag** — A sleek crossbody/shoulder bag with a relaxed but put-together vibe. The canvas keeps it lightweight.
+          
+          3. **Tan Vegan Leather Satchel** — A classic work satchel with a top handle and shoulder strap. Timeless silhouette, stays under budget.
+          
+          4. **Terracotta Faux-Leather Tote** — A bold but grounded earth tone that adds personality to a work outfit without being over the top.
+          
+          ---
+          
+          Do any of these styles appeal to you? I can also ask a few quick questions to narrow it down further:
+          
+          - **Do you prefer a tote, backpack, crossbody, or briefcase-style?**
+          - **Do you need to carry a laptop, and if so, what size?**
+          - **Do you lean more structured/professional or relaxed/casual for work?**
+  markdown cell:
+    source:
+      In the output, notice that the agent reads `/mnt/memory/shopper-preferences/` before answering, then recommends bags that are vegan leather, in earth tones, and under $200, even though this message mentioned none of those things. The preferences carried across from the first session.
+  markdown cell:
+    source:
+      ## Going further: patterns for production
+      
+      ### Seed a store from your existing data
+      
+      If you already know things about a customer from their account profile or purchase history, you can write them into the store before the first session so the agent starts informed. In a real application you would run this seeding step before any sessions are created; it appears here, after the demo, only so the main learn-then-recall flow above stays focused.
+      
+      > **Note:** Run this cell before the cleanup cell at the end of the notebook, since `store` is deleted there.
+  code cell:
+    source:
+      # Optional: run before the cleanup cell.
+      seeded = client.beta.memory_stores.memories.create(
+          store.id,
+          path="/purchase-history.md",
+          content=(
+              "## Recent purchases\n"
+              "- Canvas tote, olive, $89 (Jan 2026)\n"
+              "- Wool beanie, rust, $34 (Dec 2025)\n"
+          ),
+      )
+      print(f"Seeded {seeded.path}")
+    outputs:
+      output 0:
+        output_type: stream
+        name: stdout
+        text:
+          Seeded /purchase-history.md
+  markdown cell:
+    source:
+      ### Combine per-customer and shared stores
+      
+      A session can attach up to eight memory stores, each with its own access level. A common pattern is one read-write store per customer plus a read-only store of brand-wide knowledge that every session shares.
+      
+      ```python
+      catalog = client.beta.memory_stores.create(
+          name="Product Catalog Notes",
+          description="Current promotions, sizing guidance, and stock notes.",
+      )
+      
+      session = client.beta.sessions.create(
+          agent={"type": "agent", "id": agent.id, "version": agent.version},
+          environment_id=environment.id,
+          resources=[
+              {
+                  "type": "memory_store",
+                  "memory_store_id": store.id,
+                  "access": "read_write",
+                  "instructions": "This customer's personal preferences.",
+              },
+              {
+                  "type": "memory_store",
+                  "memory_store_id": catalog.id,
+                  "access": "read_only",
+                  "instructions": "Brand-wide product guidance. Consult before recommending items.",
+              },
+          ],
+      )
+      
+      # Remember to clean up the catalog store when you are done:
+      # client.beta.memory_stores.delete(catalog.id)
+      ```
+      
+      ### Audit and correct
+      
+      Every write to a memory store is recorded as an immutable version with the session that made it. Use `client.beta.memory_stores.memory_versions.list(...)` to review history, and `client.beta.memory_stores.memories.update(...)` to correct a file that the agent got wrong. See the [Memory API reference](https://docs.anthropic.com/en/docs/managed-agents/memory) for the full surface.
+  markdown cell:
+    source:
+      ## Clean up
+      
+      Delete the resources you created while following this guide.
+  code cell:
+    source:
+      wait_for_idle_status(client, session_one.id)
+      wait_for_idle_status(client, session_two.id)
+      
+      client.beta.sessions.archive(session_one.id)
+      client.beta.sessions.archive(session_two.id)
+      client.beta.memory_stores.delete(store.id)
+      client.beta.agents.archive(agent.id)
+      client.beta.environments.archive(environment.id)
+  markdown cell:
+    source:
+      ## Summary
+      
+      You built a shopping agent that remembers its customers across visits by:
+      
+      1. Creating a memory store for the customer
+      2. Attaching it to each session through `resources`
+      3. Letting Claude read and write files under `/mnt/memory/` as it learned new preferences
+      4. Inspecting those files from your own application through the Memory API
+      
+      From here you can map your own user IDs to memory store IDs, seed stores from your existing customer data, and layer shared read-only stores on top for brand-wide knowledge.
+      
+      ### Other notebooks in this series
+      
+      - [`CMA_iterate_fix_failing_tests.ipynb`](CMA_iterate_fix_failing_tests.ipynb) — the entry-point notebook. Introduces agents, environments, sessions, file mounts, and the streaming event loop through a do-observe-fix loop on a failing test suite.
+      - [`CMA_operate_in_production.ipynb`](CMA_operate_in_production.ipynb) — production setup story: vault-backed MCP credentials, the `session.status_idled` webhook for HITL without long-lived connections, and the resource lifecycle CRUD verbs.
+      
+      ### Learn more
+      
+      - [Claude Managed Agents overview](https://docs.anthropic.com/en/docs/managed-agents/overview)
+      - [Memory stores API reference](https://docs.anthropic.com/en/docs/managed-agents/memory)
+      - [Session resources](https://docs.anthropic.com/en/docs/managed-agents/memory#attach-a-memory-store-to-a-session)

Generated by nbdime

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review

Recommendation: REQUEST_CHANGES

Summary

This PR adds a well-structured memory store cookbook for Claude Managed Agents, demonstrating how a shopping agent learns user preferences in one session and recalls them in the next. The two-session demo is effective and the prose is clear, but two blocking issues should be resolved before merging.

Actionable Feedback (6 items)

Blocking:

  • CMA_remember_user_preferences.ipynb (in cell with def run_turn() — The stream loop breaks on any session.status_idle event regardless of stop_reason. The comment claims this prevents hangs, but it actually silently swallows requires_action idle events and leaves the session in an unresolved state. Since readers will copy this pattern, add a guard: if event.stop_reason and event.stop_reason.type != "end_turn": raise RuntimeError(...), or explicitly note in the comment that this helper must not be used with custom-tool agents.
  • CMA_remember_user_preferences.ipynb (setup cell with client = Anthropic()) — Missing dotenv.load_dotenv() before constructing the client. The most recent peer notebook (CMA_prompt_versioning_and_rollback) added python-dotenv and load_dotenv(). Please follow that pattern here.

Important:

  • CMA_remember_user_preferences.ipynb (intro cell) — The "What you will build" section describes artifacts, not skills. Replace with **By the end of this guide, you'll be able to:** learning objectives per the cookbook template.
  • CMA_remember_user_preferences.ipynb (seeding cell in "Going further") — The seeding cell runs after both sessions complete and is immediately followed by cleanup, so the write produces no observable outcome. Either add a brief run_turn call after seeding to show the agent using the pre-seeded data, or restructure so seeding happens before the first session.

Suggestions:

  • CMA_remember_user_preferences.ipynb ("Going further" / Combine stores snippet) — The catalog store created in the illustrative snippet is never deleted by the cleanup cell. The commented-out cleanup line is easy to miss; add a prose callout below the block: "If you run this snippet, delete the catalog store separately with client.beta.memory_stores.delete(catalog.id) — it is not included in the cleanup cell below."
  • CMA_remember_user_preferences.ipynb (Step 4 memory_stores.memories.list call) — Add one sentence explaining what the default (no view="full") returns, so readers know when they can omit the parameter in production.
Detailed Review

Code Quality

The run_turn helper is well type-annotated and its inline comments explain why each event branch exists. All three sessions.create calls correctly pin agent.version, including the one inside the "Combine stores" markdown snippet — good attention to detail. The %%capture magic on the pip install cell follows project convention. The cleanup cell is complete and correctly calls wait_for_idle_status before archiving.

Security

No secrets or hardcoded credentials. Anthropic() reads from the environment as expected. The only concern is the missing load_dotenv() call (see blocking item above).

Suggestions

  • The run_turn helper prints each streaming agent.message block as it arrives, which can produce fragmented output during a live run. Other series notebooks accumulate the full reply or use end="". Not blocking, but worth aligning.
  • Consider adding a parenthetical to the Summary section noting that the webhook fires session.status_idled while the SSE stream emits session.status_idle, to prevent confusion for readers who work with both patterns.
  • The view="full" parameter description would benefit from one sentence explaining what the default view omits (metadata-only vs. full content).

Positive Notes

  • The problem hook is strong — opening with the "starts from scratch" pain point grounds the reader immediately.
  • The two-session structure is the right pedagogical choice: the memory effect is demonstrated rather than described.
  • managed_agents/README.md row is well-formatted and accurate.
  • registry.yaml entry is correct; gaganb-ant exists in authors.yaml.
  • The beta-feature callout in the intro cell is well-placed and accurate.

@gaganb-ant gaganb-ant changed the title feat(managed_agents): add memory store cookbook feat(managed_agents): add Managed Agents Memory Cookbook Apr 27, 2026
@gaganb-ant gaganb-ant merged commit 33424c3 into main Apr 27, 2026
9 checks passed
oferw-xpz added a commit to oferw-xpz/claude-cookbooks that referenced this pull request May 6, 2026
- Rebase onto current main (resolves DIRTY merge state from PRs anthropics#573, anthropics#595)
- Re-execute notebook end-to-end against live XPOZ MCP + Claude Sonnet 4.6
  (804 posts sampled across Twitter/Reddit/Instagram, structured analysis
   committed as cell outputs per CONTRIBUTING.md guidance)
- ruff check + ruff format clean (sorted imports, formatted code)
- authors.yaml sorted alphabetically (validate_authors_sorted.py passes)
- registry.yaml description: 'analyse' -> 'analyze' (US English)
oferw-xpz added a commit to oferw-xpz/claude-cookbooks that referenced this pull request May 10, 2026
- Rebase onto current main (resolves DIRTY merge state from PRs anthropics#573, anthropics#595)
- Re-execute notebook end-to-end against live XPOZ MCP + Claude Sonnet 4.6
  (804 posts sampled across Twitter/Reddit/Instagram, structured analysis
   committed as cell outputs per CONTRIBUTING.md guidance)
- ruff check + ruff format clean (sorted imports, formatted code)
- authors.yaml sorted alphabetically (validate_authors_sorted.py passes)
- registry.yaml description: 'analyse' -> 'analyze' (US English)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants