@bd7pil/opencode-deep-memory 0.4.0 → 0.4.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.
package/README.md CHANGED
@@ -79,10 +79,9 @@ Activates when context pressure exceeds thresholds. Inspired by
79
79
 
80
80
  | Pressure | Threshold | Actions |
81
81
  |----------|-----------|---------|
82
- | **low** | < 50% context | Layer 1 only |
83
- | **medium** | 50–70% | + tool dedup + error purge + tool output compression |
84
- | **high** | 70–85% | + JSON array crush + old message truncation + nudge |
85
- | **critical** | > 85% | + aggressive nudge (model prompted to compress) |
82
+ | **always** | every turn | tool dedup + error purge + tool output compress + JSON crush (all reversible via CCR) |
83
+ | **medium** | ≥ 30% context | + old message text truncation (lossy, extracts key info) |
84
+ | **high** | ≥ 50% context | + nudge (alerts model to save important findings)
86
85
 
87
86
  What gets compressed at medium+:
88
87
 
@@ -179,6 +178,7 @@ updated incrementally on writes.
179
178
  | `memory_forget` | Remove memory entries matching a query |
180
179
  | `memory_expand` | Decompress a sentinel reference to its original content |
181
180
  | `deep_expand` | Retrieve original content compressed by CCR (use `[ccr:HASH]` marker) |
181
+ | `deep_expand` | Retrieve original content compressed by CCR (use `[ccr:HASH]` marker) |
182
182
 
183
183
  ## Commands
184
184
 
package/dist/index.js CHANGED
@@ -260,6 +260,7 @@ var PluginState = class {
260
260
  _toolSignatures = /* @__PURE__ */ new Map();
261
261
  _ccrCache = /* @__PURE__ */ new Map();
262
262
  _lastInputTokens = 0;
263
+ _lastNudgeMessageCount = 0;
263
264
  agentOf(sessionID) {
264
265
  return this._agents.get(sessionID);
265
266
  }
@@ -404,6 +405,12 @@ var PluginState = class {
404
405
  lastInputTokens() {
405
406
  return this._lastInputTokens;
406
407
  }
408
+ recordNudge(messageCount) {
409
+ this._lastNudgeMessageCount = messageCount;
410
+ }
411
+ messagesSinceLastNudge(currentMessageCount) {
412
+ return currentMessageCount - this._lastNudgeMessageCount;
413
+ }
407
414
  };
408
415
  function createPluginState() {
409
416
  return new PluginState();
@@ -15210,10 +15217,8 @@ function createCompactingHandler(args) {
15210
15217
  // src/compress/pressure.ts
15211
15218
  var DEFAULT_MAX_CONTEXT = 128e3;
15212
15219
  var THRESHOLDS = {
15213
- low: 0.5,
15214
- medium: 0.7,
15215
- high: 0.85,
15216
- critical: 0.95
15220
+ medium: 0.3,
15221
+ high: 0.5
15217
15222
  };
15218
15223
  var MODEL_CONTEXT_LIMITS = {
15219
15224
  "deepseek-chat": 64e3,
@@ -15282,8 +15287,7 @@ function detectPressure(messages, modelId) {
15282
15287
  const estimated = inputTokens > 0 ? inputTokens : extractTokensFromMessages(messages);
15283
15288
  const ratio = Math.min(estimated / maxContext, 1);
15284
15289
  let level;
15285
- if (ratio >= THRESHOLDS.critical) level = "critical";
15286
- else if (ratio >= THRESHOLDS.high) level = "high";
15290
+ if (ratio >= THRESHOLDS.high) level = "high";
15287
15291
  else if (ratio >= THRESHOLDS.medium) level = "medium";
15288
15292
  else level = "low";
15289
15293
  return { level, ratio, estimatedTokens: estimated };
@@ -15552,16 +15556,16 @@ function extractKeyInfo(text) {
15552
15556
 
15553
15557
  // src/compress/nudge.ts
15554
15558
  var NUDGE_COOLDOWN = 5;
15555
- function shouldInjectNudge(level, messageCount, lastNudgeAt) {
15556
- if (level !== "high" && level !== "critical") return false;
15557
- if (messageCount - lastNudgeAt < NUDGE_COOLDOWN) return false;
15559
+ function shouldInjectNudge(level, messagesSinceLastNudge) {
15560
+ if (level !== "high") return false;
15561
+ if (messagesSinceLastNudge < NUDGE_COOLDOWN) return false;
15558
15562
  return true;
15559
15563
  }
15560
15564
  function buildNudgeText(level) {
15561
- if (level === "critical") {
15562
- return '\n<dm-nudge level="critical">Context is nearly full. Use deep_compress tool to compress old messages before the conversation becomes unusable.</dm-nudge>';
15565
+ if (level === "high") {
15566
+ return '\n<dm-nudge level="high">Context pressure is high. Consider summarizing old completed tasks and moving on. Use memory_store to persist important findings before they are compressed.</dm-nudge>';
15563
15567
  }
15564
- return '\n<dm-nudge level="high">Context is getting large. Consider compressing old tool outputs and messages to free space.</dm-nudge>';
15568
+ return "";
15565
15569
  }
15566
15570
 
15567
15571
  // src/compress/detector.ts
@@ -15602,25 +15606,15 @@ function runCompressionPipeline(ctx) {
15602
15606
  pressureLevel: pressure.level,
15603
15607
  estimatedTokens: pressure.estimatedTokens
15604
15608
  };
15605
- if (pressure.level === "low") {
15606
- logger?.debug("compress: low pressure, skipping", { ratio: pressure.ratio.toFixed(2) });
15607
- return { stats };
15608
- }
15609
- logger?.debug("compress: pipeline running", {
15610
- level: pressure.level,
15611
- ratio: pressure.ratio.toFixed(2),
15612
- tokens: pressure.estimatedTokens
15613
- });
15614
- if (pressure.level === "medium" || pressure.level === "high" || pressure.level === "critical") {
15615
- stats.toolDedup = deduplicateToolOutputs(messages, state);
15616
- stats.errorPurge = purgeOldErrors(messages);
15617
- stats.toolOutputCompressed = compressOldToolOutputs(messages, state);
15618
- }
15619
- if (pressure.level === "high" || pressure.level === "critical") {
15620
- stats.jsonCrushed = crushJsonToolOutputs(messages, state);
15609
+ stats.toolDedup = deduplicateToolOutputs(messages, state);
15610
+ stats.errorPurge = purgeOldErrors(messages);
15611
+ stats.toolOutputCompressed = compressOldToolOutputs(messages, state);
15612
+ stats.jsonCrushed = crushJsonToolOutputs(messages, state);
15613
+ if (pressure.level === "medium" || pressure.level === "high") {
15621
15614
  stats.messagePruned = pruneOldMessages(messages);
15622
15615
  }
15623
- if (shouldInjectNudge(pressure.level, messages.length, 0)) {
15616
+ const messagesSinceNudge = state.messagesSinceLastNudge(messages.length);
15617
+ if (shouldInjectNudge(pressure.level, messagesSinceNudge)) {
15624
15618
  const lastMsg = messages[messages.length - 1];
15625
15619
  if (lastMsg) {
15626
15620
  const textParts = lastMsg.parts.filter(
@@ -15630,11 +15624,15 @@ function runCompressionPipeline(ctx) {
15630
15624
  if (lastTextPart && typeof lastTextPart.text === "string") {
15631
15625
  lastTextPart.text += buildNudgeText(pressure.level);
15632
15626
  stats.nudgeInjected = true;
15627
+ state.recordNudge(messages.length);
15633
15628
  }
15634
15629
  }
15635
15630
  }
15636
- if (stats.toolDedup > 0 || stats.errorPurge > 0 || stats.toolOutputCompressed > 0 || stats.jsonCrushed > 0 || stats.messagePruned > 0 || stats.nudgeInjected) {
15637
- logger?.debug("compress: pipeline complete", { ...stats });
15631
+ const active = stats.toolDedup > 0 || stats.errorPurge > 0 || stats.toolOutputCompressed > 0 || stats.jsonCrushed > 0 || stats.messagePruned > 0 || stats.nudgeInjected;
15632
+ if (active) {
15633
+ logger?.debug("compress: pipeline result", { ...stats });
15634
+ } else {
15635
+ logger?.debug("compress: no action needed", { ratio: pressure.ratio.toFixed(2) });
15638
15636
  }
15639
15637
  return { stats };
15640
15638
  }
@@ -15651,6 +15649,7 @@ function compressOldToolOutputs(messages, state) {
15651
15649
  if (!p.state.output) continue;
15652
15650
  if (p.state.output === "[superseded by duplicate call]") continue;
15653
15651
  if (p.state.output.startsWith("[compressed")) continue;
15652
+ if (p.state.output.includes("[ccr:")) continue;
15654
15653
  const toolName = p.tool || "unknown";
15655
15654
  const output = p.state.output;
15656
15655
  const result = compressToolOutput(toolName, output);
@@ -15676,6 +15675,7 @@ function crushJsonToolOutputs(messages, state) {
15676
15675
  if (!p.state.output) continue;
15677
15676
  if (p.state.output.startsWith("[compressed")) continue;
15678
15677
  if (p.state.output.startsWith("[superseded")) continue;
15678
+ if (p.state.output.includes("[ccr:")) continue;
15679
15679
  if (detectContentType(p.state.output) !== "json") continue;
15680
15680
  const original = p.state.output;
15681
15681
  const crushed_output = crushJsonArray(original);