@joshuaswarren/openclaw-engram 8.3.58 → 8.3.60

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 = [
@@ -15105,30 +15163,123 @@ ${texts.map((t, i) => `[${i + 1}] ${t}`).join("\n\n")}`;
15105
15163
  log.info("consolidation complete");
15106
15164
  return { memoriesProcessed: allMemories.length, merged, invalidated };
15107
15165
  }
15108
- async runCompressionGuidelineLearningPass() {
15109
- if (!this.config.compressionGuidelineLearningEnabled) return;
15110
- try {
15111
- const previousState = await this.storage.readCompressionGuidelineOptimizerState();
15112
- const events = await this.storage.readMemoryActionEvents(500);
15113
- const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
15114
- const candidate = computeCompressionGuidelineCandidate(events, {
15115
- generatedAtIso: generatedAt,
15116
- previousState
15117
- });
15118
- const content = renderCompressionGuidelinesMarkdown(candidate);
15166
+ async optimizeCompressionGuidelines(options) {
15167
+ const dryRun = options?.dryRun === true;
15168
+ const eventLimit = typeof options?.eventLimit === "number" ? Math.max(0, Math.floor(options.eventLimit)) : 500;
15169
+ const previousState = await this.storage.readCompressionGuidelineOptimizerState();
15170
+ if (!this.config.compressionGuidelineLearningEnabled) {
15171
+ return {
15172
+ enabled: false,
15173
+ dryRun,
15174
+ eventCount: 0,
15175
+ previousGuidelineVersion: previousState?.guidelineVersion ?? null,
15176
+ nextGuidelineVersion: previousState?.guidelineVersion ?? 0,
15177
+ changedRules: 0,
15178
+ semanticRefinementApplied: false,
15179
+ persisted: false
15180
+ };
15181
+ }
15182
+ const events = await this.storage.readMemoryActionEvents(eventLimit);
15183
+ const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
15184
+ const candidate = computeCompressionGuidelineCandidate(events, {
15185
+ generatedAtIso: generatedAt,
15186
+ previousState
15187
+ });
15188
+ const refinedCandidate = await refineCompressionGuidelineCandidateSemantically(candidate, {
15189
+ enabled: this.config.compressionGuidelineSemanticRefinementEnabled,
15190
+ timeoutMs: this.config.compressionGuidelineSemanticTimeoutMs,
15191
+ runRefinement: async (baseline) => {
15192
+ const prompt = [
15193
+ "You refine compression policy suggestions conservatively.",
15194
+ "Return JSON only in this shape:",
15195
+ '{"updates":[{"action":"summarize_node","delta":0.02,"confidence":"medium","note":"..."}]}',
15196
+ "Constraints:",
15197
+ "- Keep updates sparse and conservative.",
15198
+ "- delta must stay between -0.15 and 0.15.",
15199
+ "- Only include actions present in the input.",
15200
+ "Input candidate:",
15201
+ JSON.stringify(baseline)
15202
+ ].join("\n");
15203
+ const response = await this.localLlm.chatCompletion(
15204
+ [
15205
+ { role: "system", content: "Respond with strict JSON only. No markdown." },
15206
+ { role: "user", content: prompt }
15207
+ ],
15208
+ {
15209
+ temperature: 0.1,
15210
+ maxTokens: 400,
15211
+ timeoutMs: this.config.compressionGuidelineSemanticTimeoutMs,
15212
+ operation: "compression_guideline_semantic_refinement"
15213
+ }
15214
+ );
15215
+ return this.parseCompressionSemanticRefinement(response?.content ?? "");
15216
+ }
15217
+ });
15218
+ const content = renderCompressionGuidelinesMarkdown(refinedCandidate);
15219
+ const semanticRefinementApplied = JSON.stringify(refinedCandidate.ruleUpdates) !== JSON.stringify(candidate.ruleUpdates);
15220
+ const changedRules = refinedCandidate.ruleUpdates.filter((rule) => rule.delta !== 0).length;
15221
+ if (!dryRun) {
15119
15222
  await this.storage.writeCompressionGuidelines(content);
15120
15223
  await this.storage.writeCompressionGuidelineOptimizerState({
15121
- version: candidate.optimizerVersion,
15122
- updatedAt: candidate.generatedAt,
15123
- sourceWindow: candidate.sourceWindow,
15124
- eventCounts: candidate.eventCounts,
15125
- guidelineVersion: candidate.guidelineVersion
15224
+ version: refinedCandidate.optimizerVersion,
15225
+ updatedAt: refinedCandidate.generatedAt,
15226
+ sourceWindow: refinedCandidate.sourceWindow,
15227
+ eventCounts: refinedCandidate.eventCounts,
15228
+ guidelineVersion: refinedCandidate.guidelineVersion
15126
15229
  });
15127
- log.info(`compression guideline learning updated (${events.length} events)`);
15230
+ }
15231
+ return {
15232
+ enabled: true,
15233
+ dryRun,
15234
+ eventCount: events.length,
15235
+ previousGuidelineVersion: previousState?.guidelineVersion ?? null,
15236
+ nextGuidelineVersion: refinedCandidate.guidelineVersion,
15237
+ changedRules,
15238
+ semanticRefinementApplied,
15239
+ persisted: !dryRun
15240
+ };
15241
+ }
15242
+ async runCompressionGuidelineLearningPass() {
15243
+ if (!this.config.compressionGuidelineLearningEnabled) return;
15244
+ try {
15245
+ const result = await this.optimizeCompressionGuidelines({ dryRun: false, eventLimit: 500 });
15246
+ log.info(`compression guideline learning updated (${result.eventCount} events)`);
15128
15247
  } catch (err) {
15129
15248
  log.warn(`compression guideline learning failed (ignored): ${err}`);
15130
15249
  }
15131
15250
  }
15251
+ parseCompressionSemanticRefinement(raw) {
15252
+ if (typeof raw !== "string" || raw.trim().length === 0) return null;
15253
+ const trimmed = raw.trim();
15254
+ const start = trimmed.indexOf("{");
15255
+ const end = trimmed.lastIndexOf("}");
15256
+ if (start === -1 || end === -1 || end <= start) return null;
15257
+ try {
15258
+ const parsed = JSON.parse(trimmed.slice(start, end + 1));
15259
+ if (!Array.isArray(parsed?.updates)) return null;
15260
+ const validActions = /* @__PURE__ */ new Set([
15261
+ "store_episode",
15262
+ "store_note",
15263
+ "update_note",
15264
+ "create_artifact",
15265
+ "summarize_node",
15266
+ "discard",
15267
+ "link_graph"
15268
+ ]);
15269
+ const updates = parsed.updates.filter((item) => item && typeof item.action === "string" && validActions.has(item.action)).map((item) => {
15270
+ const confidence = item.confidence === "low" || item.confidence === "medium" || item.confidence === "high" ? item.confidence : void 0;
15271
+ return {
15272
+ action: item.action,
15273
+ delta: typeof item.delta === "number" && Number.isFinite(item.delta) ? item.delta : void 0,
15274
+ confidence,
15275
+ note: typeof item.note === "string" ? item.note : void 0
15276
+ };
15277
+ });
15278
+ return { updates };
15279
+ } catch {
15280
+ return null;
15281
+ }
15282
+ }
15132
15283
  async runLifecyclePolicyPass(allMemories) {
15133
15284
  const now = /* @__PURE__ */ new Date();
15134
15285
  const nowIso = now.toISOString();
@@ -17447,6 +17598,49 @@ NOTE: You did not provide sessionKey; under concurrency this may not match your
17447
17598
  },
17448
17599
  { name: "memory_action_apply" }
17449
17600
  );
17601
+ api.registerTool(
17602
+ {
17603
+ name: "compression_guidelines_optimize",
17604
+ label: "Optimize Compression Guidelines",
17605
+ description: "Run compression guideline optimizer and optionally persist the new guideline/state (v8.11).",
17606
+ parameters: Type.Object({
17607
+ dryRun: Type.Optional(
17608
+ Type.Boolean({
17609
+ description: "When true, compute candidate/output but do not persist changes."
17610
+ })
17611
+ ),
17612
+ eventLimit: Type.Optional(
17613
+ Type.Number({
17614
+ description: "Max telemetry events to analyze (default: 500)."
17615
+ })
17616
+ )
17617
+ }),
17618
+ async execute(_toolCallId, params) {
17619
+ const { dryRun, eventLimit } = params;
17620
+ const result = await orchestrator.optimizeCompressionGuidelines({
17621
+ dryRun: dryRun === true,
17622
+ eventLimit
17623
+ });
17624
+ if (!result.enabled) {
17625
+ return toolResult(
17626
+ "Compression guideline learning is disabled. Enable `compressionGuidelineLearningEnabled: true` to run optimizer."
17627
+ );
17628
+ }
17629
+ return toolResult(
17630
+ [
17631
+ "Compression guideline optimization complete.",
17632
+ `dryRun=${result.dryRun}`,
17633
+ `persisted=${result.persisted}`,
17634
+ `eventCount=${result.eventCount}`,
17635
+ `guidelineVersion: ${result.previousGuidelineVersion ?? "none"} -> ${result.nextGuidelineVersion}`,
17636
+ `changedRules=${result.changedRules}`,
17637
+ `semanticRefinementApplied=${result.semanticRefinementApplied}`
17638
+ ].join("\n")
17639
+ );
17640
+ }
17641
+ },
17642
+ { name: "compression_guidelines_optimize" }
17643
+ );
17450
17644
  api.registerTool(
17451
17645
  {
17452
17646
  name: "memory_store",