blun-king-cli 9.1.203 → 9.1.205

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.
@@ -1,6 +1,69 @@
1
1
  'use strict';
2
2
 
3
3
  const ERROR_MEMORY_MAX_CHARS = 2_800;
4
+ const ERROR_MEMORY_MAX_PATTERNS = 6;
5
+ const CORE_PATTERN_NUMBERS = new Set([4, 9, 10, 11]);
6
+ const STOP_WORDS = new Set([
7
+ 'aber', 'alle', 'alles', 'auch', 'belegt', 'beweis', 'echt', 'echte', 'echten',
8
+ 'echter', 'eine', 'einem', 'einen', 'einer', 'das', 'den', 'fuer', 'gegen',
9
+ 'haben', 'hier', 'immer',
10
+ 'jetzt', 'kann', 'machen', 'mehr', 'nicht', 'noch', 'oder', 'pruefe', 'pruefen',
11
+ 'pruefung', 'sein', 'sind', 'soll', 'und', 'vollstaendig', 'vollstaendige',
12
+ 'wenn', 'werden', 'with', 'that',
13
+ 'this', 'from', 'have', 'into', 'your', 'you', 'the', 'and', 'for', 'are',
14
+ ]);
15
+
16
+ function normalizedTerms(value) {
17
+ return [...new Set(String(value || '')
18
+ .normalize('NFKD')
19
+ .replace(/\p{M}/gu, '')
20
+ .toLowerCase()
21
+ .match(/[\p{L}\p{N}_-]{3,}/gu) || [])]
22
+ .filter((term) => !STOP_WORDS.has(term));
23
+ }
24
+
25
+ function fieldMatchesTerm(fieldTerms, term) {
26
+ return fieldTerms.some((fieldTerm) => (
27
+ fieldTerm === term
28
+ || (term.length >= 6 && fieldTerm.includes(term))
29
+ || (fieldTerm.length >= 6 && term.includes(fieldTerm))
30
+ ));
31
+ }
32
+
33
+ function selectRelevantErrorMemoryPatterns(patterns, query, options = {}) {
34
+ const maxPatterns = Math.max(1, Number(options.maxPatterns) || ERROR_MEMORY_MAX_PATTERNS);
35
+ const source = Array.isArray(patterns) ? patterns.filter(Boolean) : [];
36
+ if (source.length <= maxPatterns) return source;
37
+
38
+ const queryTerms = normalizedTerms(query);
39
+ const ranked = source.map((pattern, index) => {
40
+ const title = normalizedTerms(pattern.title);
41
+ const rule = normalizedTerms(pattern.rule);
42
+ const check = normalizedTerms(pattern.check);
43
+ let score = 0;
44
+ for (const term of queryTerms) {
45
+ if (fieldMatchesTerm(title, term)) score += 4;
46
+ if (fieldMatchesTerm(rule, term)) score += 2;
47
+ if (fieldMatchesTerm(check, term)) score += 1;
48
+ }
49
+ return { index, pattern, score };
50
+ });
51
+ const selectedIndexes = new Set(ranked
52
+ .filter(({ pattern }) => CORE_PATTERN_NUMBERS.has(Number(pattern.number)))
53
+ .map(({ index }) => index));
54
+ ranked
55
+ .filter(({ index, score }) => !selectedIndexes.has(index) && score > 0)
56
+ .sort((left, right) => right.score - left.score || left.index - right.index)
57
+ .slice(0, Math.max(0, maxPatterns - selectedIndexes.size))
58
+ .forEach(({ index }) => selectedIndexes.add(index));
59
+
60
+ if (selectedIndexes.size === 0) {
61
+ source.slice(-Math.min(2, maxPatterns)).forEach((pattern) => {
62
+ selectedIndexes.add(source.indexOf(pattern));
63
+ });
64
+ }
65
+ return source.filter((pattern, index) => selectedIndexes.has(index)).slice(0, maxPatterns);
66
+ }
4
67
 
5
68
  function buildCompactErrorMemoryReminder(patterns) {
6
69
  const lines = [
@@ -42,7 +105,9 @@ function compactLegacyErrorMemoryReminder(text) {
42
105
  }
43
106
 
44
107
  module.exports = {
108
+ ERROR_MEMORY_MAX_PATTERNS,
45
109
  ERROR_MEMORY_MAX_CHARS,
46
110
  buildCompactErrorMemoryReminder,
47
111
  compactLegacyErrorMemoryReminder,
112
+ selectRelevantErrorMemoryPatterns,
48
113
  };
@@ -8,6 +8,7 @@ const REPEATED_RESPONSE_PLACEHOLDER = '[Historical repeated assistant response o
8
8
  const INTRA_MESSAGE_MIN_REPEATS = 3;
9
9
  const INTRA_MESSAGE_MIN_BLOCK_CHARS = 40;
10
10
  const INTRA_MESSAGE_MIN_SAVED_CHARS = 1_000;
11
+ const INTRA_MESSAGE_SIMILARITY_THRESHOLD = 0.88;
11
12
  const INTRA_MESSAGE_REPEAT_PLACEHOLDER = '[Exact repeated assistant paragraphs omitted from model projection; raw history preserved.]';
12
13
 
13
14
  function isUserPrompt(message) {
@@ -60,6 +61,17 @@ function responseSimilarity(left, right) {
60
61
  return (2 * intersection) / (leftBigrams.size + rightBigrams.size);
61
62
  }
62
63
 
64
+ function fencedBlockIndexes(blocks) {
65
+ const protectedIndexes = new Set();
66
+ let insideFence = false;
67
+ for (let index = 0; index < blocks.length; index += 1) {
68
+ const fenceCount = blocks[index].match(/(?:^|\n)\s*(?:```|~~~)/gu)?.length ?? 0;
69
+ if (insideFence || fenceCount > 0) protectedIndexes.add(index);
70
+ if (fenceCount % 2 === 1) insideFence = !insideFence;
71
+ }
72
+ return protectedIndexes;
73
+ }
74
+
63
75
  function hasUserPromptBetween(history, leftIndex, rightIndex) {
64
76
  for (let index = leftIndex + 1; index < rightIndex; index += 1) {
65
77
  if (isUserPrompt(history[index])) return true;
@@ -120,27 +132,31 @@ function compactRepeatedAssistantParagraphs(message) {
120
132
  )) return null;
121
133
 
122
134
  const text = textParts[0].text;
123
- if (/```|~~~/u.test(text)) return null;
124
135
  const blocks = text.split(/\n\s*\n/u).map((block) => block.trim()).filter(Boolean);
125
136
  if (blocks.length < INTRA_MESSAGE_MIN_REPEATS + 1) return null;
137
+ const protectedIndexes = fencedBlockIndexes(blocks);
138
+
139
+ const clusters = [];
140
+ for (let index = 0; index < blocks.length; index += 1) {
141
+ const block = blocks[index];
142
+ if (protectedIndexes.has(index) || block.length < INTRA_MESSAGE_MIN_BLOCK_CHARS) continue;
143
+ const cluster = clusters.find((candidate) => (
144
+ candidate.representative === block
145
+ || responseSimilarity(candidate.representative, block) >= INTRA_MESSAGE_SIMILARITY_THRESHOLD
146
+ ));
147
+ if (cluster) cluster.indexes.push(index);
148
+ else clusters.push({ indexes: [index], representative: block });
149
+ }
150
+ const repeatedIndexes = new Set(clusters
151
+ .filter((cluster) => cluster.indexes.length >= INTRA_MESSAGE_MIN_REPEATS)
152
+ .flatMap((cluster) => cluster.indexes.slice(1)));
153
+ if (repeatedIndexes.size === 0) return null;
126
154
 
127
- const counts = new Map();
128
- for (const block of blocks) counts.set(block, (counts.get(block) ?? 0) + 1);
129
- const repeated = new Set([...counts]
130
- .filter(([block, count]) => (
131
- block.length >= INTRA_MESSAGE_MIN_BLOCK_CHARS
132
- && count >= INTRA_MESSAGE_MIN_REPEATS
133
- ))
134
- .map(([block]) => block));
135
- if (repeated.size === 0) return null;
136
-
137
- const seen = new Set();
138
155
  const projectedBlocks = [];
139
156
  let markerAdded = false;
140
- for (const block of blocks) {
141
- if (!repeated.has(block) || !seen.has(block)) {
142
- projectedBlocks.push(block);
143
- if (repeated.has(block)) seen.add(block);
157
+ for (let index = 0; index < blocks.length; index += 1) {
158
+ if (!repeatedIndexes.has(index)) {
159
+ projectedBlocks.push(blocks[index]);
144
160
  continue;
145
161
  }
146
162
  if (!markerAdded) {
@@ -184,6 +200,7 @@ module.exports = {
184
200
  INTRA_MESSAGE_MIN_BLOCK_CHARS,
185
201
  INTRA_MESSAGE_MIN_REPEATS,
186
202
  INTRA_MESSAGE_MIN_SAVED_CHARS,
203
+ INTRA_MESSAGE_SIMILARITY_THRESHOLD,
187
204
  INTRA_MESSAGE_REPEAT_PLACEHOLDER,
188
205
  MAX_RECENT_ASSISTANT_RESPONSES,
189
206
  MIN_RESPONSE_CHARS,
package/blun.mjs CHANGED
@@ -79260,6 +79260,11 @@ var init_error_memory$1 = __esmMin((() => {
79260
79260
  getInjection() {
79261
79261
  const patterns = this.agent.errorMemory?.getActivePatterns();
79262
79262
  if (!patterns || patterns.length === 0) return void 0;
79263
+ try {
79264
+ const { buildCompactErrorMemoryReminder, selectRelevantErrorMemoryPatterns } = createRequire(import.meta.url)("./bin/error-memory-performance-policy.cjs");
79265
+ const { recentUserText } = createRequire(import.meta.url)("./bin/mistake-relevance-policy.cjs");
79266
+ return buildCompactErrorMemoryReminder(selectRelevantErrorMemoryPatterns(patterns, recentUserText(this.agent.context.history)));
79267
+ } catch {}
79263
79268
  return buildErrorMemoryReminder(patterns);
79264
79269
  }
79265
79270
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.203",
3
+ "version": "9.1.205",
4
4
  "description": "BLUN CLI - your own AI agent with a Telegram channel. Get it done. With BLUN.",
5
5
  "license": "MIT",
6
6
  "bin": {