╔════════════════════════════════════════════════════════════════════════════════╗
║                    MICRO_COMPACT ARCHITECTURE & DATA FLOW                      ║
╚════════════════════════════════════════════════════════════════════════════════╝

┌─ LAYER OVERVIEW ──────────────────────────────────────────────────────────────┐
│                                                                               │
│  LAYER 1: micro_compact()        (In-memory, $0 cost)                        │
│  └─ Every agent loop round                                                   │
│  └─ Replace old large tool results with placeholders                         │
│  └─ Fast (< 1ms), zero API calls                                             │
│                                                                               │
│  LAYER 2: auto_compact()         (LLM-based, costs money)                    │
│  └─ Triggered if Layer 1 insufficient                                        │
│  └─ Full conversation summarization                                          │
│  └─ Skill instructions preservation                                          │
│                                                                               │
└───────────────────────────────────────────────────────────────────────────────┘


┌─ AGENT LOOP INTEGRATION ──────────────────────────────────────────────────────┐
│                                                                               │
│  ┌─ Round N ─────────────────────────────────────────────┐                   │
│  │                                                       │                   │
│  │  1. Drain pending messages                           │                   │
│  │     └─ Add any user input from queue                 │                   │
│  │                                                       │                   │
│  │  2. IF compact_config.enabled:                        │                   │
│  │     ├─ Call micro_compact(messages, keep_recent)    │ ← THIS FUNCTION  │
│  │     │  └─ Replace old tool results in-place         │                   │
│  │     │                                                │                   │
│  │     └─ estimate_tokens(messages)                    │                   │
│  │        IF tokens > THRESHOLD:                        │                   │
│  │        └─ Call auto_compact() (Layer 2)             │                   │
│  │                                                       │                   │
│  │  3. Send messages to LLM                             │                   │
│  │                                                       │                   │
│  │  4. Process tool calls                               │                   │
│  │     └─ Execute tools, get results                    │                   │
│  │     └─ Add new tool result messages                  │                   │
│  │                                                       │                   │
│  │  5. Loop → Round N+1                                 │                   │
│  │                                                       │                   │
│  └───────────────────────────────────────────────────────┘                   │
│                                                                               │
└───────────────────────────────────────────────────────────────────────────────┘


┌─ DATA STRUCTURES ─────────────────────────────────────────────────────────────┐
│                                                                               │
│  ChatMessage (from storage.rs)                                               │
│  ├─ role: String           ("user", "assistant", "tool", "system")           │
│  ├─ content: String        (the message text - GETS COMPACTED HERE)          │
│  ├─ tool_calls: Option<Vec<ToolCallItem>>                                   │
│  │  └─ [Only on "assistant" messages]                                        │
│  │  └─ Contains: [{id, name, arguments}, ...]                               │
│  ├─ tool_call_id: Option<String>                                            │
│  │  └─ [Only on "tool" messages]                                             │
│  │  └─ References ToolCallItem.id from assistant message                     │
│  └─ images: Option<Vec<ImageData>>                                           │
│     └─ [Optional, not persisted]                                             │
│                                                                               │
│  ToolCallItem                                                                 │
│  ├─ id: String          (unique identifier, e.g., "call_abc123")             │
│  ├─ name: String        (tool name, e.g., "Read", "Glob")                   │
│  └─ arguments: String   (JSON arguments for the tool)                        │
│                                                                               │
└───────────────────────────────────────────────────────────────────────────────┘


┌─ MICRO_COMPACT ALGORITHM (5 STEPS) ───────────────────────────────────────────┐
│                                                                               │
│  INPUT: messages: &mut [ChatMessage], keep_recent: usize                    │
│  ├─ messages: All conversation messages                                      │
│  └─ keep_recent: How many recent tool results to protect (default: 10)      │
│                                                                               │
│  STEP 1: Build tool_call_id → tool_name Map                                 │
│  ├─ Scan all messages                                                        │
│  ├─ For each "assistant" message:                                           │
│  │  └─ For each ToolCallItem in tool_calls:                                 │
│  │     └─ Add to map: tool_call_id → tool_name                              │
│  └─ Result: HashMap<String, String>                                         │
│                                                                               │
│     Example:                                                                  │
│     "call_1" → "Read"                                                        │
│     "call_2" → "Grep"                                                        │
│     "call_3" → "Shell"                                                       │
│                                                                               │
│  ─────────────────────────────────────────────────────────────────────────   │
│                                                                               │
│  STEP 2: Find All Tool Result Messages                                      │
│  ├─ Scan all messages                                                        │
│  ├─ Collect indices where role == "tool"                                    │
│  └─ Result: Vec<usize> of indices                                           │
│                                                                               │
│     Example (3 tool messages at indices 2, 4, 6):                           │
│     tool_indices = [2, 4, 6]                                                │
│                                                                               │
│  ─────────────────────────────────────────────────────────────────────────   │
│                                                                               │
│  STEP 3: Early Exit Check                                                    │
│  ├─ if tool_indices.len() <= keep_recent:                                   │
│  │  └─ return  (nothing to compact, all stay)                               │
│  └─ Otherwise continue                                                       │
│                                                                               │
│  ─────────────────────────────────────────────────────────────────────────   │
│                                                                               │
│  STEP 4: Identify Compaction Candidates                                     │
│  ├─ Calculate: to_compact = indices[..indices.len() - keep_recent]          │
│  ├─ Keep last keep_recent tool messages intact                              │
│  └─ Compact all earlier ones (if conditions met)                            │
│                                                                               │
│     Example (keep_recent = 2):                                              │
│     tool_indices = [2, 4, 6]    (3 tool messages)                           │
│     to_compact = [2]             (indices 4, 6 are protected)               │
│                                                                               │
│  ─────────────────────────────────────────────────────────────────────────   │
│                                                                               │
│  STEP 5: Process Candidates                                                  │
│  ├─ FOR each idx in to_compact:                                             │
│  │  ├─ Get message at idx                                                    │
│  │  ├─ Check 1: msg.content.len() > 800 chars?                              │
│  │  │  └─ If NO: skip (small message, not worth compacting)                │
│  │  ├─ Check 2: Get tool name via tool_call_id lookup                       │
│  │  ├─ Check 3: Is tool in EXEMPT_TOOLS list?                               │
│  │  │  └─ If YES: skip (critical tool, never compact)                       │
│  │  └─ Action: Replace content with "[Previous: used {tool_name}]"          │
│  │     └─ Increment compacted_count                                          │
│  │                                                                            │
│  │     EXEMPT_TOOLS = [                                                      │
│  │       LoadSkill,    // Skill instructions (workflow critical)             │
│  │       Task,         // Task tracking state                                │
│  │       Todo*,        // Todo tracking                                      │
│  │       Plan*,        // Workflow execution state                           │
│  │       Agent*,       // Collaboration context                              │
│  │       Ask,          // User confirmations                                 │
│  │       SendMessage,  // Team communication                                 │
│  │       CreateTeammate                                                       │
│  │     ]                                                                      │
│  │                                                                            │
│  └─ END FOR                                                                   │
│                                                                               │
│  ─────────────────────────────────────────────────────────────────────────   │
│                                                                               │
│  STEP 6: Log Results                                                          │
│  └─ If compacted_count > 0:                                                 │
│     └─ write_info_log("压缩了 X 个旧 tool result...")                       │
│                                                                               │
│  OUTPUT: messages modified in-place                                          │
│                                                                               │
└───────────────────────────────────────────────────────────────────────────────┘


┌─ MESSAGE FLOW EXAMPLE ────────────────────────────────────────────────────────┐
│                                                                               │
│  Initial State:                                                              │
│                                                                               │
│  [0] role: "user"                                                            │
│      content: "Read main.rs"                                                 │
│                                                                               │
│  [1] role: "assistant"                                                       │
│      content: "I'll read the file"                                           │
│      tool_calls: [{id: "call_A", name: "Read", args: "..."}]                │
│                                                                               │
│  [2] role: "tool"                                                            │
│      tool_call_id: "call_A"                                                  │
│      content: "fn main() { ... }" (5000 chars)  ← CANDIDATE FOR COMPACTION  │
│                          ↓↓↓ During micro_compact ↓↓↓                       │
│      1. Look up "call_A" in tool_name_map: "Read"                           │
│      2. Check size: 5000 > 800? YES                                          │
│      3. Check exempt: "Read" in exempt list? NO                              │
│      4. Replace: content = "[Previous: used Read]"                           │
│                          ↓↓↓ After micro_compact ↓↓↓                        │
│      content: "[Previous: used Read]" (20 chars)                             │
│                                                                               │
│  [3] role: "assistant"                                                       │
│      content: "The file has..."                                              │
│      tool_calls: [{id: "call_B", name: "Grep", args: "..."}]                │
│                                                                               │
│  [4] role: "tool"                                                            │
│      tool_call_id: "call_B"                                                  │
│      content: "grep results" (200 chars)  ← PROTECTED (< 800 chars)          │
│      (stays intact)                                                           │
│                                                                               │
│  Token Savings: ~1240 tokens (5000 chars → 20 chars)                        │
│                                                                               │
└───────────────────────────────────────────────────────────────────────────────┘


┌─ CONSTANTS & THRESHOLDS ──────────────────────────────────────────────────────┐
│                                                                               │
│  MICRO_COMPACT_BYTES_THRESHOLD = 800                                         │
│  └─ Only compact if message content > 800 characters                         │
│  └─ Rationale: Small messages don't waste much space                         │
│                                                                               │
│  COMPACT_KEEP_RECENT = 10                                                    │
│  └─ Always preserve last 10 tool result messages                             │
│  └─ Ensures fresh context for LLM                                            │
│                                                                               │
│  COMPACT_TOKEN_THRESHOLD = 256 * 800 = 204,800 tokens (~820K chars)         │
│  └─ If conversation > this size after micro_compact                         │
│  └─ Trigger auto_compact (Layer 2)                                           │
│                                                                               │
│  Token Estimation: ~4 characters = 1 token                                   │
│                                                                               │
│  ROLE_ASSISTANT = "assistant"                                                │
│  └─ Messages that initiate tool calls                                        │
│                                                                               │
│  ROLE_TOOL = "tool"                                                          │
│  └─ Messages that contain tool results                                       │
│                                                                               │
└───────────────────────────────────────────────────────────────────────────────┘


┌─ PERFORMANCE CHARACTERISTICS ─────────────────────────────────────────────────┐
│                                                                               │
│  Time Complexity:   O(n) where n = total messages                            │
│                     └─ Two passes: map building + compaction                 │
│                                                                               │
│  Space Complexity:  O(t) where t = distinct tool_calls                       │
│                     └─ HashMap stores: tool_call_id → tool_name              │
│                                                                               │
│  API Cost:          $0 (pure in-memory operation)                            │
│                                                                               │
│  Execution Time:    < 1ms for typical 100-message history                    │
│                                                                               │
│  Token Savings:     20-40% reduction on old tool results                     │
│                     Example: 6000+ tokens per round typical                  │
│                                                                               │
│  Memory Overhead:   Negligible (HashMap size ≈ 2KB for typical session)     │
│                                                                               │
└───────────────────────────────────────────────────────────────────────────────┘


┌─ CALL CHAIN ──────────────────────────────────────────────────────────────────┐
│                                                                               │
│  src/command/chat/agent.rs                                                   │
│  └─ agent_loop()                                                             │
│     └─ Line 123: compact::micro_compact(&mut messages, keep_recent)         │
│        └─ src/command/chat/compact.rs                                        │
│           └─ pub fn micro_compact(...)  [lines 168-241]                      │
│              ├─ Builds tool_name_map (HashMap)                               │
│              ├─ Finds tool messages (indices)                                │
│              ├─ Filters candidates                                            │
│              └─ Replaces content with placeholders                           │
│                                                                               │
│  After micro_compact:                                                        │
│  └─ estimate_tokens(&messages)                                              │
│     └─ If > threshold:                                                       │
│        └─ auto_compact() [Layer 2]                                           │
│                                                                               │
└───────────────────────────────────────────────────────────────────────────────┘


╔════════════════════════════════════════════════════════════════════════════════╗
║                             KEY DESIGN PATTERNS                                ║
╚════════════════════════════════════════════════════════════════════════════════╝

1. LAZY LINKING
   └─ Don't pre-compute all links
   └─ Build map on-demand
   └─ Look up when needed
   └─ Cache-friendly, simpler code

2. TWO-LEVEL FILTERING
   └─ Filter by message count (keep_recent)
   └─ Filter by content size (800 chars)
   └─ Filter by tool type (EXEMPT_TOOLS)
   └─ Ensures both context AND space optimization

3. GRACEFUL DEGRADATION
   └─ Tool name not in map? Default to "unknown"
   └─ Still creates valid placeholder
   └─ Function always succeeds

4. SEMANTIC PRESERVATION
   └─ Placeholder retains tool name information
   └─ "[Previous: used Read]" tells model: used Read tool
   └─ Maintains conversational context

5. IMMUTABLE PROTECTION
   └─ Recent messages stay intact
   └─ Workflow-critical tools stay intact
   └─ Only old non-critical messages compacted
   └─ Ensures reliability

