@martian-engineering/lossless-claw 0.11.2 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -212,6 +212,17 @@ LCM handles crash recovery through **bootstrap reconciliation**:
212
212
 
213
213
  This handles the case where OpenClaw wrote messages to the session file but crashed before LCM could persist them.
214
214
 
215
+ For forked child sessions, LCM treats a host-copied parent JSONL branch as a
216
+ first-time bootstrap source and imports only the newest messages that fit within
217
+ `bootstrapMaxTokens`. That keeps child LCM state bounded even if the host fork
218
+ payload still contains a long raw parent branch. The remaining fork-continuity
219
+ contract belongs to the host: when lossless-claw advertises the
220
+ `subagent-spawn` requirement for `thread-bootstrap-projection`, OpenClaw should
221
+ bootstrap the child model thread from the context-engine projection rather than
222
+ from the raw copied transcript. If the host cannot provide that capability,
223
+ lossless-claw can preserve bounded durable state, but it cannot stop the host
224
+ from replaying raw JSONL into the model before assembly.
225
+
215
226
  ## Operation serialization
216
227
 
217
228
  All mutating operations (ingest, compact) are serialized per-session using a promise queue. This prevents races between concurrent afterTurn/compact calls for the same conversation without blocking operations on different conversations.
@@ -2,6 +2,15 @@
2
2
 
3
3
  Lossless-claw reads plugin configuration from `plugins.entries.lossless-claw.config`.
4
4
 
5
+ Lossless-claw requires OpenClaw `2026.5.22` or newer so the host can enforce
6
+ context-engine runtime capabilities before an agent run starts. Agent runs need
7
+ a native host that provides the full context-engine lifecycle: session bootstrap,
8
+ pre-prompt assembly, after-turn ingestion, maintenance, compaction, and runtime
9
+ LLM completion. Native Codex and Pi embedded runs provide those capabilities;
10
+ generic CLI harnesses such as `claude-cli` and `codex-cli` do not. If you must
11
+ use a generic CLI harness, set `plugins.slots.contextEngine` to `legacy` for
12
+ that run instead of `lossless-claw`.
13
+
5
14
  Configuration precedence is:
6
15
 
7
16
  1. Environment variables
@@ -49,6 +58,9 @@ Most installations only need to override a handful of keys. If you want a comple
49
58
  "expansionModel": "",
50
59
  "delegationTimeoutMs": 120000,
51
60
  "summaryTimeoutMs": 60000,
61
+ "summaryCallWindowMs": 600000,
62
+ "summaryMaxCallsPerWindow": 24,
63
+ "summarySpendBackoffMs": 1800000,
52
64
  "timezone": "America/Los_Angeles",
53
65
  "pruneHeartbeatOk": false,
54
66
  "transcriptGcEnabled": false,
@@ -78,7 +90,13 @@ Most installations only need to override a handful of keys. If you want a comple
78
90
  "dynamicLeafChunkTokens": {
79
91
  "enabled": true,
80
92
  "max": 40000
81
- }
93
+ },
94
+ "stripInjectedContextTags": [
95
+ "active_memory_plugin",
96
+ "relevant-memories",
97
+ "relevant_memories",
98
+ "hindsight_memories"
99
+ ]
82
100
  }
83
101
  ```
84
102
 
@@ -135,11 +153,11 @@ openclaw plugins install --link /path/to/lossless-claw
135
153
  | `autoRotateSessionFiles.createBackups` | `boolean` | `false` | `LCM_AUTO_ROTATE_SESSION_FILES_CREATE_BACKUPS` | Creates or replaces the rolling `rotate-latest` SQLite backup before automatic session-file rotation. Manual `/lcm rotate` backups are always created. |
136
154
  | `autoRotateSessionFiles.sizeBytes` | `integer` | `2097152` | `LCM_AUTO_ROTATE_SESSION_FILES_SIZE_BYTES` | Byte threshold that triggers automatic session-file rotation. |
137
155
  | `autoRotateSessionFiles.startup` | `"rotate" \| "warn" \| "off"` | `"rotate"` | `LCM_AUTO_ROTATE_SESSION_FILES_STARTUP` | Startup behavior for oversized indexed OpenClaw session transcripts that also have active LCM bootstrap state. |
138
- | `autoRotateSessionFiles.runtime` | `"rotate" \| "warn" \| "off"` | `"rotate"` | `LCM_AUTO_ROTATE_SESSION_FILES_RUNTIME` | Runtime behavior after `afterTurn()` and `maintain()` check the current transcript size. |
156
+ | `autoRotateSessionFiles.runtime` | `"rotate" \| "warn" \| "off"` | `"rotate"` | `LCM_AUTO_ROTATE_SESSION_FILES_RUNTIME` | Runtime behavior after post-turn checks. Runtime `rotate` logs deferral for active session JSONL rewrites and leaves direct rotation to startup or manual `/lcm rotate`. |
139
157
 
140
158
  > **Multi-profile note:** `OPENCLAW_STATE_DIR` (set by the host OpenClaw gateway) controls where state is stored. When two gateways run on the same host (e.g. separate bot personas), each gateway sets its own `OPENCLAW_STATE_DIR` and lossless-claw automatically uses that directory for the database, large-file payloads, auth-profile lookups, and legacy secrets — no per-profile plugin config is needed.
141
159
 
142
- Automatic session-file rotation rewrites only the live session transcript, keeps the active LCM conversation and durable history intact, and refreshes the bootstrap checkpoint. Startup rotation first scans OpenClaw's current indexed session stores for configured agents, then intersects those candidates with active LCM conversations and matching bootstrap file mappings. Automatic rotation does not create a SQLite backup by default; set `autoRotateSessionFiles.createBackups` to `true` to make runtime rotation replace the rolling `rotate-latest` backup and to make startup rotation create one pre-rotation LCM database backup for the batch before any transcript is rewritten. Manual `/lcm rotate` always keeps its backup-backed behavior regardless of this flag. Rotation never runs for ignored sessions, stateless sessions, or sessions without active LCM state. The preserved JSONL tail follows the existing rotate behavior, which is controlled by `freshTailCount`.
160
+ Automatic session-file rotation rewrites only the live session transcript, keeps the active LCM conversation and durable history intact, and refreshes the bootstrap checkpoint. Before manual or startup rewrites, rotation forces leaf-only compaction for raw context outside the preserved tail so trimmed transcript messages are covered by LCM summaries without running unrelated summary-condensation passes. Startup rotation first scans OpenClaw's current indexed session stores for configured agents, then intersects those candidates with active LCM conversations and matching bootstrap file mappings. Runtime rotation checks from `afterTurn()` and `maintain()` intentionally do not directly rewrite active session JSONL because embedded prompt-lock fences can still be open while tool-call loops and host background maintenance overlap; runtime `rotate` logs a deferral until startup, manual `/lcm rotate`, or a future host-owned full-transcript rewrite primitive is available. Automatic rotation does not create a SQLite backup by default; set `autoRotateSessionFiles.createBackups` to `true` to make startup rotation create one pre-rotation LCM database backup for the batch before any transcript is rewritten. Manual `/lcm rotate` always keeps its backup-backed behavior regardless of this flag. Rotation never runs for ignored sessions, stateless sessions, or sessions without active LCM state. The preserved JSONL tail follows the existing rotate behavior, which is controlled by `freshTailCount`. Transcript GC uses the host-provided `rewriteTranscriptEntries` primitive and defers until host-approved background maintenance when `transcriptGcEnabled` is enabled.
143
161
 
144
162
  Every automatic decision emits grep-able log lines prefixed with `[lcm] auto-rotate:`. Startup emits one compact summary line with `phase=startup`, `action=summary`, `scanned`, `eligible`, `rotated`, `warned`, `skipped`, `durationMs`, `bytesRemoved`, and backup fields when a batch backup was created; quiet skips such as missing files, missing bootstrap mappings, and below-threshold files are counted there instead of producing one line per candidate. Rotation detail lines include `phase`, `action`, `sessionId`, `sessionKey`, `sessionFile`, `sizeBytes`, `thresholdBytes`, `durationMs`, `backupPath`, `bytesRemoved`, `preservedTailMessageCount`, and `checkpointSize`; real warning lines include the same available context plus `reason` or `error`.
145
163
 
@@ -171,6 +189,13 @@ Every automatic decision emits grep-able log lines prefixed with `[lcm] auto-rot
171
189
  | `maxAssemblyTokenBudget` | `integer` | unset | `LCM_MAX_ASSEMBLY_TOKEN_BUDGET` | Optional hard cap for assembly and threshold evaluation, useful with smaller-context models. |
172
190
  | `maxExpandTokens` | `integer` | `4000` | `LCM_MAX_EXPAND_TOKENS` | Default token cap for `lcm_expand_query` responses. |
173
191
 
192
+ Forked child transcripts are also bounded by `bootstrapMaxTokens` when a host
193
+ copies a raw parent JSONL branch into the child file. This protects the LCM
194
+ database from importing unbounded parent history, but the host must still honor
195
+ the `thread-bootstrap-projection` context-engine capability for subagent or
196
+ thread forks so the model starts from the LCM-assembled compact view instead of
197
+ the raw copied transcript.
198
+
174
199
  ### Model selection, execution, and prompts
175
200
 
176
201
  | Key | Type | Default | Env override | Purpose |
@@ -183,6 +208,9 @@ Every automatic decision emits grep-able log lines prefixed with `[lcm] auto-rot
183
208
  | `expansionProvider` | `string` | `""` | `LCM_EXPANSION_PROVIDER` | `lcm_expand_query` sub-agent provider hint for bare model names. |
184
209
  | `delegationTimeoutMs` | `integer` | `120000` | `LCM_DELEGATION_TIMEOUT_MS` | Maximum time to wait for delegated expansion work. `lcm_expand_query` advertises a dynamic tool `timeoutMs` default with 30 seconds of extra RPC headroom so OpenClaw's tool watchdog does not fire before this wait completes. |
185
210
  | `summaryTimeoutMs` | `integer` | `60000` | `LCM_SUMMARY_TIMEOUT_MS` | Maximum time to wait for one model-backed summarizer call. |
211
+ | `summaryCallWindowMs` | `integer` | `600000` | `LCM_SUMMARY_CALL_WINDOW_MS` | Rolling window for the per-session summarization spend guard. |
212
+ | `summaryMaxCallsPerWindow` | `integer` | `24` | `LCM_SUMMARY_MAX_CALLS_PER_WINDOW` | Maximum model-backed summarization calls per session/window before Lossless opens a non-auth spend backoff. |
213
+ | `summarySpendBackoffMs` | `integer` | `1800000` | `LCM_SUMMARY_SPEND_BACKOFF_MS` | Cooldown after the summarization spend guard opens. |
186
214
  | `customInstructions` | `string` | `""` | `LCM_CUSTOM_INSTRUCTIONS` | Extra natural-language instructions injected into every summarization prompt. |
187
215
 
188
216
  Summary calls are executed through OpenClaw's `api.runtime.llm.complete` capability. If you configure an explicit Lossless summary model (`summaryModel`, `largeFileSummaryModel`, or `fallbackProviders`), OpenClaw must allow that runtime LLM override under `plugins.entries.lossless-claw.llm.allowModelOverride` and `plugins.entries.lossless-claw.llm.allowedModels`. `openclaw doctor --fix` can add the minimal policy entries for configured Lossless summary models. Delegated expansion calls use OpenClaw's runtime sub-agent layer; explicit `expansionModel` values require `plugins.entries.lossless-claw.subagent.allowModelOverride` and a matching `subagent.allowedModels` entry, or `"*"` if you intentionally trust any expansion target. `openclaw doctor --fix` can add the minimal subagent policy, and `lcm_expand_query` retries once without the override if the host rejects it.
@@ -194,6 +222,7 @@ Summary calls are executed through OpenClaw's `api.runtime.llm.complete` capabil
194
222
  | `fallbackProviders` | `Array<{ provider: string; model: string }>` | `[]` | `LCM_FALLBACK_PROVIDERS` | Explicit provider/model fallback chain for compaction summarization. Format for env vars is `provider/model,provider/model`. |
195
223
  | `circuitBreakerThreshold` | `integer` | `5` | `LCM_CIRCUIT_BREAKER_THRESHOLD` | Consecutive auth failures before the summarization circuit breaker trips. |
196
224
  | `circuitBreakerCooldownMs` | `integer` | `1800000` | `LCM_CIRCUIT_BREAKER_COOLDOWN_MS` | Cooldown before the summarization circuit breaker resets automatically. |
225
+ | `stripInjectedContextTags` | `string[]` | `["active_memory_plugin", "relevant-memories", "relevant_memories", "hindsight_memories"]` | `LCM_STRIP_INJECTED_CONTEXT_TAGS` | XML tag names whose blocks are stripped from message content before compaction summarization. Memory/context plugins inject these via `prependContext`; stripping prevents ephemeral retrieval context from polluting compacted summaries. Env var format is comma-separated tag names. Set to `[]` (or empty env string) to disable. |
197
226
 
198
227
  ### Nested objects
199
228
 
@@ -223,7 +252,8 @@ Automatic compaction is threshold-only:
223
252
  - `afterTurn()` evaluates `contextThreshold` against the active token budget
224
253
  - below threshold, no automatic compaction runs and no leaf debt is recorded
225
254
  - at or above threshold, inline mode runs a threshold full sweep immediately
226
- - deferred mode records one coalesced `"threshold"` maintenance row and drains it in the background, `maintain()`, or pre-assembly
255
+ - deferred mode records one coalesced `"threshold"` maintenance row and normally drains it in the background or host-approved `maintain()`
256
+ - pre-assembly drain is reserved as an emergency safeguard when the live prompt is already over the active token budget
227
257
 
228
258
  Lossless still records prompt-cache telemetry for status and diagnostics, but cache hotness no longer delays threshold debt. Legacy `cacheAwareCompaction.*` and `dynamicLeafChunkTokens.*` settings remain accepted so existing OpenClaw config continues to load, but they do not change automatic compaction behavior.
229
259
 
@@ -278,6 +308,8 @@ LCM_EXPANSION_MODEL=openai/gpt-5.4-mini
278
308
  - `*` matches any characters except `:`
279
309
  - `**` matches anything, including `:`
280
310
 
311
+ Cron scheduler keys (`agent:<agent>:cron:<job>...`) are isolated automatically when a new runtime `sessionId` reuses the same `sessionKey`. Configure `ignoreSessionPatterns` for cron only when the run should bypass LCM entirely; leave cron sessions included when they need in-run compaction.
312
+
281
313
  Example:
282
314
 
283
315
  ```json
@@ -309,7 +341,8 @@ Lossless-claw now defaults `proactiveThresholdCompactionMode` to `deferred`.
309
341
  - deferred mode records a single coalesced maintenance debt row per conversation
310
342
  - new deferred compaction debt is only created for `contextThreshold` pressure and uses reason `"threshold"`
311
343
  - `maintain()` consumes threshold debt when the host explicitly opts in to deferred execution
312
- - `assemble()` consumes pending threshold debt before building the next prompt
344
+ - `assemble()` leaves pending threshold debt for after-turn background drain or host-approved `maintain()` while the live prompt is still within budget
345
+ - `assemble()` only consumes pending threshold debt synchronously as an emergency safeguard when the live prompt estimate is already over the active token budget
313
346
  - old non-threshold debt from earlier builds is revalidated; if the conversation is no longer over threshold, it is cleared as a no-op
314
347
  - `/lcm status` / `/lossless status` shows the current maintenance state, including pending/running/last-failure details
315
348
  - status output also surfaces the latest API/cache telemetry as diagnostics, not as a deferral gate
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "id": "lossless-claw",
3
+ "name": "Lossless Context Management",
3
4
  "kind": "context-engine",
4
5
  "activation": {
5
6
  "onStartup": true
@@ -168,6 +169,18 @@
168
169
  "label": "Summary Timeout (ms)",
169
170
  "help": "Maximum time to wait for a single model-backed LCM summarizer call before timing out"
170
171
  },
172
+ "summaryCallWindowMs": {
173
+ "label": "Summary Call Window (ms)",
174
+ "help": "Rolling window used by the summarization spend guard"
175
+ },
176
+ "summaryMaxCallsPerWindow": {
177
+ "label": "Summary Max Calls Per Window",
178
+ "help": "Maximum model-backed summarization calls per session/window before Lossless opens a non-auth spend backoff"
179
+ },
180
+ "summarySpendBackoffMs": {
181
+ "label": "Summary Spend Backoff (ms)",
182
+ "help": "Cooldown after the summarization spend guard opens"
183
+ },
171
184
  "maxAssemblyTokenBudget": {
172
185
  "label": "Max Assembly Token Budget",
173
186
  "help": "Hard ceiling for assembly token budget — caps runtime-provided and fallback budgets. Set for smaller context-window models (e.g., 30000 for 32k models)"
@@ -263,6 +276,10 @@
263
276
  "fallbackProviders": {
264
277
  "label": "Fallback Providers",
265
278
  "help": "Explicit runtime LLM fallback provider/model pairs for compaction summarization; entries require plugins.entries.lossless-claw.llm policy"
279
+ },
280
+ "stripInjectedContextTags": {
281
+ "label": "Strip Injected Context Tags",
282
+ "help": "XML tag names whose blocks are stripped from messages before summarization. Covers memory/context plugin prepended blocks (active-memory, memory-lancedb, hindsight-openclaw). Set to [] to disable."
266
283
  }
267
284
  },
268
285
  "configSchema": {
@@ -406,6 +423,18 @@
406
423
  "type": "integer",
407
424
  "minimum": 1
408
425
  },
426
+ "summaryCallWindowMs": {
427
+ "type": "integer",
428
+ "minimum": 1
429
+ },
430
+ "summaryMaxCallsPerWindow": {
431
+ "type": "integer",
432
+ "minimum": 1
433
+ },
434
+ "summarySpendBackoffMs": {
435
+ "type": "integer",
436
+ "minimum": 1
437
+ },
409
438
  "maxAssemblyTokenBudget": {
410
439
  "type": "integer",
411
440
  "minimum": 1000
@@ -540,6 +569,13 @@
540
569
  "required": ["provider", "model"],
541
570
  "additionalProperties": false
542
571
  }
572
+ },
573
+ "stripInjectedContextTags": {
574
+ "description": "XML tag names whose blocks are stripped from messages before summarization. Memory/context plugins prepend tagged blocks via prependContext that should not leak into compacted summaries.",
575
+ "type": "array",
576
+ "items": {
577
+ "type": "string"
578
+ }
543
579
  }
544
580
  }
545
581
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@martian-engineering/lossless-claw",
3
- "version": "0.11.2",
3
+ "version": "0.12.0",
4
4
  "description": "Lossless Context Management plugin for OpenClaw — DAG-based conversation summarization with threshold compaction",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -18,8 +18,10 @@
18
18
  "scripts": {
19
19
  "build": "esbuild index.ts --bundle --platform=node --target=node22 --format=esm --outfile=dist/index.js --external:openclaw --external:\"@earendil-works/*\" --minify-whitespace",
20
20
  "changeset": "changeset",
21
- "release:verify": "npm run build && npm test && npm pack --dry-run",
21
+ "plugin-inspector:ci": "npm exec --yes --package @openclaw/plugin-inspector@0.3.11 -- plugin-inspector ci --plugin-root . --out plugin-inspector-reports",
22
+ "release:verify": "npm run typecheck && npm run build && npm test && npm pack --dry-run",
22
23
  "test": "vitest run --dir test",
24
+ "typecheck": "tsc --noEmit --pretty false",
23
25
  "version-packages": "changeset version"
24
26
  },
25
27
  "files": [
@@ -46,7 +48,7 @@
46
48
  "vitest": "^3.0.0"
47
49
  },
48
50
  "peerDependencies": {
49
- "openclaw": ">=2026.5.12"
51
+ "openclaw": ">=2026.5.22"
50
52
  },
51
53
  "peerDependenciesMeta": {
52
54
  "openclaw": {
@@ -61,14 +63,14 @@
61
63
  "./dist/index.js"
62
64
  ],
63
65
  "compat": {
64
- "pluginApi": ">=2026.5.12",
65
- "minGatewayVersion": "2026.5.12",
66
+ "pluginApi": ">=2026.5.22",
67
+ "minGatewayVersion": "2026.5.22",
66
68
  "tested": [
67
- "2026.5.12"
69
+ "2026.5.22"
68
70
  ]
69
71
  },
70
72
  "build": {
71
- "openclawVersion": "2026.5.12"
73
+ "openclawVersion": "2026.5.22"
72
74
  }
73
75
  },
74
76
  "repository": {
@@ -296,7 +296,7 @@ Why it matters:
296
296
  - `deferred` also stores provider/model/cache telemetry so Anthropic-family sessions can avoid rewriting a still-hot prompt cache
297
297
  - `inline` preserves the legacy foreground compaction path for hosts that do not yet support deferred execution
298
298
  - `/lossless status` and `/lcm status` surface pending/running/last-failure maintenance state so operators can see when compaction is queued
299
- - background `maintain()` can still do non-prompt-mutating work, but prompt-mutating debt is consumed pre-assembly once cache is cold or the next turn is already approaching overflow
299
+ - after-turn background drain and host-approved `maintain()` consume routine threshold debt; `assemble()` only drains pending threshold debt synchronously as an emergency safeguard when the live prompt estimate is already over budget
300
300
 
301
301
  ### `autoRotateSessionFiles`
302
302
 
@@ -314,6 +314,7 @@ Why it matters:
314
314
 
315
315
  - prevents very large OpenClaw session JSONL files from choking fallback/gateway startup while LCM owns the durable context
316
316
  - runtime rotation only creates or replaces the rolling `rotate-latest` DB backup when `createBackups` is `true`; manual `/lossless rotate` / `/lcm rotate` always keeps its backup-backed behavior
317
+ - runtime JSONL rewrites run from `afterTurn()` after the host turn completes; `maintain()` skips rotation and leaves it to `afterTurn()` or startup because background maintenance can overlap an embedded model call
317
318
  - startup scans OpenClaw's current indexed session stores for configured agents, intersects those candidates with active LCM bootstrap state, and creates one pre-rotation DB backup for the startup batch only when `createBackups` is `true`
318
319
  - only runs for active, writable LCM conversations; ignored sessions, stateless sessions, sessions outside the indexed startup candidate set, and sessions without active LCM state are skipped
319
320
  - the preserved transcript tail follows the normal rotate behavior controlled by `freshTailCount`
@@ -468,6 +469,7 @@ Why it matters:
468
469
 
469
470
  - keeps low-value automation or noisy sessions out of the DB
470
471
  - useful for excluding certain agent lanes or ephemeral traffic entirely
472
+ - cron scheduler keys are already isolated per runtime run, so ignore them only when they should bypass LCM compaction
471
473
 
472
474
  ### `statelessSessionPatterns`
473
475
 
@@ -622,6 +624,28 @@ Why it matters:
622
624
  - guards against runaway summaries that are much larger than their target budget
623
625
  - useful when summary models are verbose or unstable
624
626
 
627
+ ### `summaryMaxCallsPerWindow`, `summaryCallWindowMs`, and `summarySpendBackoffMs`
628
+
629
+ Bounds model-backed compaction and large-file summarization calls per session.
630
+
631
+ Defaults:
632
+
633
+ - `summaryMaxCallsPerWindow`: `24`
634
+ - `summaryCallWindowMs`: `600000`
635
+ - `summarySpendBackoffMs`: `1800000`
636
+
637
+ Env overrides:
638
+
639
+ - `LCM_SUMMARY_MAX_CALLS_PER_WINDOW`
640
+ - `LCM_SUMMARY_CALL_WINDOW_MS`
641
+ - `LCM_SUMMARY_SPEND_BACKOFF_MS`
642
+
643
+ Why they matter:
644
+
645
+ - prevents non-auth provider failures, ineffective compaction, or repeated deferred debt from spending unbounded summarization calls
646
+ - keeps provider-auth failures on the separate auth circuit breaker path
647
+ - direct deterministic fallbacks remain available when model-backed large-file summaries are throttled
648
+
625
649
  ### `customInstructions`
626
650
 
627
651
  Natural-language instructions injected into summarization prompts.
@@ -631,6 +655,25 @@ Why it matters:
631
655
  - lets operators steer formatting or emphasis without patching code
632
656
  - should be used sparingly; low-quality instructions can degrade summary quality system-wide
633
657
 
658
+ ### `stripInjectedContextTags`
659
+
660
+ | | |
661
+ | --- | --- |
662
+ | Type | `string[]` |
663
+ | Default | `["active_memory_plugin", "relevant-memories", "relevant_memories", "hindsight_memories"]` |
664
+ | Env | `LCM_STRIP_INJECTED_CONTEXT_TAGS` (comma-separated) |
665
+
666
+ XML tag names whose blocks are stripped from message content before compaction summarization.
667
+
668
+ Why it matters:
669
+
670
+ - Memory and context plugins (active-memory, memory-lancedb, hindsight-openclaw) prepend XML-tagged blocks to user messages via the `prependContext` hook. These blocks are ephemeral retrieval context — they helped the model on that specific turn but are not part of the actual conversation.
671
+ - Without stripping, the summarizer treats injected memories as real conversation content, permanently corrupting compacted summaries with auto-retrieved context that the user never said.
672
+ - The default list covers well-known OpenClaw memory plugin tags. Add custom tag names if you use plugins that inject context via other tags.
673
+ - Set to `[]` (or empty env string) to disable stripping.
674
+
675
+ Design note: stripping happens at compaction time, not at message ingestion. The raw message stored in the LCM database still contains the original injected blocks, so `lcm_expand` and `lcm_grep` can still surface the full context the model saw on any given turn. Only the summarizer input is cleaned.
676
+
634
677
  ## Practical operator workflow
635
678
 
636
679
  1. Install and enable the plugin.
@@ -8,25 +8,28 @@ For stock `lossless-claw` on current main:
8
8
 
9
9
  - OpenClaw handles `/new` and `/reset` as session-reset operations.
10
10
  - `lossless-claw` handles `/lossless rotate` (`/lcm rotate`) as transcript maintenance on the current conversation.
11
- - `lossless-claw` does **not** currently register its own `before_reset` hook or a custom reset policy.
12
11
  - `lossless-claw` prefers **`sessionKey`** as the stable identity for an LCM conversation.
13
- - When the same `sessionKey` reappears with a new `sessionId`, `lossless-claw` updates the stored `sessionId` on the existing LCM conversation row instead of creating a brand-new LCM conversation.
12
+ - `/reset` archives the active conversation and creates a fresh active row for the same stable `sessionKey`.
13
+ - Cron scheduler keys (`agent:<agent>:cron:<job>...`) are isolated per runtime run when a new `sessionId` reuses the same `sessionKey`.
14
+ - For ordinary non-cron session keys, continuity still follows the stable `sessionKey`.
14
15
 
15
16
  ## What that means in practice
16
17
 
17
- If a user asks whether `/new` or `/reset` gives them a fresh LCM conversation, the answer is usually **no** under the current implementation.
18
+ If a user asks whether `/new` or `/reset` gives them a fresh LCM conversation, distinguish the commands.
18
19
 
19
- They get a fresh OpenClaw session runtime, but LCM continuity still follows the stable `sessionKey` when one is available.
20
+ They get a fresh OpenClaw session runtime, but LCM continuity usually still follows the stable `sessionKey` when one is available.
20
21
 
21
22
  So today:
22
23
 
23
- - `/new` and `/reset` can reset the runtime session
24
- - but LCM history may continue in the same conversation row if the chat/thread keeps the same `sessionKey`
24
+ - `/new` prunes active context but keeps the same LCM conversation row
25
+ - `/reset` archives the active LCM conversation row and creates a fresh active row
26
+ - ordinary chat/thread LCM history may continue in the same row across runtime `sessionId` changes when the stable `sessionKey` continues
27
+ - cron scheduler keys create fresh LCM rows per runtime run so prior runs do not enter the new run's assembled context
25
28
  - `/lossless rotate` keeps that same conversation row, summaries, and context items in place while compacting only the live transcript backing
26
29
 
27
30
  ## Why
28
31
 
29
- Current lossless-claw conversation resolution does this:
32
+ Current lossless-claw conversation resolution generally does this:
30
33
 
31
34
  1. look up by `sessionKey` first
32
35
  2. fall back to `sessionId` only when no `sessionKey` match exists
@@ -34,6 +37,8 @@ Current lossless-claw conversation resolution does this:
34
37
 
35
38
  That behavior preserves continuity across session resets for the same chat identity.
36
39
 
40
+ Cron keys are the exception: when an active cron conversation exists for the same `sessionKey` but a different runtime `sessionId`, lossless-claw archives the prior active row and starts a fresh one for the new run. Prior messages remain persisted on the archived conversation.
41
+
37
42
  ## `/lossless rotate`
38
43
 
39
44
  `/lossless rotate` is distinct from `/new` and `/reset`.
@@ -49,22 +54,23 @@ This makes rotate the lightweight option when the problem is transcript bloat ra
49
54
 
50
55
  ## Important limitation
51
56
 
52
- There is still **no plugin-specific `/new` vs `/reset` split** in stock lossless-claw docs or runtime behavior.
57
+ There is a plugin-specific `/new` vs `/reset` split in current lossless-claw behavior.
53
58
 
54
59
  If someone is asking for semantics like:
55
60
 
56
61
  - `/new` gives them a fresh LCM conversation row
57
- - `/reset` archives old LCM conversation and starts a new one for the same stable `sessionKey`
58
62
 
59
- that is a **design/spec topic**, not current stock behavior.
63
+ that remains a **design/spec topic**, not current stock behavior.
60
64
 
61
65
  ## Safe operator guidance
62
66
 
63
67
  When answering users:
64
68
 
65
- - do not promise that `/new` or `/reset` clears LCM history
69
+ - do not promise that `/new` clears LCM history
70
+ - explain that `/reset` archives the active LCM row and starts a fresh one for the same stable `sessionKey`
66
71
  - explain that `/lossless rotate` compacts the current transcript without splitting the LCM conversation
67
- - explain that current stock behavior follows `sessionKey` continuity
72
+ - explain that ordinary current stock behavior follows `sessionKey` continuity
73
+ - explain that cron scheduler session keys are isolated per runtime run while preserving archived prior runs
68
74
  - if they need a truly separate LCM history, use a different session key context (for example a different chat/thread/binding) or explicit non-MVP migration/surgery tools
69
75
 
70
76
  ## Relation to `/status`