@digitalforgestudios/openclaw-sulcus 1.1.0 → 1.1.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.
Files changed (2) hide show
  1. package/index.ts +62 -0
  2. package/package.json +1 -1
package/index.ts CHANGED
@@ -214,6 +214,23 @@ function shouldCapture(text: string): boolean {
214
214
  // Reject if stripping removed >60% of the content (mostly metadata)
215
215
  if (cleaned.length < text.length * 0.4) return false;
216
216
 
217
+ // Reject system prompts and OpenClaw operational messages that caused 1,000+ dupes
218
+ const rejectPatterns = [
219
+ /^Pre-compaction memory flush/i,
220
+ /^A new session was started via/i,
221
+ /^\[cron:[0-9a-f-]+/i,
222
+ /^To send an image back, prefer the message tool/i,
223
+ /^Heartbeat prompt:/i,
224
+ /^Read HEARTBEAT\.md/i,
225
+ /^Run your Session Startup sequence/i,
226
+ /^You are \w+\. T/i, // cron job identity preambles
227
+ /^Gateway restart/i,
228
+ /^System: \[/,
229
+ /^HEARTBEAT_OK$/i,
230
+ /^NO_REPLY$/i,
231
+ ];
232
+ if (rejectPatterns.some((r) => r.test(cleaned))) return false;
233
+
217
234
  const triggers = [
218
235
  /remember|zapamatuj/i,
219
236
  /prefer|like|love|hate|want/i,
@@ -558,6 +575,51 @@ const sulcusMemoryPlugin = {
558
575
  });
559
576
  }
560
577
 
578
+ // ========================================================================
579
+ // Lifecycle — Preserve memories before compaction
580
+ // ========================================================================
581
+
582
+ api.on("before_compaction", async (event) => {
583
+ if (!event.messages || event.messages.length === 0) return;
584
+
585
+ try {
586
+ // Scan messages being compacted for important content worth preserving
587
+ const toPreserve: string[] = [];
588
+ for (const msg of event.messages) {
589
+ if (!msg || typeof msg !== "object") continue;
590
+ const msgObj = msg as Record<string, unknown>;
591
+ const content = typeof msgObj.content === "string" ? msgObj.content : "";
592
+ if (!content || content.length < 30) continue;
593
+
594
+ const cleaned = stripMetadataEnvelope(content);
595
+ if (cleaned.length < 30) continue;
596
+ if (!shouldCapture(cleaned)) continue;
597
+
598
+ toPreserve.push(cleaned);
599
+ }
600
+
601
+ if (toPreserve.length === 0) return;
602
+
603
+ // Store up to 5 important memories before compaction discards them
604
+ let stored = 0;
605
+ for (const text of toPreserve.slice(0, 5)) {
606
+ const type = detectMemoryType(text);
607
+ try {
608
+ await client.store(text.slice(0, 2000), type);
609
+ stored++;
610
+ } catch {
611
+ // 409 (dedup) or other — continue
612
+ }
613
+ }
614
+
615
+ if (stored > 0) {
616
+ api.logger.info(`openclaw-sulcus: preserved ${stored} memories before compaction`);
617
+ }
618
+ } catch (err) {
619
+ api.logger.warn(`openclaw-sulcus: before_compaction failed: ${String(err)}`);
620
+ }
621
+ });
622
+
561
623
  // ========================================================================
562
624
  // Lifecycle — Auto-capture
563
625
  // ========================================================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@digitalforgestudios/openclaw-sulcus",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Sulcus memory plugin for OpenClaw — thermodynamic memory with heat-based decay, reactive triggers, and cross-agent sync.",
5
5
  "keywords": [
6
6
  "openclaw",