@martian-engineering/lossless-claw 0.12.0 → 0.13.1

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.
@@ -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,
@@ -64,11 +81,14 @@ Most installations only need to override a handful of keys. If you want a comple
64
81
  "timezone": "America/Los_Angeles",
65
82
  "pruneHeartbeatOk": false,
66
83
  "transcriptGcEnabled": false,
84
+ "enableSummaryThinking": true,
67
85
  "maxAssemblyTokenBudget": 30000,
68
86
  "summaryMaxOverageFactor": 3,
69
87
  "customInstructions": "",
70
88
  "circuitBreakerThreshold": 5,
71
89
  "circuitBreakerCooldownMs": 1800000,
90
+ "replayFloodThresholdExternal": 3,
91
+ "replayFloodThresholdInternal": 32,
72
92
  "fallbackProviders": [],
73
93
  "proactiveThresholdCompactionMode": "deferred",
74
94
  "autoRotateSessionFiles": {
@@ -78,6 +98,11 @@ Most installations only need to override a handful of keys. If you want a comple
78
98
  "startup": "rotate",
79
99
  "runtime": "rotate"
80
100
  },
101
+ "independentLogFile": {
102
+ "enabled": true,
103
+ "file": "/tmp/openclaw/lossless-claw-2026-05-19.log",
104
+ "maxFileBytes": 104857600
105
+ },
81
106
  "cacheAwareCompaction": {
82
107
  "enabled": true,
83
108
  "cacheTTLSeconds": 300,
@@ -148,17 +173,23 @@ openclaw plugins install --link /path/to/lossless-claw
148
173
  | `timezone` | `string` | `TZ` or system timezone | `TZ` | IANA timezone used for timestamp rendering in summaries. |
149
174
  | `pruneHeartbeatOk` | `boolean` | `false` | `LCM_PRUNE_HEARTBEAT_OK` | Retroactively removes `HEARTBEAT_OK` turn cycles from persisted storage. |
150
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. |
151
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. |
152
178
  | `autoRotateSessionFiles.enabled` | `boolean` | `true` | `LCM_AUTO_ROTATE_SESSION_FILES_ENABLED` | Enables automatic rotation for oversized LCM-managed session JSONL files. |
153
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. |
154
180
  | `autoRotateSessionFiles.sizeBytes` | `integer` | `2097152` | `LCM_AUTO_ROTATE_SESSION_FILES_SIZE_BYTES` | Byte threshold that triggers automatic session-file rotation. |
155
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. |
156
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`. |
157
186
 
158
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.
159
188
 
160
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.
161
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`.
192
+
162
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`.
163
194
 
164
195
  ### Compaction thresholds and summary sizing
@@ -166,6 +197,7 @@ Every automatic decision emits grep-able log lines prefixed with `[lcm] auto-rot
166
197
  | Key | Type | Default | Env override | Purpose |
167
198
  | --- | --- | --- | --- | --- |
168
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. |
169
201
  | `freshTailCount` | `integer` | `64` | `LCM_FRESH_TAIL_COUNT` | Number of newest messages always kept raw. |
170
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. |
171
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. |
@@ -223,6 +255,8 @@ Summary calls are executed through OpenClaw's `api.runtime.llm.complete` capabil
223
255
  | `circuitBreakerThreshold` | `integer` | `5` | `LCM_CIRCUIT_BREAKER_THRESHOLD` | Consecutive auth failures before the summarization circuit breaker trips. |
224
256
  | `circuitBreakerCooldownMs` | `integer` | `1800000` | `LCM_CIRCUIT_BREAKER_COOLDOWN_MS` | Cooldown before the summarization circuit breaker resets automatically. |
225
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"}`). |
226
260
 
227
261
  ### Nested objects
228
262
 
@@ -249,7 +283,7 @@ Summary calls are executed through OpenClaw's `api.runtime.llm.complete` capabil
249
283
 
250
284
  Automatic compaction is threshold-only:
251
285
 
252
- - `afterTurn()` evaluates `contextThreshold` against the active token budget
286
+ - `afterTurn()` evaluates the resolved context threshold against the active token budget
253
287
  - below threshold, no automatic compaction runs and no leaf debt is recorded
254
288
  - at or above threshold, inline mode runs a threshold full sweep immediately
255
289
  - deferred mode records one coalesced `"threshold"` maintenance row and normally drains it in the background or host-approved `maintain()`
@@ -257,6 +291,8 @@ Automatic compaction is threshold-only:
257
291
 
258
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.
259
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
+
260
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.
261
297
 
262
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.
@@ -25,6 +25,10 @@
25
25
  "label": "Context Threshold",
26
26
  "help": "Fraction of context window that triggers compaction (0.0–1.0)"
27
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
+ },
28
32
  "sweepMaxDepth": {
29
33
  "label": "Sweep Max Depth",
30
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."
@@ -201,6 +205,14 @@
201
205
  "label": "Circuit Breaker Cooldown (ms)",
202
206
  "help": "Cooldown before the summarization circuit breaker auto-resets"
203
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
+ },
204
216
  "cacheAwareCompaction.enabled": {
205
217
  "label": "Cache-Aware Compaction (Deprecated)",
206
218
  "help": "Deprecated compatibility setting. Automatic compaction is now threshold-only and does not use prompt-cache hot/cold state."
@@ -249,6 +261,10 @@
249
261
  "label": "Transcript GC",
250
262
  "help": "Enable transcript rewrite GC during maintain(); disabled by default"
251
263
  },
264
+ "enableSummaryThinking": {
265
+ "label": "Enable Summary Thinking",
266
+ "help": "Request low reasoning budget from the model during summarization calls"
267
+ },
252
268
  "proactiveThresholdCompactionMode": {
253
269
  "label": "Proactive Threshold Compaction Mode",
254
270
  "help": "Choose deferred compaction debt by default or keep legacy inline proactive compaction"
@@ -273,6 +289,18 @@
273
289
  "label": "Runtime Auto-Rotate",
274
290
  "help": "Runtime behavior for oversized current LCM session files: rotate, warn, or off"
275
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
+ },
276
304
  "fallbackProviders": {
277
305
  "label": "Fallback Providers",
278
306
  "help": "Explicit runtime LLM fallback provider/model pairs for compaction summarization; entries require plugins.entries.lossless-claw.llm policy"
@@ -294,6 +322,50 @@
294
322
  "minimum": 0,
295
323
  "maximum": 1
296
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
+ },
297
369
  "sweepMaxDepth": {
298
370
  "type": "integer",
299
371
  "minimum": -1
@@ -454,6 +526,14 @@
454
526
  "type": "integer",
455
527
  "minimum": 1
456
528
  },
529
+ "replayFloodThresholdExternal": {
530
+ "type": "integer",
531
+ "minimum": 1
532
+ },
533
+ "replayFloodThresholdInternal": {
534
+ "type": "integer",
535
+ "minimum": 1
536
+ },
457
537
  "cacheAwareCompaction": {
458
538
  "type": "object",
459
539
  "additionalProperties": false,
@@ -511,6 +591,9 @@
511
591
  "transcriptGcEnabled": {
512
592
  "type": "boolean"
513
593
  },
594
+ "enableSummaryThinking": {
595
+ "type": "boolean"
596
+ },
514
597
  "proactiveThresholdCompactionMode": {
515
598
  "type": "string",
516
599
  "enum": [
@@ -550,6 +633,22 @@
550
633
  }
551
634
  }
552
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
+ },
553
652
  "databasePath": {
554
653
  "description": "Path to LCM SQLite database (preferred key; alias of dbPath, default: <OPENCLAW_STATE_DIR>/lcm.db)",
555
654
  "type": "string"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@martian-engineering/lossless-claw",
3
- "version": "0.12.0",
3
+ "version": "0.13.1",
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",
@@ -35,20 +35,20 @@
35
35
  "LICENSE"
36
36
  ],
37
37
  "dependencies": {
38
- "@earendil-works/pi-agent-core": ">=0.74 <1",
39
- "@earendil-works/pi-ai": ">=0.74 <1",
40
- "@earendil-works/pi-coding-agent": ">=0.74 <1",
41
38
  "@sinclair/typebox": "0.34.48"
42
39
  },
43
40
  "devDependencies": {
44
41
  "@changesets/changelog-github": "^0.6.0",
45
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",
46
46
  "esbuild": "^0.28.0",
47
47
  "typescript": "^5.7.0",
48
48
  "vitest": "^3.0.0"
49
49
  },
50
50
  "peerDependencies": {
51
- "openclaw": ">=2026.5.22"
51
+ "openclaw": ">=2026.5.28"
52
52
  },
53
53
  "peerDependenciesMeta": {
54
54
  "openclaw": {
@@ -63,14 +63,14 @@
63
63
  "./dist/index.js"
64
64
  ],
65
65
  "compat": {
66
- "pluginApi": ">=2026.5.22",
67
- "minGatewayVersion": "2026.5.22",
66
+ "pluginApi": ">=2026.5.28",
67
+ "minGatewayVersion": "2026.5.28",
68
68
  "tested": [
69
- "2026.5.22"
69
+ "2026.5.28"
70
70
  ]
71
71
  },
72
72
  "build": {
73
- "openclawVersion": "2026.5.22"
73
+ "openclawVersion": "2026.5.28"
74
74
  }
75
75
  },
76
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
 
@@ -30,6 +30,44 @@ Good default:
30
30
 
31
31
  - `0.75`
32
32
 
33
+ ### `contextThresholdOverrides`
34
+
35
+ Optional ordered rules that choose a different compaction threshold for matching runtime contexts.
36
+
37
+ Supported match fields:
38
+
39
+ - `model`: exact runtime model id, such as `openai/gpt-5.5`
40
+ - `modelContextWindowMin`: match models/windows at or above this token count
41
+ - `modelContextWindowMax`: match models/windows at or below this token count
42
+ - `sessionPattern`: session-key glob, using the same `*` and `**` semantics as ignored/stateless sessions
43
+
44
+ Rules are AND-matched: if a rule includes both `model` and `sessionPattern`, both must match. If multiple rules match, Lossless picks the highest-specificity rule, then the earliest rule in the array for ties. If no rule matches, it falls back to global `contextThreshold`.
45
+
46
+ Example:
47
+
48
+ ```json
49
+ {
50
+ "contextThreshold": 0.75,
51
+ "contextThresholdOverrides": [
52
+ {
53
+ "name": "large-context-models",
54
+ "match": { "modelContextWindowMin": 900000 },
55
+ "contextThreshold": 0.15
56
+ },
57
+ {
58
+ "name": "telegram-sessions",
59
+ "match": { "sessionPattern": "agent:*:telegram:**" },
60
+ "contextThreshold": 0.3
61
+ }
62
+ ]
63
+ }
64
+ ```
65
+
66
+ Debugging:
67
+
68
+ - threshold-selection logs include the selected threshold, source, rule index/name, token budget, threshold tokens, model, context-window value, and match reason
69
+ - there is no env-var override for `contextThresholdOverrides`; use plugin config for structured rules
70
+
33
71
  ### `freshTailCount`
34
72
 
35
73
  Keeps the newest messages raw instead of compacting them.
@@ -286,6 +324,20 @@ Why it matters:
286
324
  - keep this off unless you want transcript GC to mutate the live session file during maintenance
287
325
  - the default is `false`
288
326
 
327
+ ### `enableSummaryThinking`
328
+
329
+ Controls whether the summarization model receives a low reasoning budget.
330
+
331
+ Why it matters:
332
+
333
+ - when `true` (default), summarization calls request `reasoningIfSupported: "low"`, allowing the model to think before producing summaries — this is the current default behavior
334
+ - when `false`, no explicit reasoning budget is requested, which can reduce cost and keep summarization output more concise when reasoning is not needed for faithful summaries
335
+ - set to `false` when you want to minimize token spend on reasoning during compaction, especially with reasoning-capable models
336
+
337
+ Env override:
338
+
339
+ - `LCM_ENABLE_SUMMARY_THINKING`
340
+
289
341
  ### `proactiveThresholdCompactionMode`
290
342
 
291
343
  Controls whether proactive threshold compaction is deferred into maintenance debt or kept inline for legacy behavior.
@@ -326,6 +378,28 @@ Operational logging:
326
378
  - rotate logs include `phase`, `action`, `sessionId`, `sessionKey`, `sessionFile`, `sizeBytes`, `thresholdBytes`, `durationMs`, `backupPath`, `bytesRemoved`, `preservedTailMessageCount`, and `checkpointSize`
327
379
  - real warning logs include the same available context plus `reason` or `error`; quiet startup skips such as missing files, missing bootstrap mappings, and below-threshold files are counted in the summary instead of logged per candidate
328
380
 
381
+ ### `independentLogFile`
382
+
383
+ Writes lossless-claw JSONL logs to an independent plugin-owned file in addition to OpenClaw's runtime logger.
384
+
385
+ Defaults:
386
+
387
+ - `enabled: true`
388
+ - `file: /tmp/openclaw/lossless-claw-YYYY-MM-DD.log`
389
+ - `maxFileBytes: 104857600`
390
+
391
+ Why it matters:
392
+
393
+ - keeps high-volume `[lcm]` operational traces separate from the shared OpenClaw gateway log
394
+ - still sends startup banners and warning/error lines through OpenClaw's runtime logger, so gateway-level startup and failure diagnostics remain visible
395
+ - a dated `lossless-claw-YYYY-MM-DD.log` path rolls over daily, stale dated files are pruned after 3 days, and oversized files rotate through `.1.log` to `.5.log`
396
+
397
+ Env overrides:
398
+
399
+ - `LCM_LOG_FILE_ENABLED`
400
+ - `LCM_LOG_FILE`
401
+ - `LCM_LOG_MAX_FILE_BYTES`
402
+
329
403
  ## Compaction timing and shape
330
404
 
331
405
  ### `contextThreshold`
@@ -517,6 +591,29 @@ Why it matters:
517
591
  - useful when the runtime model window is smaller than the surrounding system assumes
518
592
  - can prevent oversized assembly on smaller-context models
519
593
 
594
+ ## Anti-replay flood guard
595
+
596
+ The ingest path runs `assertNoReplayTimestampFlood` to refuse batches that look like webhook-style replay attacks (many replay-like user messages or many identical internal messages at the same `created_at`). Because SQLite `datetime('now')` is second-granularity, legitimate idempotent bursts from sub-agents can also trip the guard if it is single-threshold. The role-aware thresholds below split the budget by message origin.
597
+
598
+ ### `replayFloodThresholdExternal`
599
+
600
+ Max replay-like messages allowed in a single SQLite-second for `role=user` before the guard refuses the batch. Defaults to `3`.
601
+
602
+ Why it matters:
603
+
604
+ - preserves replay defense for third-partyly-rebroadcastable input
605
+ - lower values are stricter but risk rejecting legitimate dedup retries from upstream channels
606
+
607
+ ### `replayFloodThresholdInternal`
608
+
609
+ Max identical messages allowed in a single SQLite-second for `role=tool/assistant/system` before the guard refuses the batch. Defaults to `32`.
610
+
611
+ Why it matters:
612
+
613
+ - absorbs legitimate same-second idempotent tool returns (for example, sub-agents emitting many `{"status":"ok"}` results)
614
+ - still bounded so a pathological loop cannot ingest unboundedly under the same timestamp
615
+ - raise it if you operate cron sub-agents that emit very tight bursts; lower it if you want stricter sanity protection
616
+
520
617
  ## Nested objects
521
618
 
522
619
  ### `cacheAwareCompaction`
@@ -691,7 +788,7 @@ Design note: stripping happens at compaction time, not at message ingestion. Th
691
788
 
692
789
  Useful interpretation notes:
693
790
 
694
- - `tokens in context` is the current LCM frontier token count in the live LCM state.
791
+ - `LCM frontier tokens` is the current LCM frontier token count in the live LCM state.
695
792
  - `compression ratio` is shown as a rounded `1:N`, which is easier to read than a tiny percentage for heavily compacted conversations.
696
793
  - `/status` may still show a different context number because it reflects the runtime prompt that was actually assembled and sent on the last turn.
697
794
 
@@ -1,9 +1,34 @@
1
1
  # Diagnostics
2
2
 
3
- For the MVP, use the native command surface first.
3
+ For the MVP, use the native command surface first. For debugging lossless-claw behavior or failures, inspect the independent Lossless log before the shared OpenClaw gateway log.
4
4
 
5
5
  ## Fast path
6
6
 
7
+ ### Independent Lossless log
8
+
9
+ Check this first when lossless-claw needs to debug itself, because routine `[lcm]` info and debug lines are written here instead of the shared OpenClaw gateway log.
10
+
11
+ Default path:
12
+
13
+ ```bash
14
+ /tmp/openclaw/lossless-claw-YYYY-MM-DD.log
15
+ ```
16
+
17
+ For today's local log, use:
18
+
19
+ ```bash
20
+ tail -n 200 "/tmp/openclaw/lossless-claw-$(date +%F).log"
21
+ ```
22
+
23
+ Useful patterns:
24
+
25
+ ```bash
26
+ rg -n "\\[lcm\\] (auto-rotate|rotate|runtime\\.llm\\.complete|summary|compact|assembly)" /tmp/openclaw/lossless-claw-*.log
27
+ rg -n "warn|error|failed|truncated|deterministic|fallback" /tmp/openclaw/lossless-claw-*.log
28
+ ```
29
+
30
+ The dated default log rolls over daily. Dated files are pruned after 3 days, and oversized active logs rotate through `.1.log` to `.5.log`. Startup banners and warning/error lines are also sent to OpenClaw's runtime logger, so check `/tmp/openclaw/openclaw-YYYY-MM-DD.log` after the Lossless log when you need gateway-level startup or failure context.
31
+
7
32
  ### `/lossless` (`/lcm` alias)
8
33
 
9
34
  Use this when you need a quick health snapshot.