openclaw-topic-shift-reset 0.4.4 → 0.4.5

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/package.json +1 -1
  2. package/src/index.ts +24 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-topic-shift-reset",
3
- "version": "0.4.4",
3
+ "version": "0.4.5",
4
4
  "description": "OpenClaw plugin that detects topic shifts and starts a fresh session automatically.",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
package/src/index.ts CHANGED
@@ -1286,20 +1286,29 @@ function classifyMessage(params: {
1286
1286
  params.lexical.score >= cfg.hardScoreThreshold ||
1287
1287
  (params.lexical.novelty >= cfg.hardNoveltyThreshold &&
1288
1288
  params.lexical.lexicalDistance >= 0.65);
1289
- const hardSignal =
1290
- hasSimilarity &&
1291
- (score >= cfg.hardScoreThreshold ||
1292
- (params.similarity <= cfg.hardSimilarityThreshold &&
1293
- params.lexical.novelty >= cfg.hardNoveltyThreshold));
1289
+ // Require a strong combined score for immediate hard-rotate.
1290
+ // Similarity+novelty spikes go through soft confirmation instead.
1291
+ const hardSignal = hasSimilarity && score >= cfg.hardScoreThreshold;
1294
1292
 
1295
1293
  if (hardSignal) {
1296
1294
  return { kind: "rotate-hard", metrics, reason: "hard-threshold" };
1297
1295
  }
1298
1296
 
1299
1297
  const forceSoftPathFromLexicalHard = !hasSimilarity && lexicalHardSignal;
1298
+ const forceSoftPathFromHardSimilarity =
1299
+ hasSimilarity &&
1300
+ params.similarity <= cfg.hardSimilarityThreshold &&
1301
+ params.lexical.novelty >= cfg.hardNoveltyThreshold;
1302
+ const embeddingLexicalOverrideSoftSignal =
1303
+ hasSimilarity &&
1304
+ params.lexical.lexicalDistance >= 0.9 &&
1305
+ params.lexical.novelty >= cfg.softNoveltyThreshold &&
1306
+ score >= cfg.softScoreThreshold - cfg.embeddingTriggerMargin;
1300
1307
  const softSignal =
1301
1308
  forceSoftPathFromLexicalHard ||
1309
+ forceSoftPathFromHardSimilarity ||
1302
1310
  score >= cfg.softScoreThreshold ||
1311
+ embeddingLexicalOverrideSoftSignal ||
1303
1312
  (hasSimilarity
1304
1313
  ? params.similarity <= cfg.softSimilarityThreshold &&
1305
1314
  params.lexical.novelty >= cfg.softNoveltyThreshold
@@ -1795,11 +1804,19 @@ async function rotateSessionEntry(params: {
1795
1804
  logger: params.api.logger,
1796
1805
  });
1797
1806
 
1807
+ const seededHistory =
1808
+ params.reason === "soft-confirmed"
1809
+ ? trimHistory([...params.state.pendingEntries, params.entry], params.cfg.historyWindow)
1810
+ : trimHistory([params.entry], params.cfg.historyWindow);
1811
+
1798
1812
  params.state.lastResetAt = Date.now();
1799
1813
  params.state.pendingSoftSignals = 0;
1800
1814
  params.state.pendingEntries = [];
1801
- params.state.history = trimHistory([params.entry], params.cfg.historyWindow);
1802
- seedTopicCentroid(params.state, params.entry.embedding);
1815
+ params.state.history = seededHistory;
1816
+ seedTopicCentroid(params.state, undefined);
1817
+ for (const historyEntry of seededHistory) {
1818
+ updateTopicCentroid(params.state, historyEntry.embedding);
1819
+ }
1803
1820
 
1804
1821
  params.api.logger.info(
1805
1822
  [