Informe Técnico 498AS Research 2026-02-04

Compaction & Pre-Compaction Memory Flush

Investigación profunda del sistema de gestión de contexto y persistencia de memoria en OpenClaw

📋 Resumen Ejecutivo

Esta investigación analiza el mecanismo de compaction y memory flush de OpenClaw, su configuración actual, y las discrepancias observadas entre documentación y comportamiento real.

memoryFlush existe y funciona — Evidencia de 18 invocaciones con NO_REPLY en transcripts JSONL de sesiones antiguas
⚠️
No hay tracking activo — Todos los campos memoryFlushAt y memoryFlushCompactionCount son null actualmente
🔧
Configuración en safeguard mode — Compaction está configurado como mode: "safeguard", no hay override de memoryFlush
📂
Archivos de memoria existenmemory/ tiene 3 archivos (Jan 31, Feb 2, Feb 4) pero gaps en días intermedios

1. Objetivo de la Investigación

Se buscaba entender y potencialmente extender el sistema de hooks de OpenClaw para ejecutar lógica personalizada before/after compaction. El caso de uso principal: asegurar que información crítica de sesión se persista a disco antes de que el contexto sea compactado (y potencialmente perdido).

Preguntas clave

2. Documentación vs. Código Real

Lo que dicen los docs

La documentación en /opt/openclaw/docs/ describe un sistema completo de memory flush pre-compaction:

📄 /concepts/memory.md

"When a session is close to auto-compaction, OpenClaw triggers a silent, agentic turn that reminds the model to write durable memory before the context is compacted."

agents.defaults.compaction.memoryFlush:
  enabled: true (default)
  softThresholdTokens: 4000 (default)
  prompt: "Write any lasting notes to memory/YYYY-MM-DD.md..."
  systemPrompt: "Session nearing compaction. Store durable memories now."

Lo que encontramos en código

El código en /opt/openclaw/dist/auto-reply/reply/agent-runner-memory.js confirma la existencia del sistema:

// Extractos del código real
const memoryFlushSettings = resolveMemoryFlushSettings(params.cfg);
if (!memoryFlushSettings) { return; }

const shouldFlushMemory = memoryFlushSettings &&
    memoryFlushWritable &&
    reserveTokensFloor: memoryFlushSettings.reserveTokensFloor,
    softThresholdTokens: memoryFlushSettings.softThresholdTokens,

// Tracking en session entry
memoryFlushAt: Date.now(),
memoryFlushCompactionCount: nextCount

Discrepancias observadas

Aspecto Documentación Realidad Observada
memoryFlush.enabled Default true No explícito en config, asumido true
Tracking fields Se actualizan en sessions.json Todos null actualmente
Hook before_compact "Pi exposes session_before_compact" No visible en openclaw hooks list
Evidencia de ejecución Silent turns con NO_REPLY 18 ocurrencias en JSONL antiguos ✓

3. Cómo Funciona memoryFlush

Flujo de ejecución

┌─────────────────────────────────────────────────────────────┐
│                    SESSION TURN                              │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  1. Gateway recibe mensaje                                  │
│           ↓                                                  │
│  2. Calcula contextTokens vs contextWindow                  │
│           ↓                                                  │
│  3. Si contextTokens > (window - reserveFloor - softThresh) │
│           ↓                                                  │
│  4. ¿memoryFlushCompactionCount < compactionCount?          │
│           ↓                                                  │
│     YES → Ejecuta silent turn con prompt de flush           │
│           ↓                                                  │
│  5. Agente escribe a memory/YYYY-MM-DD.md                   │
│           ↓                                                  │
│  6. Responde "NO_REPLY" (suprimido al usuario)              │
│           ↓                                                  │
│  7. Actualiza memoryFlushAt + memoryFlushCompactionCount    │
│           ↓                                                  │
│  8. Continúa con el turn normal del usuario                 │
│                                                              │
└─────────────────────────────────────────────────────────────┘

Condiciones para el flush

  1. memoryFlush.enabled debe ser true
  2. Workspace debe ser writable (workspaceAccess no "ro" ni "none")
  3. Tokens cerca del límite: contextTokens > contextWindow - reserveTokensFloor - softThresholdTokens
  4. No se ha hecho flush en este ciclo: memoryFlushCompactionCount < compactionCount
  5. Solo sesiones Pi embedded (CLI backends lo saltan)

Defaults del sistema

20k
reserveTokensFloor
4k
softThresholdTokens
true
enabled (default)

Prompts default

// User prompt
"Pre-compaction memory flush. Store durable memories now 
(use memory/YYYY-MM-DD.md; create memory/ if needed). 
If nothing to store, reply with NO_REPLY."

// System prompt append
"Session nearing compaction. Store durable memories now."

4. Campos de Tracking en sessions.json

El archivo ~/.openclaw/agents/main/sessions/sessions.json contiene un objeto por cada sessionKey con los siguientes campos relevantes:

Campo Tipo Descripción
compactionCount number | null Veces que se ha ejecutado auto-compaction
memoryFlushAt timestamp | null Última vez que se ejecutó el flush
memoryFlushCompactionCount number | null compactionCount cuando se hizo el último flush
contextTokens number Estimación de tokens en contexto actual

Estado actual observado

// Ejemplo de sesión WhatsApp principal
"agent:main:whatsapp:dm:+34690395233": {
  "compactionCount": 0,
  "memoryFlushAt": null,
  "memoryFlushCompactionCount": null
}

// Ejemplo de sesión MS Teams con compaction
"agent:main:msteams:group:19:d2e55d7c...@thread.v2": {
  "compactionCount": 2,
  "memoryFlushAt": null,
  "memoryFlushCompactionCount": null
}
⚠️ Observación Importante

A pesar de que compactionCount: 2 indica que ha habido compaction, memoryFlushAt sigue siendo null. Esto sugiere que el flush no se ejecutó antes de esas compactions, o los campos no se están persistiendo correctamente.

5. Análisis de Evidencia

Evidencia positiva: JSONL transcripts

Encontramos 18 ocurrencias de NO_REPLY en los transcripts JSONL, con el patrón exacto del memory flush:

// Archivo: 0bcee1ba-4e58-41b2-a365-41e3c12840e1.jsonl (Jan 31)

// User prompt (silent injection)
{
  "role": "user",
  "text": "Pre-compaction memory flush. Store durable memories now 
           (use memory/YYYY-MM-DD.md; create memory/ if needed). 
           If nothing to store, reply with NO_REPLY."
}

// Assistant response
{
  "role": "assistant",
  "thinking": "Good, I've updated the memory file with the current state. 
              The summary is now persisted. I should reply NO_REPLY since 
              this was a memory flush request...",
  "text": "NO_REPLY"
}
✅ Confirmado

El sistema memoryFlush funcionó correctamente el 31 de enero. El agente recibió el prompt, reflexionó sobre qué guardar, escribió a memory/2026-01-31.md, y respondió NO_REPLY.

Archivos de memoria existentes

Archivo Fecha modificación Tamaño
memory/2026-01-31.md Jan 31 10:59 1.6 KB
memory/2026-02-02.md Feb 2 17:41 2.2 KB
memory/2026-02-04.md Feb 4 17:12 0.9 KB
📝 Nota

Hay gaps en Feb 1 y Feb 3. Posiblemente no hubo suficiente actividad para triggear el flush, o las sesiones se resetearon antes de alcanzar el threshold.

6. Estado Actual del Sistema

Configuración de compaction

// ~/.openclaw/openclaw.json (extracto)
{
  "agents": {
    "defaults": {
      "compaction": {
        "mode": "safeguard"
      }
    }
  }
}
ℹ️ Safeguard Mode

El modo "safeguard" permite que OpenClaw maneje compaction automáticamente con defaults seguros. No hay override explícito de memoryFlush, por lo que usa los defaults del sistema.

Sesiones activas por uso de contexto

Sesión Modelo Tokens % Contexto Status
whatsapp:dm:+34690395233 gpt-5.2 181k/400k 45% Watch
cron:...d3f99a claude-opus-4-5 109k/200k 54% Watch
agent:main:main claude-opus-4-5 69k/200k 34% OK
whatsapp:dm:+34667475153 claude-opus-4-5 48k/200k 24% OK

Hooks disponibles

$ openclaw hooks list

Hooks (3/4 ready)
┌───────────┬──────────────────┬────────────────────────────────────────┐
│ Status    │ Hook             │ Description                            │
├───────────┼──────────────────┼────────────────────────────────────────┤
│ ✓ ready   │ 🚀 boot-md        │ Run BOOT.md on gateway startup         │
│ ✓ ready   │ 📝 command-logger │ Log all command events                 │
│ ✓ ready   │ 💾 session-memory │ Save session context on /new command   │
│ ✗ missing │ 😈 soul-evil      │ Swap SOUL.md with SOUL_EVIL.md         │
└───────────┴──────────────────┴────────────────────────────────────────┘
⚠️ No hay hook before_compaction

El hook session_before_compact mencionado en docs ("Pi exposes a hook in the extension API") no aparece en la lista de hooks disponibles. Esto puede significar: (a) está en la extension API de Pi, no en OpenClaw hooks CLI, o (b) no está implementado todavía.

7. ¿Por Qué No Hay Archivos Recientes?

Varias razones explican por qué no vemos actividad de memoryFlush en los últimos días:

Hipótesis 1: Threshold no alcanzado

El flush se dispara cuando:

contextTokens > contextWindow - reserveTokensFloor - softThresholdTokens

Para Claude Opus (200k window):
contextTokens > 200,000 - 20,000 - 4,000 = 176,000 tokens

La sesión más cargada tiene 109k tokens (54%), aún lejos del threshold de ~176k (88%).

Hipótesis 2: Daily session reset

OpenClaw hace reset automático de sesión a las 4:00 AM por defecto. Si las sesiones se resetean antes de alcanzar el threshold, nunca se dispara el flush.

Hipótesis 3: Cambio de modelo mid-conversation

Si una sesión cambia de modelo (ej: claude → gpt-5.2), el context window cambia. Un session con 181k tokens en gpt-5.2 (400k window) está al 45%, muy lejos del threshold.

Hipótesis 4: Session archivos son de sesiones antiguas

Los archivos memory/*.md fueron creados por sesiones que ya no existen (session IDs diferentes). Las sesiones actuales no han alcanzado el threshold todavía.

8. Recomendaciones y Próximos Pasos

Opción A: Ajustar thresholds de memoryFlush

Reducir el threshold para que el flush ocurra más temprano:

// En ~/.openclaw/openclaw.json
{
  "agents": {
    "defaults": {
      "compaction": {
        "mode": "safeguard",
        "memoryFlush": {
          "enabled": true,
          "softThresholdTokens": 20000,  // Trigger más temprano
          "reserveTokensFloor": 30000    // Más margen
        }
      }
    }
  }
}

Pro: Flush ocurre más frecuentemente. Con: Más turns silenciosos, más tokens consumidos.

Opción B: Personalizar el prompt de flush

Hacer el prompt más específico para tu workflow:

"memoryFlush": {
  "prompt": "Session approaching compaction. Write critical context to memory/YYYY-MM-DD.md:\n- Key decisions made\n- User preferences learned\n- Action items pending\n- Important facts mentioned\nIf nothing critical, reply NO_REPLY.",
  "systemPrompt": "You are about to lose context. Preserve anything important."
}

Opción C: Hook en session-memory

El hook session-memory ya existe y se ejecuta en /new. Se podría extender para:

Opción D: Inyección en before_agent_start

Si no hay hook nativo, se puede inyectar lógica en el system prompt para recordar al agente que escriba a memoria periódicamente:

// En AGENTS.md o system prompt
"Every ~10 turns, proactively write a summary of the conversation
to memory/YYYY-MM-DD.md. Don't wait for compaction."

Opción E: Plugin custom

Crear un plugin de OpenClaw que intercepte eventos de sesión y fuerce flush basado en criterios custom (tiempo, turns, tokens).

💡 Recomendación

Empezar con Opción A (bajar threshold) + Opción B (prompt específico). Es la solución menos invasiva y usa mecanismos ya existentes. Monitorear sessions.json para verificar que memoryFlushAt se actualiza.

9. Checklist de Verificación

Para verificar que memoryFlush está funcionando:

  1. Revisar sessions.json
    cat ~/.openclaw/agents/main/sessions/sessions.json | \
      jq '.[] | select(.memoryFlushAt != null)'
  2. Buscar NO_REPLY en transcripts recientes
    grep -l "NO_REPLY" ~/.openclaw/agents/main/sessions/*.jsonl | \
      xargs ls -la
  3. Monitorear archivos de memoria
    watch -n 60 'ls -la /root/kleo/memory/'
  4. Verificar logs del gateway
    grep -i "memory.*flush\|compaction" /tmp/openclaw/openclaw-*.log
  5. Forzar threshold bajo temporalmente para testear que el mecanismo funciona

📄 Versión Markdown

# OpenClaw Compaction & Memory Flush — Informe de Investigación

**Fecha:** 2026-02-04  
**Autor:** 498AS Research  
**Sistema:** OpenClaw 2026.1.29

---

## Resumen Ejecutivo

Esta investigación analiza el mecanismo de **compaction** y **memory flush** de OpenClaw, su configuración actual, y las discrepancias observadas entre documentación y comportamiento real.

### Key Findings

- ✅ **memoryFlush existe y funciona** — 18 invocaciones con `NO_REPLY` en transcripts JSONL antiguos
- ⚠️ **No hay tracking activo** — Campos `memoryFlushAt` y `memoryFlushCompactionCount` son `null`
- 🔧 **Configuración en safeguard mode** — Compaction usa defaults del sistema
- 📂 **Archivos de memoria existen** — 3 archivos en `memory/` con gaps en días intermedios

---

## 1. Objetivo

Entender y extender el sistema de hooks para ejecutar lógica **before/after compaction**. 

Caso de uso: persistir información crítica antes de que el contexto sea compactado.

---

## 2. Documentación vs Realidad

### Documentación dice:
- `memoryFlush.enabled: true` (default)
- Soft threshold: 4000 tokens
- Reserve floor: 20000 tokens
- Silent turn con `NO_REPLY`

### Código confirma:
- Sistema existe en `agent-runner-memory.js`
- Tracking fields en sessions.json
- Prompt injection funciona

### Discrepancias:
| Aspecto | Docs | Realidad |
|---------|------|----------|
| Tracking fields | Se actualizan | Todos `null` |
| Hook before_compact | "Pi exposes it" | No visible en CLI |
| Evidencia ejecución | Expected | 18 ocurrencias ✓ |

---

## 3. Cómo Funciona memoryFlush

### Condiciones para trigger:
1. `memoryFlush.enabled = true`
2. Workspace writable
3. `contextTokens > contextWindow - reserveFloor - softThreshold`
4. `memoryFlushCompactionCount < compactionCount`
5. Sesión Pi embedded (no CLI)

### Defaults:
- reserveTokensFloor: 20,000
- softThresholdTokens: 4,000
- Para 200k window: trigger a ~176k tokens (88%)

---

## 4. Estado Actual

### Sessions activas:
| Sesión | Tokens | % | Status |
|--------|--------|---|--------|
| whatsapp:+34690395233 | 181k/400k | 45% | Watch |
| cron:...d3f99a | 109k/200k | 54% | Watch |
| main | 69k/200k | 34% | OK |

### Hooks disponibles:
- ✓ boot-md
- ✓ command-logger  
- ✓ session-memory
- ✗ soul-evil (missing)
- ✗ before_compaction (no existe en CLI)

---

## 5. Por Qué No Hay Actividad Reciente

1. **Threshold no alcanzado** — Sesiones más cargadas están al 54%, threshold es ~88%
2. **Daily reset** — 4:00 AM reset puede ocurrir antes del threshold
3. **Cambio de modelo** — GPT-5.2 tiene 400k window, más difícil alcanzar threshold

---

## 6. Recomendaciones

### Opción A: Bajar thresholds
```json
"memoryFlush": {
  "softThresholdTokens": 20000,
  "reserveTokensFloor": 30000
}
```

### Opción B: Prompt específico
Personalizar el prompt de flush para tu workflow.

### Opción C: Proactive memory
Instruir al agente a escribir memoria cada ~10 turns.

---

## 7. Verificación

```bash
# Check sessions.json
jq '.[] | select(.memoryFlushAt != null)' sessions.json

# Buscar NO_REPLY
grep -l "NO_REPLY" *.jsonl

# Monitor memory files
watch ls -la /root/kleo/memory/
```

---

*Generado por OpenClaw Research Pipeline*