$npx -y skills add simbajigege/book2skills --skill folded-memory-implementationDeveloper implementation guide for building hierarchical (folded) memory into an Agent. Three-layer architecture where recent turns stay detailed, older content compresses into episodes, and the oldest distills into durable semantic facts. Use when compact-memory-implementation i
| 1 | # folded-memory-implementation |
| 2 | |
| 3 | A developer guide for hierarchical memory: instead of replacing history with a single flat summary, maintain three memory layers at different levels of detail. Old content is compressed more aggressively — not discarded. Each layer is independently stored and selectively recalled. |
| 4 | |
| 5 | **Prerequisite**: read `compact-memory-implementation` first. Folded memory builds on the same fork-agent and trigger concepts. |
| 6 | |
| 7 | --- |
| 8 | |
| 9 | ## The core idea |
| 10 | |
| 11 | ``` |
| 12 | L1 Working memory [ turn 38..50 ] — raw turns, full detail, short window |
| 13 | L2 Episodic memory [ turn 10..37 ] — compressed episodes, medium detail |
| 14 | L3 Semantic memory [ turn 1..9 ] — abstract facts and decisions, sparse |
| 15 | ``` |
| 16 | |
| 17 | When L1 fills up → fork an episode compactor → move oldest L1 turns into L2. |
| 18 | When L2 fills up → fork a semantic extractor → distill L2 into L3. |
| 19 | |
| 20 | At each agent turn, inject the right combination of layers into the system prompt. |
| 21 | |
| 22 | --- |
| 23 | |
| 24 | ## Step 1 — Understand the setup |
| 25 | |
| 26 | Same questions as `compact-memory-implementation`, plus: |
| 27 | |
| 28 | - **How long do sessions run?** If sessions are short (<50 turns), flat compact is enough. |
| 29 | - **What kind of information ages badly?** Decisions and patterns age well (good for L3). Exact tool outputs age badly (keep only in L1 or summarize into L2). |
| 30 | - **Does the agent need to cite past reasoning?** If yes, L2/L3 must preserve decision rationale, not just conclusions. |
| 31 | |
| 32 | --- |
| 33 | |
| 34 | ## Step 2 — Three-layer architecture |
| 35 | |
| 36 | ### Layer 1 — Working memory |
| 37 | |
| 38 | - **Content**: raw conversation turns, full fidelity |
| 39 | - **Window**: last N turns (e.g., 20 turns or ~30k tokens) |
| 40 | - **Trigger to flush**: when L1 exceeds its window, oldest turns move to L2 |
| 41 | - **Injected as**: full message history in `messages[]` |
| 42 | |
| 43 | ### Layer 2 — Episodic memory |
| 44 | |
| 45 | - **Content**: compressed episode summaries — what happened, what was decided, what was tried |
| 46 | - **Window**: up to M episodes (e.g., 10 episodes, each covering ~20 turns) |
| 47 | - **Trigger to flush**: when episode count exceeds M, oldest episodes distill into L3 |
| 48 | - **Injected as**: structured block in system prompt |
| 49 | |
| 50 | ### Layer 3 — Semantic memory |
| 51 | |
| 52 | - **Content**: abstract facts, stable decisions, eliminated approaches, domain knowledge learned |
| 53 | - **Window**: unbounded, but aggressively filtered — only durable knowledge |
| 54 | - **Trigger to flush**: never purged, but updated/merged when contradicted |
| 55 | - **Injected as**: compact block in system prompt, always present |
| 56 | |
| 57 | --- |
| 58 | |
| 59 | ## Step 3 — Data structures |
| 60 | |
| 61 | ```python |
| 62 | from dataclasses import dataclass, field |
| 63 | from typing import Any |
| 64 | |
| 65 | @dataclass |
| 66 | class Episode: |
| 67 | episode_id: int |
| 68 | turn_range: tuple[int, int] # (start_turn, end_turn) |
| 69 | summary: str |
| 70 | decisions: list[dict] # [{"decision": ..., "reason": ...}] |
| 71 | eliminated: list[dict] # [{"approach": ..., "why": ...}] |
| 72 | open_questions: list[str] |
| 73 | tool_results: dict[str, Any] # summarized results worth keeping |
| 74 | |
| 75 | @dataclass |
| 76 | class SemanticMemory: |
| 77 | facts: list[str] # stable domain facts |
| 78 | decisions: list[dict] # durable decisions (never re-litigate) |
| 79 | eliminated_approaches: list[dict] # things proven not to work |
| 80 | patterns: list[str] # recurring patterns observed |
| 81 | |
| 82 | @dataclass |
| 83 | class FoldedMemory: |
| 84 | semantic: SemanticMemory |
| 85 | episodes: list[Episode] |
| 86 | current_episode_id: int = 0 |
| 87 | total_turns_seen: int = 0 |
| 88 | ``` |
| 89 | |
| 90 | --- |
| 91 | |
| 92 | ## Step 4 — Fork agent: L1 → L2 (episode compactor) |
| 93 | |
| 94 | Triggered when working memory (L1) exceeds its window. Takes the oldest N turns and compresses them into one `Episode`. |
| 95 | |
| 96 | ```python |
| 97 | EPISODE_COMPACTOR_PROMPT = """ |
| 98 | You are an episode compactor. Read the provided conversation turns and produce a structured Episode summary. |
| 99 | |
| 100 | An Episode captures: |
| 101 | - What was attempted and what happened (not the full dialogue — the outcome) |
| 102 | - Decisions made and WHY (the reasoning behind them, not just the choice) |
| 103 | - Approaches tried and ruled out, with reasons (prevents re-exploration) |
| 104 | - Open questions that were not resolved |
| 105 | - Tool results that future turns will need (summarize, don't dump raw output) |
| 106 | |
| 107 | Do NOT include: |
| 108 | - Intermediate back-and-forth that led to a conclusion (keep the conclusion, drop the path) |
| 109 | - Tool outputs that have already been acted on and have no future relevance |
| 110 | - Anything a fresh agent could derive by reading the code or running a command |
| 111 | |
| 112 | Output valid JSON: |
| 113 | { |
| 114 | "summary": "2-3 sentence narrative of what happened in this episode", |
| 115 | "decisions": [{"decision": "...", "reason": "...", "constraint": "..."}], |
| 116 | "eliminated": [{"approach": "...", "why": "..."}], |
| 117 | "open_questions": ["..."], |
| 118 | "tool_results" |