---
title: '@runic-labs/ledger'
description: Contabilidad de solo anexado de tokens gastados frente a tokens ahorrados.
sidebar:
  order: 5
---
`@runic-labs/ledger` es contabilidad pura: nunca impone un presupuesto, nunca rechaza una llamada, nunca estima un coste que no hayas informado. Responde una pregunta: *¿cuánto costó realmente resolver estas decisiones y cuánto se evitó mediante la reutilización?*

```package-install
npm i @runic-labs/ledger
```

## `createLedger(store?)`

```ts
import { createLedger, FileLedgerStore, defaultLedgerFilePath } from "@runic-labs/ledger";

const ledger = createLedger(new FileLedgerStore({ filePath: defaultLedgerFilePath() }));
```

De forma predeterminada, usa un `FileLedgerStore` en `.runic/ledger.json` (o `$RUNIC_HOME/ledger.json`) si no se proporciona ningún almacén.

| Prop | Type | Default | Description |
| - | - | - | - |
| `recordMiss(signature, tokensSpent)?` | `void` | - | Logs a cache miss — the agent generated its own way and spent tokensSpent. |
| `recordHit(signature)?` | `void` | - | Logs a cache hit, recording the same tokensSpent as the original miss for this signature. |
| `summary()?` | `LedgerSummary` | - | Aggregated totals — see below. |
| `all()?` | `LedgerEvent[]` | - | The full raw event log. |
| `clear()?` | `void` | - | Removes all events. |

## `LedgerSummary`

```ts
interface LedgerSummary {
  totalSpent: number;
  totalSaved: number;
  hitsBySignature: Record<string, number>;
}
```

```ts
const summary = ledger.summary();
const totalHits = Object.values(summary.hitsBySignature).reduce((a, b) => a + b, 0);
const tokensWithoutRunic = summary.totalSpent + summary.totalSaved;
const savingsPercent = Math.round((summary.totalSaved / tokensWithoutRunic) * 100);
```

Este es exactamente el cálculo que utilizan tanto `benchmarks/openrouter-savings` como `benchmarks/reuse-sweep` para imprimir sus cifras finales; consulta [Pruebas de rendimiento](/benchmarks).

## Por qué `recordHit` no recibe un argumento `tokensSpent`

El valor de un acierto se define como *lo que haya costado el fallo original para esa firma*: el libro mayor lo busca por sí mismo, en lugar de confiar en que quien llama lo repita correctamente en cada acierto. Si no existe un fallo previo para una firma que, de algún modo, está teniendo un acierto (no debería suceder en un uso normal, pero el libro mayor no asume que no pueda ocurrir), registra `0` en lugar de adivinar.

## Backends de almacenamiento

El mismo patrón que `@runic-labs/cache`: `MemoryLedgerStore` para pruebas y ejecuciones efímeras, y `FileLedgerStore` para persistencia entre invocaciones de procesos independientes. Implementa tú mismo la interfaz `LedgerStore` para un backend distinto:

```ts
interface LedgerStore {
  append(event: LedgerEvent): void;
  all(): LedgerEvent[];
  clear(): void;
}
```

## Siguiente

- [Caché](/cache) — el almacén correspondiente con claves de firma
- [CLI](/cli) — `runic ledger status` lee este resumen desde la línea de comandos
