Ramon SosaRamon Sosa·10 min de lectura

Claude Code compacta tus sesiones inactivas: lo que me cuestan las mías

Dos cambios recientes tocan lo que más me cuesta: sesiones largas que dejo abiertas y hooks que fallan sin avisar. Qué cambió y qué voy a hacer.

En este artículo
  1. Mis números
  2. Por qué volver cuesta tanto
  3. 1. Compacta cuando te vas
  4. Lo que medí el 19 de octubre
  5. 2. Hooks que bloquean si fallan
  6. Lo primero que hice
  7. Lo que me falta
  8. Revisa lo tuyo

Desde la 2.1.299 (14 oct), Claude Code compacta solo, en la terminal, una conversación de más de 200,000 tokens que lleva unos 54 minutos quieta. Y desde la 2.1.295 (8 oct), un hook puede frenar la acción si falla.

Dejo sesiones de Claude Code abiertas todo el día. Salgo a una llamada, vuelvo una hora después y sigo donde iba. Lo que no veía es lo que cuesta ese regreso. Uno de los dos cambios de estos días ataca justo eso. El otro toca algo que yo daba por hecho: que mis hooks siempre corren.

Mis números

Hace unas semanas conté mis propias sesiones desde los registros que guarda Claude Code. En 14 días de septiembre fueron 264 sesiones y 9,249 peticiones. Así se repartió el costo:

Son dos cuentas distintas. Esta mide costo y la hice el 30 de septiembre sobre los registros de esos 14 días. Las 195 sesiones de qué es Claude Code cuentan solo sesiones que abrí yo, sin subagentes, con los registros que tenía el 12 de octubre.

Releer la caché
69%
Crear la caché y respuestas
31%
Mis sesiones, 14 días de septiembre 2026, contadas de los registros de Claude Code el 30 de septiembre. El peso por tipo de token es mío (crear caché 1.25, releer 0.1, respuesta 5), no la factura.

Más de dos tercios del costo es releer lo que ya estaba en la conversación, no lo que la IA escribe. Y seis sesiones se llevaron el 57% de todo. Eran las largas: cientos de peticiones sobre la misma conversación.

Así lo conté. Claude Code guarda cada sesión como un archivo en tu Mac, y cada respuesta trae su cuenta de tokens: los que leyó de la caché, los que escribió en ella y los que produjo. Sumé esas tres cantidades por sesión y le di a cada una su peso. Son pesos parecidos a los de la factura, no la factura.

Si quieres poner números a lo tuyo antes de cambiar nada, la calculadora de costo de agentes usa precios del día.

Por qué volver cuesta tanto

Un ejemplo con números redondos. Tienes una conversación de 200,000 tokens, sales una hora y al volver escribes «sigue».

  1. Con la caché viva, Claude Code relee los 200,000 tokens. Pesan 0.1 cada uno: 20,000 unidades.
  2. Con la caché vencida, tiene que escribirlos otra vez. Pesan 1.25 cada uno: 250,000 unidades.
  3. Mismo mensaje, mismo resultado, y 12.5 veces más peso (250,000 entre 20,000).

No es dinero, son mis unidades. La proporción es lo que importa: el mensaje que escribes al volver de una llamada de una hora paga la conversación entera como si fuera nueva. Con la tarifa publicada de escritura de una hora (2 en vez de 1.25) serían 20 veces.

  1. Minuto 0Sales a una llamada. La conversación ya pesa más de 200,000 tokens.
  2. Minuto 54Con la 2.1.299, Claude Code la compacta en la terminal.
  3. Minuto 60La caché de una hora se vence. Sin compactar, el siguiente mensaje reconstruye todo.
  4. VuelvesCon compactación sigues desde el resumen. Sin ella, pagas la reconstrucción completa.
Los 54 minutos salen del changelog de la 2.1.299; la caché de una hora es la que explico en la sección siguiente. Tu hora de regreso es la que decide.

1. Compacta cuando te vas

Claude Code guarda tu conversación en una caché que dura una hora. Mientras está viva, cada mensaje sale barato. Si te vas más de una hora, la caché se pierde y tu próximo mensaje tiene que volver a procesar toda la conversación.

La compactación en reposo es eso: Claude Code resume una conversación larga mientras no estás, antes de que la caché se venza. Llegó en la versión 2.1.299, del 14 de octubre: si una conversación de más de 200,000 tokens se queda quieta unos 54 minutos en la terminal, se compacta sola. Cuando vuelves, sigue desde el resumen y no desde cero.

Fuentes: changelog oficial de Claude Code, 2.1.299; Classmethod, 14 oct 2026

Lo que pierdes: compactar es resumir, y un resumen deja detalles fuera. Si estás en medio de algo donde cada detalle cuenta, se apaga con "idleCompaction": false en tu settings.json. El umbral de 200,000 se cambia con CLAUDE_CODE_IDLE_COMPACT_MIN_TOKENS.

Lo dejo prendido. La comparación:

PrendidaApagada
Si te vas más de 54 minutos con más de 200,000 tokensSe resume sola y al volver sigues desde el resumenNo se toca: pasada la hora, el mensaje de vuelta procesa todo
Qué pierdesDetalles que el resumen deja fueraNada de la conversación, pero pagas la reconstrucción
Dónde la usaríaSesiones largas de trabajo con pausas, como llamadas o el almuerzoUna depuración donde cada línea del error cuenta
Las dos primeras filas salen del changelog y de la mecánica de la caché. La última es mi criterio.

Lo que medí el 19 de octubre

El 16 de octubre escribí aquí que no había medido cuántas de mis seis sesiones caras pasaron de 200,000 tokens. Ahora medí todas mis sesiones guardadas, no solo esas seis.

La cuenta es sencilla. Para cada sesión ordeno las respuestas por hora. Si entre dos respuestas pasan 54 minutos o más y la conversación ya pesaba 200,000 tokens o más, cuento una vuelta. Después miro esa respuesta: si leyó de la caché menos de la mitad de lo que ya había en la conversación, la cuento como caché reconstruida.

Lo que contéResultado
Sesiones con 3 o más respuestas698
Pasaron de 200,000 tokens120
Con al menos una pausa de 54 min o más por encima de 200,00063
Vueltas tras esa pausa470
Entre 54 min y 3 h190
Entre 3 h y 12 h102
Más de 12 h178
Con la caché casi toda reconstruida452 de 470
En la terminal (cli)9 sesiones, 10 vueltas
En la app de escritorio54 sesiones, 460 vueltas
Mis sesiones guardadas en esta Mac, del 4 de junio al 19 de octubre de 2026. Solo la conversación principal, sin subagentes. Los registros siguen creciendo: el script puede dar una o dos sesiones más.

Veo tres cosas. Primero, 63 de mis 120 sesiones grandes, más de la mitad, tuvieron al menos una de esas pausas. Segundo, en 452 de 470 vueltas la caché ya no estaba. Tercero, casi todo pasa en la app de escritorio: 54 de las 63 sesiones y 460 de las 470 vueltas. El changelog de la 2.1.299 dice que la compactación en reposo llegó «en la terminal», así que no sé si la app la aplica. En la terminal solo conté 10 vueltas.

Las 190 vueltas de entre 54 minutos y 3 horas, una llamada o el almuerzo, son el caso que la función describe. Lo que no sé: qué hace con las 178 de más de 12 horas, que son noches y fines de semana. Tampoco medí cuántas habrían cambiado con la 2.1.299. Mi claude de terminal está en la 2.1.294, pero la app de escritorio ya corre la 2.1.299 en 28 de mis 60 sesiones más recientes, así que esta cuenta mezcla antes y después. Y una pausa entre dos respuestas puede ser también una herramienta lenta; no lo separé.

Si quieres tu número, este es el script que usé. Solo lee y solo imprime totales:

# contar-vueltas.py: solo lee. Imprime totales, nunca el contenido de tus conversaciones.
import glob, json, os
from datetime import datetime

PAUSA = 54 * 60      # segundos sin respuesta
GRANDE = 200_000     # tokens de contexto

def contexto(u):
    return sum(u.get(k) or 0 for k in ("input_tokens", "cache_read_input_tokens", "cache_creation_input_tokens"))

sesiones = grandes = con_pausa = 0
vueltas = []
sesiones_cli = 0
for ruta in glob.glob(os.path.expanduser("~/.claude/projects/*/*.jsonl")):
    resp = {}
    cli = False
    for linea in open(ruta, errors="ignore"):
        try:
            d = json.loads(linea)
        except ValueError:
            continue
        cli = cli or d.get("entrypoint") == "cli"
        u = (d.get("message") or {}).get("usage")
        if d.get("type") != "assistant" or d.get("isSidechain") or not u or not d.get("timestamp"):
            continue
        t = datetime.fromisoformat(d["timestamp"].replace("Z", "+00:00")).timestamp()
        clave = (d["message"].get("id"), d.get("requestId"))
        if clave not in resp or contexto(u) >= resp[clave][1]:
            resp[clave] = (t, contexto(u), u.get("cache_read_input_tokens") or 0)
    seq = sorted(resp.values())
    if len(seq) < 3:
        continue
    sesiones += 1
    grandes += max(s[1] for s in seq) >= GRANDE
    hubo = False
    for antes, despues in zip(seq, seq[1:]):
        if despues[0] - antes[0] >= PAUSA and antes[1] >= GRANDE:
            hubo = True
            vueltas.append(((despues[0] - antes[0]) / 60, despues[2] < antes[1] * 0.5, cli))
    con_pausa += hubo
    sesiones_cli += hubo and cli

print(f"sesiones con 3 o más respuestas: {sesiones}")
print(f"  pasaron de 200,000 tokens: {grandes}")
print(f"  con una pausa de 54 min o más por encima de 200,000: {con_pausa}")
print(f"vueltas tras esa pausa: {len(vueltas)}")
print(f"  entre 54 min y 3 h: {sum(54 <= m < 180 for m, *_ in vueltas)}")
print(f"  entre 3 h y 12 h: {sum(180 <= m < 720 for m, *_ in vueltas)}")
print(f"  más de 12 h: {sum(m >= 720 for m, *_ in vueltas)}")
print(f"  con la caché casi toda reconstruida: {sum(v[1] for v in vueltas)}")
print(f"  en la terminal (cli): {sesiones_cli} sesiones, {sum(v[2] for v in vueltas)} vueltas")
print(f"  en la app de escritorio: {con_pausa - sesiones_cli} sesiones, {sum(not v[2] for v in vueltas)} vueltas")

2. Hooks que bloquean si fallan

Un hook es un comando que corre solo en un momento fijo. Hasta ahora, si ese comando no arrancaba, se pasaba de tiempo o salía con un error raro, Claude Code seguía como si nada. Para un hook que avisa, eso está bien. Para uno que te protege, es un hueco.

Desde la 2.1.295, del 8 de octubre, un hook de comando o HTTP puede llevar "onFailure": "block": si falla, la acción se frena en vez de pasar.

Fuentes: changelog oficial de Claude Code, 2.1.295; Classmethod, 9 oct 2026

.claude/settings.json (ejemplo)

{
  "hooks": {
    "PreToolUse": [
      { "matcher": "Bash",
        "hooks": [
          { "type": "command",
            "command": "python3 .claude/hooks/no-borrar.py",
            "onFailure": "block" }
        ] }
    ]
  }
}
Un guardia que revisa cada comando antes de que corra. Si el script se cae, el comando no pasa.

Un detalle: onFailure solo mira si el hook arrancó, si se pasó de tiempo o si salió con un código que no esperaba. Si tu script falla por dentro y aun así termina con 0, pasa igual. El guardia tiene que fallar bien.

Para decidir si un hook lleva block, me hago tres preguntas:

  1. ¿Corre antes de la acción? Un guardia que corre después ya no puede frenar nada.
  2. Si falla y nadie se entera, ¿qué pasa? Si la respuesta es que se borra algo o se sube algo que no debía, es un guardia.
  3. Si falla, ¿prefiero parar o seguir? Si prefiero seguir, lo dejo sin onFailure.

Lo primero que hice

Revisar qué tengo. Esto es lo que salió en mi Mac:

~: zsh

claude(--version)

2.1.294 (Claude Code)

python3(contar-hooks.py ~/.claude/settings.json (script mío))

hooks 7 onFailure 0
Salida real en mi Mac, 16 oct 2026. El conteo de hooks lo saqué con un script de Python sobre ~/.claude/settings.json.

El claude de mi terminal estaba en la 2.1.294: no tenía ninguno de los dos cambios. Primero toca actualizar.

Después, los 7 hooks. Esto hace cada uno:

HookCuándo correQué hace¿Guardia?
al-grano-activateAl abrir una sesiónCarga mis reglas para que las respuestas sean cortasNo
al-grano-trackerEn cada mensaje míoMe recuerda esas reglasNo
session-size-hookEn cada mensaje míoAvisa si la conversación pasa de 150 peticionesNo, avisa
session_bridgeAl terminar cada respuestaGuarda notas de la sesión en mi bóvedaNo
pane-status-hook (3)Al terminar, al avisar y al pedir permisoMarca el estado de la sesión en mi panelNo
Los 7 hooks de mi ~/.claude/settings.json, revisados el 19 oct 2026. Mis proyectos tienen hooks propios y no entran en esta cuenta.

Ninguno de esos 7 es un guardia. Si uno se cae, prefiero seguir trabajando, así que se quedan sin onFailure. El día que agregue uno que proteja una carpeta o frene un borrado, ese sí lleva block.

El que más se parece a lo de este post es session-size-hook. A las 150 peticiones me avisa que cierre y abra otra sesión, porque cada petición relee toda la conversación. Es regla mía desde el 9 de octubre y es un aviso, no un bloqueo: prefiero decidir yo.

La pregunta para cada hook es una sola: si este falla, ¿prefiero que el trabajo siga o que se pare?

Lo que me falta

  1. Actualizar el claude de mi terminal, que sigue en la 2.1.294.
  2. Averiguar si la app de escritorio aplica la compactación en reposo. Ahí pasan 460 de las 470 vueltas.
  3. Repetir el recuento con el script, ya con la versión nueva, y comparar.

Revisa lo tuyo

Pega esto en Claude Code. Solo lee, no cambia nada:

Dime qué versión de Claude Code tengo, si mis settings tienen idleCompaction configurado y cuáles de mis hooks no tienen onFailure. Para cada hook dime si es un guardia (debería bloquear si falla) o un registro (puede fallar sin parar el trabajo). No cambies nada.

En la respuesta, mira cuatro cosas:

  1. La versión. Antes de la 2.1.295 no existe onFailure, y antes de la 2.1.299 no hay compactación en reposo.
  2. idleCompaction. Si no aparece en tus settings, no lo configuraste: la función se comporta como venga por defecto en tu versión.
  3. Los hooks sin onFailure. Para un registro es lo normal.
  4. Los que Claude marca como guardia y no tienen block. Ese es el primero que arreglas.