@bd7pil/opencode-deep-memory 0.6.0 → 0.7.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/dist/index.js CHANGED
@@ -15284,12 +15284,10 @@ function createCompactingHandler(args) {
15284
15284
  }
15285
15285
 
15286
15286
  // src/compress/pressure.ts
15287
- var FALLBACK_MAX_CONTEXT = 128e3;
15287
+ var FALLBACK_MAX_CONTEXT = 1e6;
15288
15288
  var OPENCODE_COMPACTION_RATIO = 0.75;
15289
- var THRESHOLDS = {
15290
- medium: 0.2,
15291
- high: 0.4
15292
- };
15289
+ var PRESSURE_MEDIUM_TOKENS = 5e4;
15290
+ var PRESSURE_HIGH_TOKENS = 15e4;
15293
15291
  var calibratedMaxContext = 0;
15294
15292
  function calibrateFromCompaction(lastInputTokens) {
15295
15293
  if (lastInputTokens <= 0) return;
@@ -15303,67 +15301,33 @@ function maxContextFrom(modelContextWindow) {
15303
15301
  if (calibratedMaxContext > 0) return calibratedMaxContext;
15304
15302
  return FALLBACK_MAX_CONTEXT;
15305
15303
  }
15306
- function estimateTokens2(text) {
15307
- let cjk = 0;
15308
- let other = 0;
15309
- for (const ch of text) {
15310
- if (/[\u4e00-\u9fff\u3400-\u4dbf\u3000-\u303f\uff00-\uffef\u3040-\u309f\u30a0-\u30ff]/.test(ch)) {
15311
- cjk++;
15312
- } else {
15313
- other++;
15314
- }
15315
- }
15316
- return Math.ceil(cjk * 0.7 + other / 3.8);
15317
- }
15318
- function extractTokensFromMessages(messages) {
15319
- let total = 0;
15320
- for (const msg of messages) {
15321
- for (const part of msg.parts) {
15322
- if (typeof part !== "object" || part === null) continue;
15323
- const p = part;
15324
- if (p["type"] === "text" && typeof p["text"] === "string") {
15325
- total += estimateTokens2(p["text"]);
15326
- } else if (p["type"] === "tool") {
15327
- const state = p["state"];
15328
- if (state?.["output"] && typeof state["output"] === "string") {
15329
- total += estimateTokens2(state["output"]);
15330
- }
15331
- if (state?.["error"] && typeof state["error"] === "string") {
15332
- total += estimateTokens2(state["error"]);
15333
- }
15334
- } else if (p["type"] === "reasoning" || p["type"] === "thinking") {
15335
- if (typeof p["text"] === "string") {
15336
- total += estimateTokens2(p["text"]);
15337
- }
15338
- }
15339
- }
15340
- }
15341
- return total;
15342
- }
15343
15304
  function extractInputTokensFromMessages(messages) {
15305
+ let best = 0;
15344
15306
  for (let i = messages.length - 1; i >= 0; i--) {
15345
15307
  const msg = messages[i];
15346
15308
  for (const part of msg.parts) {
15347
15309
  if (typeof part !== "object" || part === null) continue;
15348
15310
  const p = part;
15349
15311
  if (p["type"] === "step-finish") {
15350
- const tokens = p.tokens;
15351
- if (tokens?.input && tokens.input > 0) {
15352
- return tokens.input;
15353
- }
15312
+ const tokens = p;
15313
+ const input = tokens.tokens?.input ?? 0;
15314
+ const cached2 = tokens.tokens?.cached ?? 0;
15315
+ const total = input + cached2;
15316
+ if (total > best) best = total;
15317
+ if (best > 0) return best;
15354
15318
  }
15355
15319
  }
15356
15320
  }
15357
- return 0;
15321
+ return best;
15358
15322
  }
15359
15323
  function detectPressure(messages, modelContextWindow) {
15360
15324
  const ctx = maxContextFrom(modelContextWindow || 0);
15361
15325
  const inputTokens = extractInputTokensFromMessages(messages);
15362
- const estimated = inputTokens > 0 ? inputTokens : extractTokensFromMessages(messages);
15326
+ const estimated = inputTokens > 0 ? inputTokens : 0;
15363
15327
  const ratio = Math.min(estimated / ctx, 1);
15364
15328
  let level;
15365
- if (ratio >= THRESHOLDS.high) level = "high";
15366
- else if (ratio >= THRESHOLDS.medium) level = "medium";
15329
+ if (estimated >= PRESSURE_HIGH_TOKENS) level = "high";
15330
+ else if (estimated >= PRESSURE_MEDIUM_TOKENS) level = "medium";
15367
15331
  else level = "low";
15368
15332
  return { level, ratio, estimatedTokens: estimated, maxContext: ctx };
15369
15333
  }
@@ -15627,6 +15591,8 @@ var PROTECTED_TOOLS = /* @__PURE__ */ new Set([
15627
15591
  var NEVER_DEDUP = /* @__PURE__ */ new Set(["read", "bash", "grep", "glob", "find", "search"]);
15628
15592
  var ERROR_PURGE_TURN_THRESHOLD = 4;
15629
15593
  var PROTECTED_HEAD_SINGLE = 2;
15594
+ var ASSISTANT_COMPRESS_MIN_LENGTH = 500;
15595
+ var ASSISTANT_COMPRESS_SAVINGS_RATIO = 0.6;
15630
15596
  function simpleHash(s) {
15631
15597
  const len = s.length;
15632
15598
  const sampleSize = 500;
@@ -15640,12 +15606,32 @@ function simpleHash(s) {
15640
15606
  }
15641
15607
  return `${len}:${h.toString(36)}`;
15642
15608
  }
15609
+ function compressAssistantText(text) {
15610
+ if (text.length < ASSISTANT_COMPRESS_MIN_LENGTH) return text;
15611
+ const lines = text.split("\n");
15612
+ const head = 3;
15613
+ const tail = 3;
15614
+ const kept = [];
15615
+ for (let i = 0; i < lines.length; i++) {
15616
+ const line = lines[i];
15617
+ if (i < head || i >= lines.length - tail) {
15618
+ kept.push(line);
15619
+ continue;
15620
+ }
15621
+ if (/^#{1,3}\s/.test(line) || /error|fail|warning|critical|important/i.test(line) || /^\s*[-*]\s/.test(line) || /^\s*\d+\.\s/.test(line) || line.trim().startsWith("```") || /^\/[^\s:]+/.test(line)) {
15622
+ kept.push(line);
15623
+ }
15624
+ }
15625
+ const result = kept.join("\n");
15626
+ return result.length < text.length * ASSISTANT_COMPRESS_SAVINGS_RATIO ? result : text;
15627
+ }
15643
15628
  function singlePassCompress(messages, state, protectedTail) {
15644
15629
  const stats = {
15645
15630
  toolDedup: 0,
15646
15631
  errorPurge: 0,
15647
15632
  toolOutputCompressed: 0,
15648
15633
  jsonCrushed: 0,
15634
+ assistantCompressed: 0,
15649
15635
  ccrStored: 0
15650
15636
  };
15651
15637
  const totalMessages = messages.length;
@@ -15725,6 +15711,21 @@ function singlePassCompress(messages, state, protectedTail) {
15725
15711
  }
15726
15712
  }
15727
15713
  }
15714
+ if (i < protectedTail && msg.info.role === "assistant") {
15715
+ for (let j = msg.parts.length - 1; j >= 0; j--) {
15716
+ const part = msg.parts[j];
15717
+ if (typeof part !== "object" || part === null) continue;
15718
+ const p = part;
15719
+ if (p["type"] !== "text") continue;
15720
+ const text = p["text"];
15721
+ if (typeof text !== "string") continue;
15722
+ const compressed = compressAssistantText(text);
15723
+ if (compressed !== text) {
15724
+ p["text"] = compressed;
15725
+ stats.assistantCompressed++;
15726
+ }
15727
+ }
15728
+ }
15728
15729
  }
15729
15730
  return stats;
15730
15731
  }
@@ -15766,6 +15767,7 @@ function runCompressionPipeline(ctx) {
15766
15767
  errorPurge: spStats.errorPurge,
15767
15768
  toolOutputCompressed: spStats.toolOutputCompressed,
15768
15769
  jsonCrushed: spStats.jsonCrushed,
15770
+ assistantCompressed: spStats.assistantCompressed,
15769
15771
  ccrStored: spStats.ccrStored,
15770
15772
  nudgeInjected: false,
15771
15773
  pressureLevel: pressure.level,
@@ -15786,7 +15788,7 @@ function runCompressionPipeline(ctx) {
15786
15788
  logger?.debug("compress: memory nudge", { type: memoryNudge.type });
15787
15789
  }
15788
15790
  }
15789
- const active = stats.toolDedup > 0 || stats.errorPurge > 0 || stats.toolOutputCompressed > 0 || stats.jsonCrushed > 0 || stats.nudgeInjected;
15791
+ const active = stats.toolDedup > 0 || stats.errorPurge > 0 || stats.toolOutputCompressed > 0 || stats.jsonCrushed > 0 || stats.assistantCompressed > 0 || stats.nudgeInjected;
15790
15792
  if (active) {
15791
15793
  logger?.debug("compress: pipeline result", { ...stats });
15792
15794
  } else {
@@ -15976,7 +15978,7 @@ function createMessagesTransformHandler(state, logger) {
15976
15978
  logger
15977
15979
  });
15978
15980
  const ds = pipelineResult.stats;
15979
- if (ds.toolDedup > 0 || ds.errorPurge > 0 || ds.toolOutputCompressed > 0 || ds.jsonCrushed > 0 || ds.nudgeInjected) {
15981
+ if (ds.toolDedup > 0 || ds.errorPurge > 0 || ds.toolOutputCompressed > 0 || ds.jsonCrushed > 0 || ds.assistantCompressed > 0 || ds.nudgeInjected) {
15980
15982
  logger?.debug("messages.transform: deep compression", { ...ds });
15981
15983
  state.mergeNotify({
15982
15984
  compression: stats,
@@ -16116,6 +16118,29 @@ function createNotifyHandler(client, logger) {
16116
16118
  };
16117
16119
  }
16118
16120
 
16121
+ // src/shared/model-limits.ts
16122
+ var KNOWN_MODEL_LIMITS = {
16123
+ "deepseek-v4-pro": 1e6,
16124
+ "deepseek-v4": 1e6,
16125
+ "deepseek-v3": 64e3,
16126
+ "deepseek-r1": 64e3,
16127
+ "claude-opus-4": 2e5,
16128
+ "claude-sonnet-4": 2e5,
16129
+ "gpt-4o": 128e3,
16130
+ "o1": 2e5,
16131
+ "o3-mini": 2e5,
16132
+ "gemini-2.5-pro": 1e6,
16133
+ "gemini-2.5-flash": 1e6,
16134
+ "qwen-max": 131072
16135
+ };
16136
+ function lookupModelLimit(modelID) {
16137
+ if (KNOWN_MODEL_LIMITS[modelID]) return KNOWN_MODEL_LIMITS[modelID];
16138
+ for (const [key, limit] of Object.entries(KNOWN_MODEL_LIMITS)) {
16139
+ if (modelID.includes(key)) return limit;
16140
+ }
16141
+ return void 0;
16142
+ }
16143
+
16119
16144
  // src/extract/enrich.ts
16120
16145
  import { stat } from "fs/promises";
16121
16146
 
@@ -16447,11 +16472,14 @@ var deepMemoryPlugin = async (input) => {
16447
16472
  const defaultModel = configResult.data?.model;
16448
16473
  if (typeof defaultModel === "string" && defaultModel.includes("/")) {
16449
16474
  const slashIdx = defaultModel.indexOf("/");
16450
- state.recordFallbackModel({
16451
- providerID: defaultModel.slice(0, slashIdx),
16452
- modelID: defaultModel.slice(slashIdx + 1)
16453
- });
16454
- logger.debug("resolved fallback model from config", { defaultModel });
16475
+ const providerID = defaultModel.slice(0, slashIdx);
16476
+ const modelID = defaultModel.slice(slashIdx + 1);
16477
+ state.recordFallbackModel({ providerID, modelID });
16478
+ const limit = lookupModelLimit(modelID);
16479
+ if (limit) {
16480
+ state.setModelContextWindow(limit);
16481
+ logger.debug("resolved model context window", { modelID, limit });
16482
+ }
16455
16483
  }
16456
16484
  }).catch((err) => {
16457
16485
  logger.debug("config.get failed, dream/distill will omit model", {