Fact-ledger — verbatim cross-turn recall
Fact-ledger — verbatim cross-turn recall of exact tool results across the last 5 turns, no extension API; the anti-fabrication surface under the classifier.
The essence
The fact-ledger is the platform's verbatim cross-turn recall surface. It stores the exact JSON-serialised ActionResult.data returned by every successful tool call and replays the most recent five turns of those facts to the intent classifier on every subsequent chat turn. Your extension writes nothing to the fact-ledger directly — the platform populates it automatically after each successful tool call. There is no fact-ledger decorator and no fact-ledger accessor on the context object. Your only contract is to return clean, structured data inside ActionResult.data, and the platform takes care of the rest.
Do not use the fact-ledger as persistent state — it is bounded to the last five turns; use ctx.store for longer-lived data. Do not rely on ActionResult.summary as the recall surface — the platform records .data (the structured payload), not the human-readable prose summary.
Why it exists
Without verbatim recall, an agent answering a follow-up turn has only the prose summaries of earlier turns to work from — and prose paraphrases away exact facts. A list_tasks call returns 36 tasks; a naive follow-up turn announces "you have 50 tasks", filling in a plausible-but-wrong number because the precise count was no longer in front of it.
The fact-ledger closes that gap: it preserves the exact structured data your tool returned for the last few turns, so the agent always has the real numbers, IDs, and addresses to ground its answers — never a paraphrase it might get wrong.
What it stores
After every successful tool call, the platform records a compact entry for that turn capturing the verbatim structured payload your tool returned:
| Field | Source | Purpose |
|---|---|---|
| App | The extension owning the tool | Lets the agent attribute a fact to its source extension |
| Tool | The @chat.function name that ran | Identifies the specific tool that produced the data |
| Data | The JSON-serialised ActionResult.data | The verbatim payload the agent later sees |
On every chat turn, the platform surfaces these FACTS: lines for the most recent five turns directly under each turn's prose preview.
Caps and limits
The fact-ledger is bounded so it stays affordable even when the agent runs on every chat turn.
| Constraint | Value |
|---|---|
| Aggregate fact-ledger size per turn | ≤ 3000 chars |
| Number of turns retained in the agent's view | Last 5 |
| PII handling | Sensitive values are masked before the agent sees them by default |
| Verbatim contract | The platform records the structured ActionResult.data, not the prose summary |
| Deep serialisation | Nested structures are preserved at full depth — no shallow projections |
When a turn's facts exceed the aggregate cap, the platform truncates from the tail (the oldest tool calls in the turn) and surfaces a warning marker. Truncation is rare in practice — typical tool calls produce 100-500 character payloads.
PII handling
When a tool returns sensitive content (an email body, a customer name, a phone number), the platform masks email-shaped, phone-shaped, and named-entity strings before the agent ever sees them. PII is masked by default — your structured data is recorded faithfully, but the recall surface the agent reads is redacted.
What the agent remembers
For each recent turn, the agent has both a short prose preview and the verbatim structured facts your tool returned that turn.
What this shows: a two-turn exchange where the second turn's anaphoric reference ("отправь на тот же") is resolvable because the first turn's structured email address was preserved verbatim.
- Turn 1 — user: "показать письма за сегодня". Your
list_inboxtool returnsdatacontaining{"unread": 8, "messages": [{"id": "abc", "from": "sarah@example.com", "subject": "Q3 plan"}]}. The platform records that structured payload verbatim. - Turn 2 — user: "отправь на тот же адрес «статус по проекту»".
In the second turn, the agent resolves "тот же адрес" by reading the previous turn's recorded facts and extracting sarah@example.com verbatim from the structured data.messages[0].from field — never from the prose summary, which might have paraphrased the address away.
What you DO NOT do
The fact-ledger has no extension API. Specifically:
- There is no fact-ledger decorator. Attempts to import one fail at module load.
- The context object exposes no fact-ledger accessor — reading one back returns nothing.
- The skeleton has no writer method on the context object either — both the skeleton and the fact-ledger are read-only from extension code; the platform is the sole writer to both.
Your only contract: return clean structured payloads in ActionResult.data.
- If you put 30 KB of nested JSON into
.data, the platform truncates per the aggregate cap (3000 chars per turn) and the agent sees only the leading portion. - If you put PII in plaintext, the platform masks it before the agent sees it.
- If you put unstructured prose in
.data(a string instead of a dict), the agent cannot index into fields — anaphora resolution will fail.
Federal invariants
The fact-ledger is defended by platform-enforced guarantees you can rely on:
Verbatim, not paraphrased
The platform records your [ActionResult](/en/reference/glossary/).data exactly — never the prose summary.
Full-depth preservation
Nested structures are kept at full depth — no shallow projections that lose fields.
Bounded per turn
≤3000 chars aggregate per turn; the oldest tool calls in a turn are truncated first when exceeded.
PII masked by default
Sensitive values are masked before the agent ever sees them.
Always replayed
Recent facts are always made available to the agent under each turn's preview, every turn.
See the federal invariants page for the full inventory.
What's next
Context channels
How the fact-ledger fits alongside skeleton and ctx.cache.
Skeletons
The other classifier-visible channel — TTL-refreshed awareness probe.
Federal invariants
Every contract enforced at the boundary, including the fact-ledger ones.
Cache vs Store
The two data surfaces extensions own directly.
Skeletons
Skeletons — small live data feeds that keep Webbee aware of your extension's user state every chat turn, so the agent routes precisely without prompting first.
Cache vs store
Cache vs store — when to use ctx.cache (TTL, ephemeral) versus ctx.store (durable, queryable) in an extension, plus how to migrate state between the two layers.