@martian-engineering/lossless-claw 0.12.0 → 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.
- package/README.md +14 -1
- package/dist/index.js +175 -41
- package/docs/configuration.md +37 -1
- package/openclaw.plugin.json +99 -0
- package/package.json +9 -9
- package/skills/lossless-claw/SKILL.md +5 -4
- package/skills/lossless-claw/references/config.md +97 -0
- package/skills/lossless-claw/references/diagnostics.md +26 -1
package/docs/configuration.md
CHANGED
|
@@ -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
|
|
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.
|
package/openclaw.plugin.json
CHANGED
|
@@ -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.
|
|
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",
|
|
@@ -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.
|
|
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.
|
|
67
|
-
"minGatewayVersion": "2026.5.
|
|
66
|
+
"pluginApi": ">=2026.5.28",
|
|
67
|
+
"minGatewayVersion": "2026.5.28",
|
|
68
68
|
"tested": [
|
|
69
|
-
"2026.5.
|
|
69
|
+
"2026.5.28"
|
|
70
70
|
]
|
|
71
71
|
},
|
|
72
72
|
"build": {
|
|
73
|
-
"openclawVersion": "2026.5.
|
|
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
|
|
15
|
-
4. If they
|
|
16
|
-
5. If they
|
|
17
|
-
6.
|
|
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`
|
|
@@ -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.
|