@dzhechkov/memory 0.2.6 → 0.2.7

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.
@@ -55,7 +55,10 @@ export declare function isNoiseInsight(text: string): boolean;
55
55
  * NOISE GATE (a real-store audit found ~85 of 91 records were junk):
56
56
  * - Tool invocations are NOT harvested — a `tool_use` is telemetry, not a learning.
57
57
  * - System wrappers arriving as user messages ({@link isSystemWrapper}) are skipped.
58
- * - Substring classification only fires on checkpoint-like messages (≤ 200 chars).
58
+ * - Verdict classification only fires on checkpoint-like messages (≤ 200 chars,
59
+ * ≤ 100 for failed) and only on ANCHORED standalone words/phrases, never bare
60
+ * substrings — "в stop hook" and "не изобретать заново" are live-store
61
+ * misfires that bare substrings produced.
59
62
  */
60
63
  export declare function harvestDreamPatterns(options: DreamOptions): DreamPattern[];
61
64
  /** Convert a DreamPattern to a MemoryRecord for storage via any MemoryBackend. */
@@ -1 +1 @@
1
- {"version":3,"file":"dreaming.d.ts","sourceRoot":"","sources":["../src/dreaming.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,qDAAqD;AACrD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,GAAG,YAAY,GAAG,QAAQ,CAAC;IACjE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,uCAAuC;AACvC,MAAM,WAAW,YAAY;IAC3B,0DAA0D;IAC1D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAqBD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOrD;AAiCD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOpD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,YAAY,GAAG,YAAY,EAAE,CAqE1E;AAaD,kFAAkF;AAClF,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,YAAY,GAAG,YAAY,CAWxE"}
1
+ {"version":3,"file":"dreaming.d.ts","sourceRoot":"","sources":["../src/dreaming.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,qDAAqD;AACrD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,GAAG,YAAY,GAAG,QAAQ,CAAC;IACjE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,uCAAuC;AACvC,MAAM,WAAW,YAAY;IAC3B,0DAA0D;IAC1D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAqBD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOrD;AAyGD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOpD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,YAAY,GAAG,YAAY,EAAE,CAyE1E;AAaD,kFAAkF;AAClF,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,YAAY,GAAG,YAAY,CAWxE"}
package/dist/dreaming.js CHANGED
@@ -61,14 +61,73 @@ const EXCELLENT_RESPONSES = new Set([
61
61
  ]);
62
62
  /**
63
63
  * Only messages this short are considered checkpoint-like for the
64
- * needs_work/failed substring checks. Longer messages routinely QUOTE words
64
+ * needs_work/failed verdict checks. Longer messages routinely QUOTE words
65
65
  * like 'stop' or 'переделай' without meaning them as verdicts.
66
66
  */
67
67
  const CHECKPOINT_MAX_LENGTH = 200;
68
+ /**
69
+ * A `failed@0.0` verdict poisons the reward store, so it demands the tightest
70
+ * gate: real rejections ("стоп", "stop — wrong direction") are terse. Anything
71
+ * longer than this is an instruction that happens to contain a rejection word.
72
+ */
73
+ const FAILED_MAX_LENGTH = 100;
68
74
  /** Normalize a checkpoint response: trim, lowercase, strip trailing punctuation. */
69
75
  function normalizeCheckpointText(text) {
70
76
  return text.trim().toLowerCase().replace(/[\s.!…]+$/u, '');
71
77
  }
78
+ /** Escape a literal string for embedding in a RegExp source. */
79
+ function escapeRegExp(literal) {
80
+ return literal.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
81
+ }
82
+ /**
83
+ * Unicode-aware "standalone word" boundary, as lookarounds.
84
+ *
85
+ * CRITICAL: JS `\b` is ASCII-only — `\bпеределай\b` NEVER matches Cyrillic
86
+ * text because Cyrillic letters are non-word characters for `\b` (both sides
87
+ * of the "boundary" are non-word, so there is no boundary). Every RU/EN
88
+ * keyword must go through these lookarounds instead; no raw `\b` anywhere.
89
+ *
90
+ * The hyphen is treated as a joiner so glued/compound forms do not match:
91
+ * "напеределай", "stopwatch", "restop", "стоп-кран", "wrong-таки".
92
+ */
93
+ const WORD_BEFORE = '(?<![\\p{L}\\p{N}-])';
94
+ const WORD_AFTER = '(?![\\p{L}\\p{N}-])';
95
+ /** True when `word` occurs in `text` as a standalone word (Unicode-aware). */
96
+ function hasStandaloneWord(text, word) {
97
+ const re = new RegExp(`${WORD_BEFORE}${escapeRegExp(word)}${WORD_AFTER}`, 'iu');
98
+ return re.test(text);
99
+ }
100
+ /**
101
+ * failed@0.0 — only when rejection is the ESSENCE of the message: the
102
+ * normalized text STARTS with a standalone rejection anchor. Mid-sentence
103
+ * mentions ("можешь добавить в stop hook…", "wrong-таки было") must NOT fire —
104
+ * a real misfire from the live store classified a normal instruction about a
105
+ * "stop hook" as failed@0.0.
106
+ *
107
+ * Documented choice: "стоп машина" starts with standalone "стоп" and DOES
108
+ * classify as failed — leading "стоп" as a word is accepted as a halt order.
109
+ *
110
+ * Applied to the normalized (trimmed, lowercased, punctuation-stripped) text.
111
+ */
112
+ const FAILED_LEADING_RE = new RegExp(`^(?:стоп|stop|wrong|(?:это\\s+|вс[её]\\s+)?неправильно)${WORD_AFTER}`, 'u');
113
+ /**
114
+ * needs_work@0.3 — only ANCHORED imperative rework phrases, never bare
115
+ * keywords. Standalone-word matches anywhere in the message.
116
+ */
117
+ const NEEDS_WORK_WORDS = [
118
+ 'переделай',
119
+ 'переделать',
120
+ 'redo',
121
+ 'rework',
122
+ 'start over',
123
+ ];
124
+ /**
125
+ * 'заново' fires ONLY inside imperative collocations ("сделай всё заново",
126
+ * "начни заново"). Bare 'заново' — and especially the idiom "не изобретать
127
+ * заново" (don't reinvent the wheel; a real misfire from the live store) —
128
+ * must NOT fire.
129
+ */
130
+ const ZANOVO_IMPERATIVE_RE = new RegExp(`${WORD_BEFORE}(?:сделай|сделать|начни|начать|давай|начн[её]м)\\s+(?:вс[её]\\s+)?заново${WORD_AFTER}`, 'iu');
72
131
  /**
73
132
  * True for insights matching the LEGACY noise this harvester used to emit,
74
133
  * so a prune step can retro-clean existing stores:
@@ -98,7 +157,10 @@ export function isNoiseInsight(text) {
98
157
  * NOISE GATE (a real-store audit found ~85 of 91 records were junk):
99
158
  * - Tool invocations are NOT harvested — a `tool_use` is telemetry, not a learning.
100
159
  * - System wrappers arriving as user messages ({@link isSystemWrapper}) are skipped.
101
- * - Substring classification only fires on checkpoint-like messages (≤ 200 chars).
160
+ * - Verdict classification only fires on checkpoint-like messages (≤ 200 chars,
161
+ * ≤ 100 for failed) and only on ANCHORED standalone words/phrases, never bare
162
+ * substrings — "в stop hook" and "не изобретать заново" are live-store
163
+ * misfires that bare substrings produced.
102
164
  */
103
165
  export function harvestDreamPatterns(options) {
104
166
  const { sessionsDir, since } = options;
@@ -135,17 +197,19 @@ export function harvestDreamPatterns(options) {
135
197
  score = 1.0;
136
198
  }
137
199
  else if (trimmed.length <= CHECKPOINT_MAX_LENGTH) {
138
- // Substring verdicts only on checkpoint-like (short) messages;
139
- // long messages quote words like 'stop' without meaning them.
140
- const text = trimmed.toLowerCase();
141
- if (text.includes('переделай') || text.includes('rework') || text.includes('заново')) {
142
- outcome = 'needs_work';
143
- score = 0.3;
144
- }
145
- else if (text.includes('стоп') || text.includes('stop') || text.includes('wrong')) {
200
+ // Anchored verdicts only on checkpoint-like (short) messages.
201
+ // PRECISION over recall: a missed downgrade costs nothing (the
202
+ // 'good' outcome is not recorded), a false failed@0.0 poisons the
203
+ // reward store. When in doubt, do not classify.
204
+ if (trimmed.length <= FAILED_MAX_LENGTH && FAILED_LEADING_RE.test(normalized)) {
146
205
  outcome = 'failed';
147
206
  score = 0.0;
148
207
  }
208
+ else if (NEEDS_WORK_WORDS.some((word) => hasStandaloneWord(trimmed, word)) ||
209
+ ZANOVO_IMPERATIVE_RE.test(trimmed)) {
210
+ outcome = 'needs_work';
211
+ score = 0.3;
212
+ }
149
213
  }
150
214
  if (outcome !== 'good') {
151
215
  patterns.push({
@@ -1 +1 @@
1
- {"version":3,"file":"dreaming.js","sourceRoot":"","sources":["../src/dreaming.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dreaming.js","sourceRoot":"","sources":["../src/dreaming.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dzhechkov/memory",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "description": "Harness memory layer - backend cascade, Reflexion feedback, and the host-memory bridge.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/dreaming.ts CHANGED
@@ -89,16 +89,88 @@ const EXCELLENT_RESPONSES: ReadonlySet<string> = new Set([
89
89
 
90
90
  /**
91
91
  * Only messages this short are considered checkpoint-like for the
92
- * needs_work/failed substring checks. Longer messages routinely QUOTE words
92
+ * needs_work/failed verdict checks. Longer messages routinely QUOTE words
93
93
  * like 'stop' or 'переделай' without meaning them as verdicts.
94
94
  */
95
95
  const CHECKPOINT_MAX_LENGTH = 200;
96
96
 
97
+ /**
98
+ * A `failed@0.0` verdict poisons the reward store, so it demands the tightest
99
+ * gate: real rejections ("стоп", "stop — wrong direction") are terse. Anything
100
+ * longer than this is an instruction that happens to contain a rejection word.
101
+ */
102
+ const FAILED_MAX_LENGTH = 100;
103
+
97
104
  /** Normalize a checkpoint response: trim, lowercase, strip trailing punctuation. */
98
105
  function normalizeCheckpointText(text: string): string {
99
106
  return text.trim().toLowerCase().replace(/[\s.!…]+$/u, '');
100
107
  }
101
108
 
109
+ /** Escape a literal string for embedding in a RegExp source. */
110
+ function escapeRegExp(literal: string): string {
111
+ return literal.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
112
+ }
113
+
114
+ /**
115
+ * Unicode-aware "standalone word" boundary, as lookarounds.
116
+ *
117
+ * CRITICAL: JS `\b` is ASCII-only — `\bпеределай\b` NEVER matches Cyrillic
118
+ * text because Cyrillic letters are non-word characters for `\b` (both sides
119
+ * of the "boundary" are non-word, so there is no boundary). Every RU/EN
120
+ * keyword must go through these lookarounds instead; no raw `\b` anywhere.
121
+ *
122
+ * The hyphen is treated as a joiner so glued/compound forms do not match:
123
+ * "напеределай", "stopwatch", "restop", "стоп-кран", "wrong-таки".
124
+ */
125
+ const WORD_BEFORE = '(?<![\\p{L}\\p{N}-])';
126
+ const WORD_AFTER = '(?![\\p{L}\\p{N}-])';
127
+
128
+ /** True when `word` occurs in `text` as a standalone word (Unicode-aware). */
129
+ function hasStandaloneWord(text: string, word: string): boolean {
130
+ const re = new RegExp(`${WORD_BEFORE}${escapeRegExp(word)}${WORD_AFTER}`, 'iu');
131
+ return re.test(text);
132
+ }
133
+
134
+ /**
135
+ * failed@0.0 — only when rejection is the ESSENCE of the message: the
136
+ * normalized text STARTS with a standalone rejection anchor. Mid-sentence
137
+ * mentions ("можешь добавить в stop hook…", "wrong-таки было") must NOT fire —
138
+ * a real misfire from the live store classified a normal instruction about a
139
+ * "stop hook" as failed@0.0.
140
+ *
141
+ * Documented choice: "стоп машина" starts with standalone "стоп" and DOES
142
+ * classify as failed — leading "стоп" as a word is accepted as a halt order.
143
+ *
144
+ * Applied to the normalized (trimmed, lowercased, punctuation-stripped) text.
145
+ */
146
+ const FAILED_LEADING_RE = new RegExp(
147
+ `^(?:стоп|stop|wrong|(?:это\\s+|вс[её]\\s+)?неправильно)${WORD_AFTER}`,
148
+ 'u',
149
+ );
150
+
151
+ /**
152
+ * needs_work@0.3 — only ANCHORED imperative rework phrases, never bare
153
+ * keywords. Standalone-word matches anywhere in the message.
154
+ */
155
+ const NEEDS_WORK_WORDS: readonly string[] = [
156
+ 'переделай',
157
+ 'переделать',
158
+ 'redo',
159
+ 'rework',
160
+ 'start over',
161
+ ];
162
+
163
+ /**
164
+ * 'заново' fires ONLY inside imperative collocations ("сделай всё заново",
165
+ * "начни заново"). Bare 'заново' — and especially the idiom "не изобретать
166
+ * заново" (don't reinvent the wheel; a real misfire from the live store) —
167
+ * must NOT fire.
168
+ */
169
+ const ZANOVO_IMPERATIVE_RE = new RegExp(
170
+ `${WORD_BEFORE}(?:сделай|сделать|начни|начать|давай|начн[её]м)\\s+(?:вс[её]\\s+)?заново${WORD_AFTER}`,
171
+ 'iu',
172
+ );
173
+
102
174
  /**
103
175
  * True for insights matching the LEGACY noise this harvester used to emit,
104
176
  * so a prune step can retro-clean existing stores:
@@ -128,7 +200,10 @@ export function isNoiseInsight(text: string): boolean {
128
200
  * NOISE GATE (a real-store audit found ~85 of 91 records were junk):
129
201
  * - Tool invocations are NOT harvested — a `tool_use` is telemetry, not a learning.
130
202
  * - System wrappers arriving as user messages ({@link isSystemWrapper}) are skipped.
131
- * - Substring classification only fires on checkpoint-like messages (≤ 200 chars).
203
+ * - Verdict classification only fires on checkpoint-like messages (≤ 200 chars,
204
+ * ≤ 100 for failed) and only on ANCHORED standalone words/phrases, never bare
205
+ * substrings — "в stop hook" and "не изобретать заново" are live-store
206
+ * misfires that bare substrings produced.
132
207
  */
133
208
  export function harvestDreamPatterns(options: DreamOptions): DreamPattern[] {
134
209
  const { sessionsDir, since } = options;
@@ -169,15 +244,19 @@ export function harvestDreamPatterns(options: DreamOptions): DreamPattern[] {
169
244
  outcome = 'excellent';
170
245
  score = 1.0;
171
246
  } else if (trimmed.length <= CHECKPOINT_MAX_LENGTH) {
172
- // Substring verdicts only on checkpoint-like (short) messages;
173
- // long messages quote words like 'stop' without meaning them.
174
- const text = trimmed.toLowerCase();
175
- if (text.includes('переделай') || text.includes('rework') || text.includes('заново')) {
176
- outcome = 'needs_work';
177
- score = 0.3;
178
- } else if (text.includes('стоп') || text.includes('stop') || text.includes('wrong')) {
247
+ // Anchored verdicts only on checkpoint-like (short) messages.
248
+ // PRECISION over recall: a missed downgrade costs nothing (the
249
+ // 'good' outcome is not recorded), a false failed@0.0 poisons the
250
+ // reward store. When in doubt, do not classify.
251
+ if (trimmed.length <= FAILED_MAX_LENGTH && FAILED_LEADING_RE.test(normalized)) {
179
252
  outcome = 'failed';
180
253
  score = 0.0;
254
+ } else if (
255
+ NEEDS_WORK_WORDS.some((word) => hasStandaloneWord(trimmed, word)) ||
256
+ ZANOVO_IMPERATIVE_RE.test(trimmed)
257
+ ) {
258
+ outcome = 'needs_work';
259
+ score = 0.3;
181
260
  }
182
261
  }
183
262