# Enable context offload

> Turn on offload.enabled, register plugins.slots.contextEngine, apply the after-tool-call messages patch, tune compression ratios, and confirm Mermaid injection paths.

- Repository: TencentCloud/TencentDB-Agent-Memory
- GitHub: https://github.com/TencentCloud/TencentDB-Agent-Memory
- Human docs: https://grok-wiki.com/public/docs/tencentcloud-tencentdb-agent-memory-5a33bbf5540a
- Complete Markdown: https://grok-wiki.com/public/docs/tencentcloud-tencentdb-agent-memory-5a33bbf5540a/llms-full.txt

## Source Files

- `README.md`
- `src/offload/index.ts`
- `src/config.ts`
- `openclaw.plugin.json`
- `scripts/openclaw-after-tool-call-messages.patch.sh`
- `scripts/setup-offload.sh`
- `index.ts`

---

---
title: "Enable context offload"
description: "Turn on offload.enabled, register plugins.slots.contextEngine, apply the after-tool-call messages patch, tune compression ratios, and confirm Mermaid injection paths."
---

Context offload is the plugin’s **symbolic short-term memory** path: when `offload.enabled` is true, `index.ts` calls `registerOffload()`, which registers hooks plus an OpenClaw `contextEngine` implementation (`memory-tencentdb`). Tool results are written under `~/.openclaw/context-offload` (or `offload.dataDir`), condensed via L1/L1.5/L2 into Mermaid canvases, and re-injected into the live message list so long tool loops stay under the context window.

Offload is **independent of long-term L0–L3 memory**. You can run layered memory without offload; enabling offload is optional and defaults to off.

<Warning>
Without `plugins.slots.contextEngine: "memory-tencentdb"`, the module logs a warning and sets an internal reject flag: **all offload functions are disabled**, even if `offload.enabled` is true.
</Warning>

## Prerequisites

| Requirement | Notes |
| :--- | :--- |
| OpenClaw plugin installed | `openclaw plugins install @tencentdb-agent-memory/memory-tencentdb` (OpenClaw ≥ 2026.3.13, Node ≥ 22.16) |
| Plugin enabled | Long-term memory may already be on; offload is a separate switch |
| Writable config | `~/.openclaw/openclaw.json` |
| Patch targets | OpenClaw `dist/**` must contain `after_tool_call` hook event construction (patch script rewrites those files) |

## What enablement turns on

| Surface | Behavior when enabled |
| :--- | :--- |
| `registerOffload(api, cfg.offload)` | Registers `after_tool_call`, `before_prompt_build` / L3 paths, L1/L1.5/L2 pipelines, reclaim scheduler |
| `api.registerContextEngine("memory-tencentdb", …)` | Claims the context-engine slot; `OffloadContextEngine.assemble()` owns context assembly (not used in `mode: "collect"`) |
| Storage under data root | Per-agent `refs/`, `mmds/`, `offload-<sessionId>.jsonl`, `state.json` |
| L3 compression | Mild score-cascade replacement and aggressive deletion driven by token ratios vs context window |
| Mermaid injection | Active MMD via `_mmdContextMessage`; history MMD after aggressive deletion |

```text
~/.openclaw/openclaw.json
  plugins.slots.contextEngine = "memory-tencentdb"
  plugins.entries.memory-tencentdb.config.offload.enabled = true
        │
        ▼
  index.ts → registerOffload()
        │
        ├─ hooks: after_tool_call / before_prompt_build / llm_input L3
        ├─ registerContextEngine("memory-tencentdb")  [unless mode=collect]
        └─ dataRoot: ~/.openclaw/context-offload/<agent>/
              refs/*.md   mmds/*.mmd   offload-<session>.jsonl   state.json
```

## Enable procedures

Use either the one-shot script (recommended when you also want the patch + compaction defaults applied together) or manual JSON edits.

### Option A — `setup-offload.sh` (all-in-one)

From the installed package (or repo checkout):

```bash
# Enable (backend-oriented example used by the script)
bash scripts/setup-offload.sh --enable \
  --user-id "<userId>" \
  --backend-url "http://host:port" \
  [--backend-api-key "<token>"]

# Status / disable
bash scripts/setup-offload.sh --status
bash scripts/setup-offload.sh --disable
```

Enable steps the script performs (in order):

1. **Patch** — runs `scripts/openclaw-after-tool-call-messages.patch.sh`; non-zero exit aborts enable (exit code 2).
2. **Slot** — sets `plugins.slots.contextEngine` to `memory-tencentdb`.
3. **Offload config** — sets `plugins.entries.memory-tencentdb.config.offload` with `enabled`, `backendUrl`, `userId`, default `backendTimeoutMs` 120000, optional `backendApiKey`.
4. **Compaction** — sets `agents.defaults.compaction.mode` to `safeguard`.

Backs up `openclaw.json` to `openclaw.json.bak.<timestamp>` before writing. Restart the OpenClaw gateway after success.

<Note>
`--enable` requires `--user-id` and `--backend-url` (URL must start with `http://` or `https://`). Local-only offload without a backend is configured manually (see Option B + `mode` / omit `backendUrl`).
</Note>

### Option B — Manual config

#### 1. Turn on offload

OpenClaw stores plugin config under `plugins.entries` (what `setup-offload.sh` and `src/offload/types.ts` document). Minimal local mode:

```json
{
  "plugins": {
    "entries": {
      "memory-tencentdb": {
        "enabled": true,
        "config": {
          "offload": {
            "enabled": true
          }
        }
      }
    }
  }
}
```

With optional tuning and backend routing:

```json
{
  "plugins": {
    "entries": {
      "memory-tencentdb": {
        "enabled": true,
        "config": {
          "offload": {
            "enabled": true,
            "mode": "local",
            "model": "openai/your-model-id",
            "mildOffloadRatio": 0.5,
            "aggressiveCompressRatio": 0.85,
            "mmdMaxTokenRatio": 0.2,
            "defaultContextWindow": 200000,
            "dataDir": "/absolute/path/to/context-offload"
          }
        }
      }
    }
  }
}
```

| Key | Type | Default | Role |
| :--- | :--- | :--- | :--- |
| `enabled` | boolean | `false` | Master switch; false skips `registerOffload` entirely |
| `mode` | `"local"` \| `"backend"` \| `"collect"` | auto: `backend` if `backendUrl` set, else `local` | Where L1/L1.5/L2 LLM work runs; `collect` skips L3 + context engine registration |
| `model` | string | host default model | `provider/model-id` for local offload LLM |
| `temperature` | number | `0.2` | Offload LLM temperature |
| `disableThinking` | boolean \| strategy string | `false` | Local-mode only: disable reasoning/thinking on supported providers |
| `forceTriggerThreshold` | number | `4` | Pending tool pairs that force L1 |
| `dataDir` | string | `~/.openclaw/context-offload` | Absolute data root override |
| `defaultContextWindow` | number | `200000` | Window used when host does not supply one |
| `maxPairsPerBatch` | number | `20` | Max tool pairs per L1 batch |
| `l2NullThreshold` | number | `4` | Trigger L2 when `node_id=null` entries reach this count |
| `l2TimeoutSeconds` | number | `300` | Trigger L2 if none ran for this many seconds |
| `mildOffloadRatio` | number | `0.5` | Mild L3 when tokens ≥ window × ratio |
| `aggressiveCompressRatio` | number | `0.85` | Aggressive L3 when tokens ≥ window × ratio |
| `mmdMaxTokenRatio` | number | `0.2` | Token budget fraction for MMD injection |
| `backendUrl` | string | — | Remote offload API base; implies `mode: "backend"` when `mode` omitted |
| `backendApiKey` | string | — | Backend auth token |
| `backendTimeoutMs` | number | `120000` (runtime parse) | Backend call timeout (ms) |
| `offloadRetentionDays` | number | `0` | Reclaim sessions/refs/mmds older than N days; `0` off; effective min `3` |
| `logMaxSizeMb` | number | `50` | Cap for offload `*.log` files under data root |
| `userId` | string | machine primary IPv4 | Sent as `X-User-Id` on backend requests |

#### 2. Register the context engine slot

```json
{
  "plugins": {
    "slots": {
      "contextEngine": "memory-tencentdb"
    }
  }
}
```

Slot ID **must** equal the plugin id `memory-tencentdb` (not a legacy `openclaw-context-offload` name). If the slot is missing or owned by another engine, registration aborts and offload hooks become no-ops.

#### 3. Apply the after-tool-call messages patch

The `after_tool_call` hook needs `event.messages` (session message list). Stock OpenClaw does not always attach it; the patch injects:

```js
messages: ctx.params.session?.messages
```

into the `after_tool_call` hook event object (after `durationMs`).

```bash
# Auto-run on package install (failures ignored)
# package.json → "postinstall": "bash scripts/openclaw-after-tool-call-messages.patch.sh 2>/dev/null || true"

# Explicit / re-apply after OpenClaw upgrade
bash scripts/openclaw-after-tool-call-messages.patch.sh
# or with explicit install root:
bash scripts/openclaw-after-tool-call-messages.patch.sh /path/to/openclaw
```

| Property | Detail |
| :--- | :--- |
| Resolve OpenClaw | `which openclaw` → walk to package root; pnpm shim parsing; common global paths |
| Targets | All `dist/**/*.js` containing `after_tool_call` + nearby `durationMs` |
| Idempotent | Skips files already containing the injection; exit 0 if patched or already skipped |
| Backup | First write creates `*.pre-offload-patch.bak` |
| Debug | `DEBUG=1 bash scripts/openclaw-after-tool-call-messages.patch.sh` |
| Exit codes | `0` success/skip; `1` no file patched and none already patched |

Without an effective patch, L3 on `after_tool_call` logs:

`after_tool_call patch check: NOT EFFECTIVE` with status `missing_field` or `empty_messages`, and skips compression for that turn (`patch_not_effective` may be reported to a backend store).

#### 4. Restart gateway

```bash
openclaw gateway restart
```

## Compression ratios

L3 compares estimated working tokens to the context window:

| Threshold | Formula | Action |
| :--- | :--- | :--- |
| Mild | `floor(window × mildOffloadRatio)` default **0.5** | `compressByScoreCascade` — replace lower-priority tool results with summaries |
| Aggressive | `floor(window × aggressiveCompressRatio)` default **0.85** | `aggressiveCompressUntilBelowThreshold` — delete history, then inject history Mermaid |
| MMD budget | `floor(window × mmdMaxTokenRatio)` default **0.2** | Cap for injected Mermaid token cost |

Tune for long-horizon sessions:

```json
"offload": {
  "enabled": true,
  "mildOffloadRatio": 0.4,
  "aggressiveCompressRatio": 0.8,
  "mmdMaxTokenRatio": 0.15,
  "defaultContextWindow": 200000
}
```

Lower ratios compress earlier; higher ratios keep more raw tool text longer (more tokens, more detail).

## Mermaid injection paths

Confirm these code paths when debugging “canvas never appears in context”:

| Path | When | What |
| :--- | :--- | :--- |
| `injectMmdIntoMessages` | `before_prompt_build` / assemble / `llm_input` | Injects **active** MMD only; marker `_mmdContextMessage: "active"` |
| `maybeUpdateMmdInMessages` | Each `after_tool_call` | Re-reads active MMD file; re-injects if content fingerprint changed |
| `buildHistoryMmdInjection` | After aggressive L3 deletion | Injects **history** MMD as replacement for deleted turns (`_mmdInjection`) |
| `findActiveMmdInsertionPoint` | Active inject | After latest user message (second half), without splitting tool_use / tool_result pairs |
| `findHistoryMmdInsertionPoint` | History inject | Immediately before active MMD, else same heuristic as active |

Injection readiness requires L1.5 settled and `stateManager.isMmdInjectionReady()`. With `waitForL15: true`, inject is skipped until judgment completes.

On-disk canvas files live at:

```text
<dataRoot>/<agentName>/mmds/*.mmd
<dataRoot>/<agentName>/refs/*.md          # full tool results
<dataRoot>/<agentName>/offload-<sessionId>.jsonl
<dataRoot>/<agentName>/state.json
```

Default `dataRoot` is `~/.openclaw/context-offload` (`DEFAULT_DATA_ROOT` in `src/offload/storage.ts`).

## Modes

| `offload.mode` | L1/L1.5/L2 | L3 compression | `registerContextEngine` |
| :--- | :--- | :--- | :--- |
| `local` (default without `backendUrl`) | Local LLM (`offload.model` or host default) | Yes | Yes (slot required) |
| `backend` (default when `backendUrl` set) | Remote `backendUrl` | Yes | Yes |
| `collect` | Yes (async data collection) | **No** | **No** — legacy compaction; remove slot or switch mode if slot is set |

## Verification checklist

<Steps>
  <Step title="Config status">
    Run `bash scripts/setup-offload.sh --status` and confirm:

    - Context Engine Slot: `memory-tencentdb`
    - Offload enabled
    - Mild / aggressive ratios as expected
    - Compaction: `safeguard` if you used the setup script
  </Step>
  <Step title="Patch health">
    Re-run the patch script; expect “已经 patch 过，跳过” / exit 0. After a tool-heavy turn, gateway logs should **not** repeatedly show `patch check: NOT EFFECTIVE`.
  </Step>
  <Step title="Registration logs">
    On gateway start with offload on, look for context-offload diagnostics such as successful `registerContextEngine` and absence of:

    - `Config plugins.slots.contextEngine=... ALL offload functions disabled`
    - `Context engine slot occupied — ALL offload functions disabled`
  </Step>
  <Step title="Data dir artifacts">
    After a multi-tool session, expect under `~/.openclaw/context-offload/<agent>/`:

    - `refs/*.md` for offloaded tool bodies
    - `offload-*.jsonl` entries
    - `mmds/*.mmd` when L2 has produced a canvas
  </Step>
  <Step title="Injection markers">
    In debug logs, active inject reports `mmd-injector: injected active MMD…`; history inject only after aggressive L3. Markers `_mmdContextMessage` / `_mmdInjection` must be skipped by L0 capture (plugin already filters them).
  </Step>
</Steps>

## Disable

```bash
bash scripts/setup-offload.sh --disable
# or set offload.enabled=false and remove plugins.slots.contextEngine
openclaw gateway restart
```

Disable sets `offload.enabled` to false and deletes `plugins.slots.contextEngine` (and empty `plugins.slots`). It does not uninstall the messages patch; that remains until you restore `*.pre-offload-patch.bak` or reinstall OpenClaw.

## Troubleshooting (enable-specific)

| Symptom | Likely cause | Action |
| :--- | :--- | :--- |
| Offload logs “slot not assigned… ALL offload disabled” | Missing/wrong `plugins.slots.contextEngine` | Set to `memory-tencentdb`, restart |
| “slot occupied” / `ok: false` | Another context engine owns the slot | Free the slot or change owner config |
| `patch check: NOT EFFECTIVE` every tool call | Patch missing after OpenClaw upgrade | `bash scripts/openclaw-after-tool-call-messages.patch.sh`; `DEBUG=1` if exit 1 |
| `setup-offload.sh` exits 2 | Patch script failed or missing | Fix OpenClaw path/version; do not leave enable half-applied |
| No `mmds/` / no canvas in context | L1.5 not settled, L2 thresholds not met, or inject not ready | Check `forceTriggerThreshold`, `l2NullThreshold`, `l2TimeoutSeconds`; confirm not `mode: "collect"` |
| Backend L1/L2 silent | `mode` backend/collect without `backendUrl` | Set `backendUrl` (and optional `backendApiKey` / `userId`) |
| Compaction fights offload | Context engine not registered; host compaction active | Ensure slot + non-collect mode; setup script sets `compaction.mode=safeguard` |

For full short-term architecture (L1/L1.5/L2 pipelines, `node_id` recovery), see [Context offload](/context-offload). For the complete config schema, see [Plugin configuration reference](/plugin-config-reference).

## Next

<CardGroup>
  <Card title="Context offload" href="/context-offload">
    Symbolic short-term memory: tool-result refs, L1/L1.5/L2, Mermaid canvas, mild vs aggressive compression, node_id recovery.
  </Card>
  <Card title="Configure OpenClaw" href="/configure-openclaw">
    Daily and advanced plugin tuning (capture, pipeline, recall, persona) and post-restart verification.
  </Card>
  <Card title="Plugin configuration reference" href="/plugin-config-reference">
    Full memory-tencentdb schema including offload field types, defaults, and parseConfig rules.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Source-backed failure modes including offload patch missing and no-recall checks.
  </Card>
  <Card title="Quickstart" href="/quickstart">
    Zero-config OpenClaw enable path and optional offload slot registration.
  </Card>
</CardGroup>
