@joshuaswarren/openclaw-engram 8.3.58 → 8.3.59

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
@@ -377,6 +377,8 @@ function parseConfig(raw) {
377
377
  proactiveExtractionEnabled: cfg.proactiveExtractionEnabled === true,
378
378
  contextCompressionActionsEnabled: cfg.contextCompressionActionsEnabled === true,
379
379
  compressionGuidelineLearningEnabled: cfg.compressionGuidelineLearningEnabled === true,
380
+ compressionGuidelineSemanticRefinementEnabled: cfg.compressionGuidelineSemanticRefinementEnabled === true,
381
+ compressionGuidelineSemanticTimeoutMs: typeof cfg.compressionGuidelineSemanticTimeoutMs === "number" ? Math.max(1, Math.floor(cfg.compressionGuidelineSemanticTimeoutMs)) : 2500,
380
382
  maxProactiveQuestionsPerExtraction: typeof cfg.maxProactiveQuestionsPerExtraction === "number" ? Math.max(0, Math.floor(cfg.maxProactiveQuestionsPerExtraction)) : 2,
381
383
  maxCompressionTokensPerHour: typeof cfg.maxCompressionTokensPerHour === "number" ? Math.max(0, Math.floor(cfg.maxCompressionTokensPerHour)) : 1500,
382
384
  // v8.0 phase 1
@@ -9498,6 +9500,13 @@ function nextOptimizerVersion(previousState) {
9498
9500
  function roundDelta(value) {
9499
9501
  return Math.round(value * 1e3) / 1e3;
9500
9502
  }
9503
+ function confidenceForDelta(delta) {
9504
+ const magnitude = Math.abs(delta);
9505
+ return magnitude >= 0.09 ? "high" : magnitude >= 0.04 ? "medium" : "low";
9506
+ }
9507
+ function directionForDelta(delta) {
9508
+ return delta > 0 ? "increase" : delta < 0 ? "decrease" : "hold";
9509
+ }
9501
9510
  function computeCompressionGuidelineCandidate(events, options = {}) {
9502
9511
  const generatedAt = options.generatedAtIso ?? (/* @__PURE__ */ new Date()).toISOString();
9503
9512
  const previousState = options.previousState ?? null;
@@ -9551,7 +9560,7 @@ function computeCompressionGuidelineCandidate(events, options = {}) {
9551
9560
  const qualitySignal = qualitySeen > 0 ? (summary.quality.good - summary.quality.poor) / qualitySeen : 0;
9552
9561
  const rawDelta = clamp((successRate - failureRate) * 0.12 + qualitySignal * 0.06, -MAX_DELTA, MAX_DELTA);
9553
9562
  const delta = roundDelta(rawDelta);
9554
- const direction = delta > 0 ? "increase" : delta < 0 ? "decrease" : "hold";
9563
+ const direction = directionForDelta(delta);
9555
9564
  if (direction === "decrease" && summary.outcomes.failed > summary.outcomes.applied) {
9556
9565
  notes.push("Failures exceed applied outcomes; conservative down-adjustment.");
9557
9566
  } else if (direction === "increase" && summary.quality.good > summary.quality.poor) {
@@ -9561,8 +9570,7 @@ function computeCompressionGuidelineCandidate(events, options = {}) {
9561
9570
  } else {
9562
9571
  notes.push("Outcomes are stable; keep bounded adjustments.");
9563
9572
  }
9564
- const magnitude = Math.abs(delta);
9565
- const confidence = magnitude >= 0.09 ? "high" : magnitude >= 0.04 ? "medium" : "low";
9573
+ const confidence = confidenceForDelta(delta);
9566
9574
  return {
9567
9575
  action: summary.action,
9568
9576
  delta,
@@ -9584,6 +9592,56 @@ function computeCompressionGuidelineCandidate(events, options = {}) {
9584
9592
  optimizerVersion: nextOptimizerVersion(previousState)
9585
9593
  };
9586
9594
  }
9595
+ async function refineCompressionGuidelineCandidateSemantically(baseline, options) {
9596
+ if (!options.enabled) return baseline;
9597
+ if (typeof options.runRefinement !== "function") return baseline;
9598
+ const timeoutMs = Math.max(1, Math.floor(options.timeoutMs));
9599
+ let timeoutId = null;
9600
+ const timeout = new Promise((resolve) => {
9601
+ timeoutId = setTimeout(() => resolve(null), timeoutMs);
9602
+ });
9603
+ const refinementPromise = options.runRefinement(baseline).catch(() => null);
9604
+ let refinement = null;
9605
+ try {
9606
+ refinement = await Promise.race([refinementPromise, timeout]);
9607
+ } catch {
9608
+ if (timeoutId) clearTimeout(timeoutId);
9609
+ return baseline;
9610
+ }
9611
+ if (timeoutId) clearTimeout(timeoutId);
9612
+ if (!refinement || !Array.isArray(refinement.updates) || refinement.updates.length === 0) {
9613
+ return baseline;
9614
+ }
9615
+ const updatesByAction = /* @__PURE__ */ new Map();
9616
+ for (const update of refinement.updates) {
9617
+ if (!update || typeof update.action !== "string") continue;
9618
+ updatesByAction.set(update.action, update);
9619
+ }
9620
+ let changed = false;
9621
+ const ruleUpdates = baseline.ruleUpdates.map((rule) => {
9622
+ const patch = updatesByAction.get(rule.action);
9623
+ if (!patch) return rule;
9624
+ const nextDelta = typeof patch.delta === "number" && Number.isFinite(patch.delta) ? roundDelta(clamp(patch.delta, -MAX_DELTA, MAX_DELTA)) : rule.delta;
9625
+ const nextConfidence = patch.confidence ?? confidenceForDelta(nextDelta);
9626
+ const nextDirection = directionForDelta(nextDelta);
9627
+ const nextNotes = typeof patch.note === "string" && patch.note.trim().length > 0 ? [patch.note.trim()] : rule.notes;
9628
+ if (nextDelta !== rule.delta || nextDirection !== rule.direction || nextConfidence !== rule.confidence || nextNotes.join("\n") !== rule.notes.join("\n")) {
9629
+ changed = true;
9630
+ }
9631
+ return {
9632
+ ...rule,
9633
+ delta: nextDelta,
9634
+ direction: nextDirection,
9635
+ confidence: nextConfidence,
9636
+ notes: nextNotes
9637
+ };
9638
+ });
9639
+ if (!changed) return baseline;
9640
+ return {
9641
+ ...baseline,
9642
+ ruleUpdates
9643
+ };
9644
+ }
9587
9645
  function renderCompressionGuidelinesMarkdown(candidate) {
9588
9646
  const actionLines = candidate.actionSummaries.length === 0 ? ["- (none)"] : candidate.actionSummaries.map((item) => `- ${item.action}: ${item.total}`);
9589
9647
  const outcomeLines = [
@@ -15115,20 +15173,82 @@ ${texts.map((t, i) => `[${i + 1}] ${t}`).join("\n\n")}`;
15115
15173
  generatedAtIso: generatedAt,
15116
15174
  previousState
15117
15175
  });
15118
- const content = renderCompressionGuidelinesMarkdown(candidate);
15176
+ const refinedCandidate = await refineCompressionGuidelineCandidateSemantically(candidate, {
15177
+ enabled: this.config.compressionGuidelineSemanticRefinementEnabled,
15178
+ timeoutMs: this.config.compressionGuidelineSemanticTimeoutMs,
15179
+ runRefinement: async (baseline) => {
15180
+ const prompt = [
15181
+ "You refine compression policy suggestions conservatively.",
15182
+ "Return JSON only in this shape:",
15183
+ '{"updates":[{"action":"summarize_node","delta":0.02,"confidence":"medium","note":"..."}]}',
15184
+ "Constraints:",
15185
+ "- Keep updates sparse and conservative.",
15186
+ "- delta must stay between -0.15 and 0.15.",
15187
+ "- Only include actions present in the input.",
15188
+ "Input candidate:",
15189
+ JSON.stringify(baseline)
15190
+ ].join("\n");
15191
+ const response = await this.localLlm.chatCompletion(
15192
+ [
15193
+ { role: "system", content: "Respond with strict JSON only. No markdown." },
15194
+ { role: "user", content: prompt }
15195
+ ],
15196
+ {
15197
+ temperature: 0.1,
15198
+ maxTokens: 400,
15199
+ timeoutMs: this.config.compressionGuidelineSemanticTimeoutMs,
15200
+ operation: "compression_guideline_semantic_refinement"
15201
+ }
15202
+ );
15203
+ return this.parseCompressionSemanticRefinement(response?.content ?? "");
15204
+ }
15205
+ });
15206
+ const content = renderCompressionGuidelinesMarkdown(refinedCandidate);
15119
15207
  await this.storage.writeCompressionGuidelines(content);
15120
15208
  await this.storage.writeCompressionGuidelineOptimizerState({
15121
- version: candidate.optimizerVersion,
15122
- updatedAt: candidate.generatedAt,
15123
- sourceWindow: candidate.sourceWindow,
15124
- eventCounts: candidate.eventCounts,
15125
- guidelineVersion: candidate.guidelineVersion
15209
+ version: refinedCandidate.optimizerVersion,
15210
+ updatedAt: refinedCandidate.generatedAt,
15211
+ sourceWindow: refinedCandidate.sourceWindow,
15212
+ eventCounts: refinedCandidate.eventCounts,
15213
+ guidelineVersion: refinedCandidate.guidelineVersion
15126
15214
  });
15127
15215
  log.info(`compression guideline learning updated (${events.length} events)`);
15128
15216
  } catch (err) {
15129
15217
  log.warn(`compression guideline learning failed (ignored): ${err}`);
15130
15218
  }
15131
15219
  }
15220
+ parseCompressionSemanticRefinement(raw) {
15221
+ if (typeof raw !== "string" || raw.trim().length === 0) return null;
15222
+ const trimmed = raw.trim();
15223
+ const start = trimmed.indexOf("{");
15224
+ const end = trimmed.lastIndexOf("}");
15225
+ if (start === -1 || end === -1 || end <= start) return null;
15226
+ try {
15227
+ const parsed = JSON.parse(trimmed.slice(start, end + 1));
15228
+ if (!Array.isArray(parsed?.updates)) return null;
15229
+ const validActions = /* @__PURE__ */ new Set([
15230
+ "store_episode",
15231
+ "store_note",
15232
+ "update_note",
15233
+ "create_artifact",
15234
+ "summarize_node",
15235
+ "discard",
15236
+ "link_graph"
15237
+ ]);
15238
+ const updates = parsed.updates.filter((item) => item && typeof item.action === "string" && validActions.has(item.action)).map((item) => {
15239
+ const confidence = item.confidence === "low" || item.confidence === "medium" || item.confidence === "high" ? item.confidence : void 0;
15240
+ return {
15241
+ action: item.action,
15242
+ delta: typeof item.delta === "number" && Number.isFinite(item.delta) ? item.delta : void 0,
15243
+ confidence,
15244
+ note: typeof item.note === "string" ? item.note : void 0
15245
+ };
15246
+ });
15247
+ return { updates };
15248
+ } catch {
15249
+ return null;
15250
+ }
15251
+ }
15132
15252
  async runLifecyclePolicyPass(allMemories) {
15133
15253
  const now = /* @__PURE__ */ new Date();
15134
15254
  const nowIso = now.toISOString();