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

INPUT: messages: &mut [ChatMessage], keep_recent: usize
─────────────────────────────────────────────────────────────────────────────────

STEP 1: Build tool_call_id → tool_name Mapping
┌─────────────────────────────────────────────────────────────────┐
│ Scan all messages for assistant role + tool_calls               │
│                                                                 │
│ messages[i]:                                                    │
│  role: "assistant"                                              │
│  tool_calls: [                                                  │
│    {id: "call_A", name: "Read", args: "..."},                  │
│    {id: "call_B", name: "Grep", args: "..."}                   │
│  ]                                                              │
│                    ↓                                             │
│  tool_name_map: {                                               │
│    "call_A" → "Read",                                           │
│    "call_B" → "Grep"                                            │
│  }                                                              │
└─────────────────────────────────────────────────────────────────┘

STEP 2: Find All Tool Result Messages
┌─────────────────────────────────────────────────────────────────┐
│ Scan all messages for role == "tool"                            │
│                                                                 │
│ messages[0]: user (skip)                                        │
│ messages[1]: assistant (skip)                                   │
│ messages[2]: tool ✓                                             │
│ messages[3]: assistant (skip)                                   │
│ messages[4]: tool ✓                                             │
│ messages[5]: tool ✓                                             │
│                    ↓                                             │
│  tool_indices: [2, 4, 5]                                        │
│                                                                 │
│  if tool_indices.len() <= keep_recent:                          │
│      return  // Nothing to compact                              │
└─────────────────────────────────────────────────────────────────┘

STEP 3: Identify Candidates for Compaction
┌─────────────────────────────────────────────────────────────────┐
│ Protect last keep_recent tool messages                          │
│                                                                 │
│ keep_recent = 2                                                 │
│ tool_indices = [2, 4, 5]                                        │
│              ┌─────────────────────────────┐                    │
│              │  PROTECTED (last 2)         │                    │
│              ↓                             │                    │
│ to_compact = [2]  (all except last 2)  ←──┘                    │
│              ↑                                                   │
│              Eligible for compaction                             │
└─────────────────────────────────────────────────────────────────┘

STEP 4: Process Each Candidate
┌─────────────────────────────────────────────────────────────────┐
│ FOR each idx in to_compact:                                     │
│                                                                 │
│  messages[2]:                                                   │
│    role: "tool"                                                 │
│    tool_call_id: "call_A"                                       │
│    content: "file1.rs\nfile2.rs\n..." (2000 chars)              │
│                        ↓                                         │
│  Check size:                                                    │
│    content.chars().count() = 2000 > THRESHOLD (800)?            │
│    YES → eligible                                               │
│                        ↓                                         │
│  Look up tool name:                                             │
│    tool_name_map["call_A"] = "Read"                             │
│                        ↓                                         │
│  Check if exempt:                                               │
│    EXEMPT_TOOLS = [LoadSkill, Task, Todo, ...]                 │
│    "Read" in EXEMPT_TOOLS? NO                                   │
│                        ↓                                         │
│  ✅ REPLACE:                                                     │
│    messages[2].content = "[Previous: used Read]"                │
│    compacted_count++                                            │
└─────────────────────────────────────────────────────────────────┘

STEP 5: Log Results
┌─────────────────────────────────────────────────────────────────┐
│ if compacted_count > 0:                                         │
│   write_info_log("压缩了 1 个旧 tool result（保留最近 2 个）")    │
└─────────────────────────────────────────────────────────────────┘

OUTPUT: Messages modified in-place (mutated &mut [ChatMessage])
─────────────────────────────────────────────────────────────────────────────────


╔════════════════════════════════════════════════════════════════════════════════╗
║                    ASSISTANT MESSAGE → TOOL RESULT LINKING                     ║
╚════════════════════════════════════════════════════════════════════════════════╝

Message Flow in Conversation:
────────────────────────────

[Index 2] Assistant calls Glob tool:
┌──────────────────────────────────────────────┐
│ ChatMessage {                                │
│   role: "assistant",                         │
│   content: "I'll find the files...",         │
│   tool_calls: Some([                         │
│     ToolCallItem {                           │
│       id: "call_abc123",    ←─────┐          │
│       name: "Glob",                │ This    │
│       arguments: "..."      │      │ ID     │
│     }                        │      │         │
│   ]),                        │      │         │
│   tool_call_id: None,        │      │         │
│ }                            │      │         │
└──────────────────────────────┼──────┼─────────┘
                               │      │
                    Linked by call_abc123

[Index 3] Tool returns result:
┌──────────────────────────────────────────────┐
│ ChatMessage {                                │
│   role: "tool",                              │
│   content: "file1.rs\nfile2.rs\n...",        │
│   tool_calls: None,                          │
│   tool_call_id: Some("call_abc123"), ←─────┐ │
│                                       │     │
│ }                                     └─────┘
└──────────────────────────────────────────────┘
        ↓
  During micro_compact:
    tool_name_map["call_abc123"] = "Glob"
    messages[3].content.len() = 5000 > 800?
    YES → REPLACE
        ↓
    messages[3].content = "[Previous: used Glob]"


╔════════════════════════════════════════════════════════════════════════════════╗
║                        EXEMPT TOOLS (NEVER COMPACTED)                          ║
╚════════════════════════════════════════════════════════════════════════════════╝

LoadSkill       → Carries skill definitions & instructions (workflow critical)
Task            → Task tracking & planning context
TodoRead        → Todo list state
TodoWrite       → Todo modifications
EnterPlanMode   → Plan execution entry point
ExitPlanMode    → Plan execution state
Agent           → Agent collaboration context
AgentTeam       → Multi-agent team state
Ask             → User interaction history
SendMessage     → Teammate communication context
CreateTeammate  → Team configuration & setup


╔════════════════════════════════════════════════════════════════════════════════╗
║                            COMPACTION THRESHOLDS                               ║
╚════════════════════════════════════════════════════════════════════════════════╝

MICRO_COMPACT_BYTES_THRESHOLD:  800 characters
                                (only compact if > 800 chars)

COMPACT_KEEP_RECENT:             10 messages
                                (always preserve last 10 tool results)

COMPACT_TOKEN_THRESHOLD:         256 * 800 = 204,800 tokens (~820K chars)
                                (if still exceeded after micro_compact, trigger auto_compact)

Token Estimation:                ~4 characters = 1 token


╔════════════════════════════════════════════════════════════════════════════════╗
║                          INTEGRATION WITH AGENT LOOP                           ║
╚════════════════════════════════════════════════════════════════════════════════╝

Agent Loop Round N:
┌────────────────────────────────────┐
│ 1. Drain pending user messages     │
├────────────────────────────────────┤
│ 2. IF compact_config.enabled:      │
│    ├─ Call micro_compact()         │  ← Layer 1: In-memory
│    │                               │     (this function)
│    └─ estimate_tokens()            │
│       IF tokens > threshold:       │
│       └─ Call auto_compact()       │  ← Layer 2: LLM summarization
├────────────────────────────────────┤
│ 3. Call LLM with modified messages │
├────────────────────────────────────┤
│ 4. Execute tool calls (if any)     │
└────────────────────────────────────┘

