@martian-engineering/lossless-claw 0.11.3 → 0.13.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.
@@ -30,6 +30,23 @@ Most installations only need to override a handful of keys. If you want a comple
30
30
  "statelessSessionPatterns": [],
31
31
  "skipStatelessSessions": true,
32
32
  "contextThreshold": 0.75,
33
+ "contextThresholdOverrides": [
34
+ {
35
+ "name": "large-context-models",
36
+ "match": { "modelContextWindowMin": 900000 },
37
+ "contextThreshold": 0.15
38
+ },
39
+ {
40
+ "name": "small-context-models",
41
+ "match": { "modelContextWindowMax": 250000 },
42
+ "contextThreshold": 0.2
43
+ },
44
+ {
45
+ "name": "telegram-sessions",
46
+ "match": { "sessionPattern": "agent:*:telegram:**" },
47
+ "contextThreshold": 0.3
48
+ }
49
+ ],
33
50
  "freshTailCount": 64,
34
51
  "freshTailMaxTokens": 24000,
35
52
  "promptAwareEviction": false,
@@ -58,14 +75,20 @@ Most installations only need to override a handful of keys. If you want a comple
58
75
  "expansionModel": "",
59
76
  "delegationTimeoutMs": 120000,
60
77
  "summaryTimeoutMs": 60000,
78
+ "summaryCallWindowMs": 600000,
79
+ "summaryMaxCallsPerWindow": 24,
80
+ "summarySpendBackoffMs": 1800000,
61
81
  "timezone": "America/Los_Angeles",
62
82
  "pruneHeartbeatOk": false,
63
83
  "transcriptGcEnabled": false,
84
+ "enableSummaryThinking": true,
64
85
  "maxAssemblyTokenBudget": 30000,
65
86
  "summaryMaxOverageFactor": 3,
66
87
  "customInstructions": "",
67
88
  "circuitBreakerThreshold": 5,
68
89
  "circuitBreakerCooldownMs": 1800000,
90
+ "replayFloodThresholdExternal": 3,
91
+ "replayFloodThresholdInternal": 32,
69
92
  "fallbackProviders": [],
70
93
  "proactiveThresholdCompactionMode": "deferred",
71
94
  "autoRotateSessionFiles": {
@@ -75,6 +98,11 @@ Most installations only need to override a handful of keys. If you want a comple
75
98
  "startup": "rotate",
76
99
  "runtime": "rotate"
77
100
  },
101
+ "independentLogFile": {
102
+ "enabled": true,
103
+ "file": "/tmp/openclaw/lossless-claw-2026-05-19.log",
104
+ "maxFileBytes": 104857600
105
+ },
78
106
  "cacheAwareCompaction": {
79
107
  "enabled": true,
80
108
  "cacheTTLSeconds": 300,
@@ -87,7 +115,13 @@ Most installations only need to override a handful of keys. If you want a comple
87
115
  "dynamicLeafChunkTokens": {
88
116
  "enabled": true,
89
117
  "max": 40000
90
- }
118
+ },
119
+ "stripInjectedContextTags": [
120
+ "active_memory_plugin",
121
+ "relevant-memories",
122
+ "relevant_memories",
123
+ "hindsight_memories"
124
+ ]
91
125
  }
92
126
  ```
93
127
 
@@ -139,16 +173,22 @@ openclaw plugins install --link /path/to/lossless-claw
139
173
  | `timezone` | `string` | `TZ` or system timezone | `TZ` | IANA timezone used for timestamp rendering in summaries. |
140
174
  | `pruneHeartbeatOk` | `boolean` | `false` | `LCM_PRUNE_HEARTBEAT_OK` | Retroactively removes `HEARTBEAT_OK` turn cycles from persisted storage. |
141
175
  | `transcriptGcEnabled` | `boolean` | `false` | `LCM_TRANSCRIPT_GC_ENABLED` | Enables transcript rewrite GC during `maintain()`; disabled by default so transcript rewrites stay opt-in. |
176
+ | `enableSummaryThinking` | `boolean` | `true` | `LCM_ENABLE_SUMMARY_THINKING` | When true, requests low reasoning budget from the model during summarization calls. Set to false to disable reasoning and keep summarization output concise. |
142
177
  | `proactiveThresholdCompactionMode` | `"deferred" \| "inline"` | `"deferred"` | `LCM_PROACTIVE_THRESHOLD_COMPACTION_MODE` | Controls whether proactive threshold compaction is deferred into maintenance debt by default or run inline for legacy behavior. |
143
178
  | `autoRotateSessionFiles.enabled` | `boolean` | `true` | `LCM_AUTO_ROTATE_SESSION_FILES_ENABLED` | Enables automatic rotation for oversized LCM-managed session JSONL files. |
144
179
  | `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. |
145
180
  | `autoRotateSessionFiles.sizeBytes` | `integer` | `2097152` | `LCM_AUTO_ROTATE_SESSION_FILES_SIZE_BYTES` | Byte threshold that triggers automatic session-file rotation. |
146
181
  | `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. |
147
- | `autoRotateSessionFiles.runtime` | `"rotate" \| "warn" \| "off"` | `"rotate"` | `LCM_AUTO_ROTATE_SESSION_FILES_RUNTIME` | Runtime behavior after `afterTurn()` and `maintain()` check the current transcript size. |
182
+ | `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`. |
183
+ | `independentLogFile.enabled` | `boolean` | `true` | `LCM_LOG_FILE_ENABLED` | Writes lossless-claw JSONL logs to an independent plugin-owned file in addition to OpenClaw's runtime logger. |
184
+ | `independentLogFile.file` | `string` | `/tmp/openclaw/lossless-claw-YYYY-MM-DD.log` | `LCM_LOG_FILE` | Optional log path. A dated `lossless-claw-YYYY-MM-DD.log` path rolls over daily. |
185
+ | `independentLogFile.maxFileBytes` | `integer` | `104857600` | `LCM_LOG_MAX_FILE_BYTES` | Size threshold for rotating the active lossless-claw log file to `.1.log` through `.5.log`. |
148
186
 
149
187
  > **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.
150
188
 
151
- 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`.
189
+ 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.
190
+
191
+ Lossless-claw writes routine operational JSONL logs by default at `/tmp/openclaw/lossless-claw-YYYY-MM-DD.log`, beside OpenClaw's `/tmp/openclaw/openclaw-YYYY-MM-DD.log`. Routine info and debug lines go to the independent file instead of the shared OpenClaw log. Startup banners and warning/error lines still go through OpenClaw's runtime logger so gateway-level startup and failure diagnostics remain visible. The independent file follows the same practical rotation model as OpenClaw: a dated filename rolls over when the local date changes, stale dated files are pruned after 3 days, and an oversized active file is rotated through `.1.log` to `.5.log`.
152
192
 
153
193
  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`.
154
194
 
@@ -157,6 +197,7 @@ Every automatic decision emits grep-able log lines prefixed with `[lcm] auto-rot
157
197
  | Key | Type | Default | Env override | Purpose |
158
198
  | --- | --- | --- | --- | --- |
159
199
  | `contextThreshold` | `number` | `0.75` | `LCM_CONTEXT_THRESHOLD` | Fraction of the active model context window that triggers compaction. |
200
+ | `contextThresholdOverrides` | `Array<{ name?: string; match: object; contextThreshold: number }>` | `[]` | none | Optional ordered rules that override `contextThreshold` by model id, model context-window range, or session glob pattern. |
160
201
  | `freshTailCount` | `integer` | `64` | `LCM_FRESH_TAIL_COUNT` | Number of newest messages always kept raw. |
161
202
  | `freshTailMaxTokens` | `integer` | unset | `LCM_FRESH_TAIL_MAX_TOKENS` | Optional token cap for the protected fresh tail. The newest message is always preserved even if it exceeds the cap. |
162
203
  | `promptAwareEviction` | `boolean` | `false` | `LCM_PROMPT_AWARE_EVICTION_ENABLED` | When enabled, budget-constrained assembly keeps older evictable items by prompt relevance instead of pure chronology. This improves retrieval under tight budgets, but it can reduce prompt-cache hit rates because the preserved prefix changes as prompts change. |
@@ -180,6 +221,13 @@ Every automatic decision emits grep-able log lines prefixed with `[lcm] auto-rot
180
221
  | `maxAssemblyTokenBudget` | `integer` | unset | `LCM_MAX_ASSEMBLY_TOKEN_BUDGET` | Optional hard cap for assembly and threshold evaluation, useful with smaller-context models. |
181
222
  | `maxExpandTokens` | `integer` | `4000` | `LCM_MAX_EXPAND_TOKENS` | Default token cap for `lcm_expand_query` responses. |
182
223
 
224
+ Forked child transcripts are also bounded by `bootstrapMaxTokens` when a host
225
+ copies a raw parent JSONL branch into the child file. This protects the LCM
226
+ database from importing unbounded parent history, but the host must still honor
227
+ the `thread-bootstrap-projection` context-engine capability for subagent or
228
+ thread forks so the model starts from the LCM-assembled compact view instead of
229
+ the raw copied transcript.
230
+
183
231
  ### Model selection, execution, and prompts
184
232
 
185
233
  | Key | Type | Default | Env override | Purpose |
@@ -192,6 +240,9 @@ Every automatic decision emits grep-able log lines prefixed with `[lcm] auto-rot
192
240
  | `expansionProvider` | `string` | `""` | `LCM_EXPANSION_PROVIDER` | `lcm_expand_query` sub-agent provider hint for bare model names. |
193
241
  | `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. |
194
242
  | `summaryTimeoutMs` | `integer` | `60000` | `LCM_SUMMARY_TIMEOUT_MS` | Maximum time to wait for one model-backed summarizer call. |
243
+ | `summaryCallWindowMs` | `integer` | `600000` | `LCM_SUMMARY_CALL_WINDOW_MS` | Rolling window for the per-session summarization spend guard. |
244
+ | `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. |
245
+ | `summarySpendBackoffMs` | `integer` | `1800000` | `LCM_SUMMARY_SPEND_BACKOFF_MS` | Cooldown after the summarization spend guard opens. |
195
246
  | `customInstructions` | `string` | `""` | `LCM_CUSTOM_INSTRUCTIONS` | Extra natural-language instructions injected into every summarization prompt. |
196
247
 
197
248
  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.
@@ -203,6 +254,9 @@ Summary calls are executed through OpenClaw's `api.runtime.llm.complete` capabil
203
254
  | `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`. |
204
255
  | `circuitBreakerThreshold` | `integer` | `5` | `LCM_CIRCUIT_BREAKER_THRESHOLD` | Consecutive auth failures before the summarization circuit breaker trips. |
205
256
  | `circuitBreakerCooldownMs` | `integer` | `1800000` | `LCM_CIRCUIT_BREAKER_COOLDOWN_MS` | Cooldown before the summarization circuit breaker resets automatically. |
257
+ | `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. |
258
+ | `replayFloodThresholdExternal` | `integer` | `3` | `LCM_REPLAY_FLOOD_THRESHOLD_EXTERNAL` | Max replay-like messages allowed in a single SQLite-second for `role=user` before `assertNoReplayTimestampFlood` refuses the batch. Defaults to `3` to preserve replay defense for third-partyly-rebroadcastable input. |
259
+ | `replayFloodThresholdInternal` | `integer` | `32` | `LCM_REPLAY_FLOOD_THRESHOLD_INTERNAL` | Max identical messages allowed in a single SQLite-second for `role=tool/assistant/system` before the anti-replay guard refuses the batch. Defaults to `32` to absorb legitimate idempotent sub-agent bursts (same-second tool returns like `{"status":"ok"}`). |
206
260
 
207
261
  ### Nested objects
208
262
 
@@ -229,13 +283,16 @@ Summary calls are executed through OpenClaw's `api.runtime.llm.complete` capabil
229
283
 
230
284
  Automatic compaction is threshold-only:
231
285
 
232
- - `afterTurn()` evaluates `contextThreshold` against the active token budget
286
+ - `afterTurn()` evaluates the resolved context threshold against the active token budget
233
287
  - below threshold, no automatic compaction runs and no leaf debt is recorded
234
288
  - at or above threshold, inline mode runs a threshold full sweep immediately
235
- - deferred mode records one coalesced `"threshold"` maintenance row and drains it in the background, `maintain()`, or pre-assembly
289
+ - deferred mode records one coalesced `"threshold"` maintenance row and normally drains it in the background or host-approved `maintain()`
290
+ - pre-assembly drain is reserved as an emergency safeguard when the live prompt is already over the active token budget
236
291
 
237
292
  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.
238
293
 
294
+ `contextThresholdOverrides` are optional and never replace the global fallback. Each rule's `match` object can include `model`, `modelContextWindowMin`, `modelContextWindowMax`, and `sessionPattern`; all fields in a rule must match. If several rules match, Lossless picks the highest-specificity rule, then the earliest rule in the array for ties. Exact `model` matches have higher specificity than `sessionPattern` matches, and session-pattern matches have higher specificity than context-window range matches. Threshold selection logs include the chosen threshold, source, rule index/name, token budget, threshold tokens, model, context-window value, and match reason.
295
+
239
296
  Full sweeps first run leaf passes until there are no more eligible raw-message chunks outside the fresh tail. Condensation is then driven by summarized-prefix pressure: the routine condensation phase obeys `sweepMaxDepth`, and if the summarized prefix still exceeds `summaryPrefixTargetTokens`, a pressure phase may use `condensedMinFanoutHard` and condense deeper. Total context pressure starts the sweep, but does not by itself force deeper condensation once the raw prefix has been summarized.
240
297
 
241
298
  A single sweep is bounded by both `maxSweepIterations` (a hard cap on summarizer passes) and `sweepDeadlineMs` (a wall-clock budget). When either limit is reached the sweep stops before starting another pass and returns the consistent partial result built so far, logging a `compactFullSweep stopped at …` warning. This keeps a slow or rate-limited summarizer from hanging the agent turn — remaining context pressure is picked up by the next sweep.
@@ -287,6 +344,8 @@ LCM_EXPANSION_MODEL=openai/gpt-5.4-mini
287
344
  - `*` matches any characters except `:`
288
345
  - `**` matches anything, including `:`
289
346
 
347
+ 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.
348
+
290
349
  Example:
291
350
 
292
351
  ```json
@@ -318,7 +377,8 @@ Lossless-claw now defaults `proactiveThresholdCompactionMode` to `deferred`.
318
377
  - deferred mode records a single coalesced maintenance debt row per conversation
319
378
  - new deferred compaction debt is only created for `contextThreshold` pressure and uses reason `"threshold"`
320
379
  - `maintain()` consumes threshold debt when the host explicitly opts in to deferred execution
321
- - `assemble()` consumes pending threshold debt before building the next prompt
380
+ - `assemble()` leaves pending threshold debt for after-turn background drain or host-approved `maintain()` while the live prompt is still within budget
381
+ - `assemble()` only consumes pending threshold debt synchronously as an emergency safeguard when the live prompt estimate is already over the active token budget
322
382
  - old non-threshold debt from earlier builds is revalidated; if the conversation is no longer over threshold, it is cleared as a no-op
323
383
  - `/lcm status` / `/lossless status` shows the current maintenance state, including pending/running/last-failure details
324
384
  - 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
@@ -24,6 +25,10 @@
24
25
  "label": "Context Threshold",
25
26
  "help": "Fraction of context window that triggers compaction (0.0–1.0)"
26
27
  },
28
+ "contextThresholdOverrides": {
29
+ "label": "Context Threshold Overrides",
30
+ "help": "Optional ordered rules that override contextThreshold by model, model context-window range, or session glob pattern"
31
+ },
27
32
  "sweepMaxDepth": {
28
33
  "label": "Sweep Max Depth",
29
34
  "help": "Preferred maximum condensation source depth during routine full sweeps (0 = leaf only, -1 = unlimited). Pressure sweeps may go deeper when summarized context remains above target."
@@ -168,6 +173,18 @@
168
173
  "label": "Summary Timeout (ms)",
169
174
  "help": "Maximum time to wait for a single model-backed LCM summarizer call before timing out"
170
175
  },
176
+ "summaryCallWindowMs": {
177
+ "label": "Summary Call Window (ms)",
178
+ "help": "Rolling window used by the summarization spend guard"
179
+ },
180
+ "summaryMaxCallsPerWindow": {
181
+ "label": "Summary Max Calls Per Window",
182
+ "help": "Maximum model-backed summarization calls per session/window before Lossless opens a non-auth spend backoff"
183
+ },
184
+ "summarySpendBackoffMs": {
185
+ "label": "Summary Spend Backoff (ms)",
186
+ "help": "Cooldown after the summarization spend guard opens"
187
+ },
171
188
  "maxAssemblyTokenBudget": {
172
189
  "label": "Max Assembly Token Budget",
173
190
  "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)"
@@ -188,6 +205,14 @@
188
205
  "label": "Circuit Breaker Cooldown (ms)",
189
206
  "help": "Cooldown before the summarization circuit breaker auto-resets"
190
207
  },
208
+ "replayFloodThresholdExternal": {
209
+ "label": "Replay Flood Threshold (External)",
210
+ "help": "Max replay-like messages allowed in a single SQLite-second for third-party roles (role=user) before the anti-replay guard refuses the batch. Defaults to 3 to preserve replay defense for third-partyly-rebroadcastable input."
211
+ },
212
+ "replayFloodThresholdInternal": {
213
+ "label": "Replay Flood Threshold (Internal)",
214
+ "help": "Max identical messages allowed in a single SQLite-second for internal roles (role=tool/assistant/system) before the anti-replay guard refuses the batch. Defaults to 32 to absorb legitimate idempotent sub-agent bursts."
215
+ },
191
216
  "cacheAwareCompaction.enabled": {
192
217
  "label": "Cache-Aware Compaction (Deprecated)",
193
218
  "help": "Deprecated compatibility setting. Automatic compaction is now threshold-only and does not use prompt-cache hot/cold state."
@@ -236,6 +261,10 @@
236
261
  "label": "Transcript GC",
237
262
  "help": "Enable transcript rewrite GC during maintain(); disabled by default"
238
263
  },
264
+ "enableSummaryThinking": {
265
+ "label": "Enable Summary Thinking",
266
+ "help": "Request low reasoning budget from the model during summarization calls"
267
+ },
239
268
  "proactiveThresholdCompactionMode": {
240
269
  "label": "Proactive Threshold Compaction Mode",
241
270
  "help": "Choose deferred compaction debt by default or keep legacy inline proactive compaction"
@@ -260,9 +289,25 @@
260
289
  "label": "Runtime Auto-Rotate",
261
290
  "help": "Runtime behavior for oversized current LCM session files: rotate, warn, or off"
262
291
  },
292
+ "independentLogFile.enabled": {
293
+ "label": "Independent Log File",
294
+ "help": "Write lossless-claw JSONL logs to a plugin-owned file in addition to OpenClaw's runtime logger"
295
+ },
296
+ "independentLogFile.file": {
297
+ "label": "Independent Log Path",
298
+ "help": "Optional lossless-claw log path; defaults to /tmp/openclaw/lossless-claw-YYYY-MM-DD.log with daily rollover"
299
+ },
300
+ "independentLogFile.maxFileBytes": {
301
+ "label": "Independent Log Max Bytes",
302
+ "help": "Byte threshold for size rotation of the current lossless-claw log file (default: 104857600)"
303
+ },
263
304
  "fallbackProviders": {
264
305
  "label": "Fallback Providers",
265
306
  "help": "Explicit runtime LLM fallback provider/model pairs for compaction summarization; entries require plugins.entries.lossless-claw.llm policy"
307
+ },
308
+ "stripInjectedContextTags": {
309
+ "label": "Strip Injected Context Tags",
310
+ "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
311
  }
267
312
  },
268
313
  "configSchema": {
@@ -277,6 +322,50 @@
277
322
  "minimum": 0,
278
323
  "maximum": 1
279
324
  },
325
+ "contextThresholdOverrides": {
326
+ "type": "array",
327
+ "items": {
328
+ "type": "object",
329
+ "additionalProperties": false,
330
+ "required": [
331
+ "match",
332
+ "contextThreshold"
333
+ ],
334
+ "properties": {
335
+ "name": {
336
+ "type": "string"
337
+ },
338
+ "match": {
339
+ "type": "object",
340
+ "additionalProperties": false,
341
+ "minProperties": 1,
342
+ "properties": {
343
+ "model": {
344
+ "type": "string",
345
+ "minLength": 1
346
+ },
347
+ "modelContextWindowMin": {
348
+ "type": "integer",
349
+ "minimum": 1
350
+ },
351
+ "modelContextWindowMax": {
352
+ "type": "integer",
353
+ "minimum": 1
354
+ },
355
+ "sessionPattern": {
356
+ "type": "string",
357
+ "minLength": 1
358
+ }
359
+ }
360
+ },
361
+ "contextThreshold": {
362
+ "type": "number",
363
+ "minimum": 0,
364
+ "maximum": 1
365
+ }
366
+ }
367
+ }
368
+ },
280
369
  "sweepMaxDepth": {
281
370
  "type": "integer",
282
371
  "minimum": -1
@@ -406,6 +495,18 @@
406
495
  "type": "integer",
407
496
  "minimum": 1
408
497
  },
498
+ "summaryCallWindowMs": {
499
+ "type": "integer",
500
+ "minimum": 1
501
+ },
502
+ "summaryMaxCallsPerWindow": {
503
+ "type": "integer",
504
+ "minimum": 1
505
+ },
506
+ "summarySpendBackoffMs": {
507
+ "type": "integer",
508
+ "minimum": 1
509
+ },
409
510
  "maxAssemblyTokenBudget": {
410
511
  "type": "integer",
411
512
  "minimum": 1000
@@ -425,6 +526,14 @@
425
526
  "type": "integer",
426
527
  "minimum": 1
427
528
  },
529
+ "replayFloodThresholdExternal": {
530
+ "type": "integer",
531
+ "minimum": 1
532
+ },
533
+ "replayFloodThresholdInternal": {
534
+ "type": "integer",
535
+ "minimum": 1
536
+ },
428
537
  "cacheAwareCompaction": {
429
538
  "type": "object",
430
539
  "additionalProperties": false,
@@ -482,6 +591,9 @@
482
591
  "transcriptGcEnabled": {
483
592
  "type": "boolean"
484
593
  },
594
+ "enableSummaryThinking": {
595
+ "type": "boolean"
596
+ },
485
597
  "proactiveThresholdCompactionMode": {
486
598
  "type": "string",
487
599
  "enum": [
@@ -521,6 +633,22 @@
521
633
  }
522
634
  }
523
635
  },
636
+ "independentLogFile": {
637
+ "type": "object",
638
+ "additionalProperties": false,
639
+ "properties": {
640
+ "enabled": {
641
+ "type": "boolean"
642
+ },
643
+ "file": {
644
+ "type": "string"
645
+ },
646
+ "maxFileBytes": {
647
+ "type": "integer",
648
+ "minimum": 1
649
+ }
650
+ }
651
+ },
524
652
  "databasePath": {
525
653
  "description": "Path to LCM SQLite database (preferred key; alias of dbPath, default: <OPENCLAW_STATE_DIR>/lcm.db)",
526
654
  "type": "string"
@@ -540,6 +668,13 @@
540
668
  "required": ["provider", "model"],
541
669
  "additionalProperties": false
542
670
  }
671
+ },
672
+ "stripInjectedContextTags": {
673
+ "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.",
674
+ "type": "array",
675
+ "items": {
676
+ "type": "string"
677
+ }
543
678
  }
544
679
  }
545
680
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@martian-engineering/lossless-claw",
3
- "version": "0.11.3",
3
+ "version": "0.13.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": [
@@ -33,20 +35,20 @@
33
35
  "LICENSE"
34
36
  ],
35
37
  "dependencies": {
36
- "@earendil-works/pi-agent-core": ">=0.74 <1",
37
- "@earendil-works/pi-ai": ">=0.74 <1",
38
- "@earendil-works/pi-coding-agent": ">=0.74 <1",
39
38
  "@sinclair/typebox": "0.34.48"
40
39
  },
41
40
  "devDependencies": {
42
41
  "@changesets/changelog-github": "^0.6.0",
43
42
  "@changesets/cli": "^2.30.0",
43
+ "@earendil-works/pi-agent-core": "^0.79.0",
44
+ "@earendil-works/pi-ai": "^0.79.0",
45
+ "@earendil-works/pi-coding-agent": "^0.79.0",
44
46
  "esbuild": "^0.28.0",
45
47
  "typescript": "^5.7.0",
46
48
  "vitest": "^3.0.0"
47
49
  },
48
50
  "peerDependencies": {
49
- "openclaw": ">=2026.5.22"
51
+ "openclaw": ">=2026.5.28"
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.22",
65
- "minGatewayVersion": "2026.5.22",
66
+ "pluginApi": ">=2026.5.28",
67
+ "minGatewayVersion": "2026.5.28",
66
68
  "tested": [
67
- "2026.5.22"
69
+ "2026.5.28"
68
70
  ]
69
71
  },
70
72
  "build": {
71
- "openclawVersion": "2026.5.22"
73
+ "openclawVersion": "2026.5.28"
72
74
  }
73
75
  },
74
76
  "repository": {
@@ -11,10 +11,11 @@ Start here:
11
11
 
12
12
  1. Confirm whether the user needs configuration help, diagnostics, recall-tool guidance, or session-lifecycle guidance.
13
13
  2. If they need a quick health check, tell them to run `/lossless` (`/lcm` is the shorter alias).
14
- 3. If they suspect summary corruption or truncation, use `/lossless doctor`.
15
- 4. If they want high-confidence junk/session cleanup guidance, use `/lossless doctor clean` before recommending any deletes.
16
- 5. If they ask how `/new`, `/reset`, or `/lossless rotate` interacts with LCM, read the session-lifecycle reference before answering.
17
- 6. Load the relevant reference file instead of improvising details from memory.
14
+ 3. If they are debugging lossless-claw behavior or failures, check the independent Lossless log before the shared OpenClaw gateway log.
15
+ 4. If they suspect summary corruption or truncation, use `/lossless doctor`.
16
+ 5. If they want high-confidence junk/session cleanup guidance, use `/lossless doctor clean` before recommending any deletes.
17
+ 6. If they ask how `/new`, `/reset`, or `/lossless rotate` interacts with LCM, read the session-lifecycle reference before answering.
18
+ 7. Load the relevant reference file instead of improvising details from memory.
18
19
 
19
20
  Reference map:
20
21