@dzhechkov/memory 0.2.6 → 0.2.9

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.
@@ -35,12 +35,19 @@ export interface DreamOptions {
35
35
  */
36
36
  export declare function isSystemWrapper(text: string): boolean;
37
37
  /**
38
- * True for insights matching the LEGACY noise this harvester used to emit,
39
- * so a prune step can retro-clean existing stores:
38
+ * True for insights that are NOISE rather than reusable learnings, so both the
39
+ * ingest gate and the retro-prune step can drop them:
40
40
  *
41
41
  * - `Tool mcp__* invoked during session` (telemetry, no longer harvested)
42
42
  * - `User responded: "<system wrapper…"` for every wrapper prefix recognized
43
43
  * by {@link isSystemWrapper}, including session-continuation forms.
44
+ * - `User responded: "<bare approval>"` — a checkpoint approval echoed as a
45
+ * pattern (e.g. `продолжай`, `ok`, `да`). These are REWARD signal (which
46
+ * phase was approved), captured separately; as a recallable "pattern" they
47
+ * carry zero reusable insight and clutter `dz recall`. Gated by EXACT match
48
+ * (after {@link normalizeCheckpointText}) against {@link EXCELLENT_RESPONSES},
49
+ * so a SUBSTANTIVE response ("продолжай, но добавь X") is NOT flagged and is
50
+ * preserved — it may carry real signal.
44
51
  *
45
52
  * This predicate is the shared contract for the prune step.
46
53
  */
@@ -55,7 +62,10 @@ export declare function isNoiseInsight(text: string): boolean;
55
62
  * NOISE GATE (a real-store audit found ~85 of 91 records were junk):
56
63
  * - Tool invocations are NOT harvested — a `tool_use` is telemetry, not a learning.
57
64
  * - System wrappers arriving as user messages ({@link isSystemWrapper}) are skipped.
58
- * - Substring classification only fires on checkpoint-like messages (≤ 200 chars).
65
+ * - Verdict classification only fires on checkpoint-like messages (≤ 200 chars,
66
+ * ≤ 100 for failed) and only on ANCHORED standalone words/phrases, never bare
67
+ * substrings — "в stop hook" and "не изобретать заново" are live-store
68
+ * misfires that bare substrings produced.
59
69
  */
60
70
  export declare function harvestDreamPatterns(options: DreamOptions): DreamPattern[];
61
71
  /** 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;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAcpD;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,21 +61,87 @@ 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
+ }
72
100
  /**
73
- * True for insights matching the LEGACY noise this harvester used to emit,
74
- * so a prune step can retro-clean existing stores:
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');
131
+ /**
132
+ * True for insights that are NOISE rather than reusable learnings, so both the
133
+ * ingest gate and the retro-prune step can drop them:
75
134
  *
76
135
  * - `Tool mcp__* invoked during session` (telemetry, no longer harvested)
77
136
  * - `User responded: "<system wrapper…"` for every wrapper prefix recognized
78
137
  * by {@link isSystemWrapper}, including session-continuation forms.
138
+ * - `User responded: "<bare approval>"` — a checkpoint approval echoed as a
139
+ * pattern (e.g. `продолжай`, `ok`, `да`). These are REWARD signal (which
140
+ * phase was approved), captured separately; as a recallable "pattern" they
141
+ * carry zero reusable insight and clutter `dz recall`. Gated by EXACT match
142
+ * (after {@link normalizeCheckpointText}) against {@link EXCELLENT_RESPONSES},
143
+ * so a SUBSTANTIVE response ("продолжай, но добавь X") is NOT flagged and is
144
+ * preserved — it may carry real signal.
79
145
  *
80
146
  * This predicate is the shared contract for the prune step.
81
147
  */
@@ -84,7 +150,16 @@ export function isNoiseInsight(text) {
84
150
  return true;
85
151
  const userPrefix = 'User responded: "';
86
152
  if (text.startsWith(userPrefix)) {
87
- return isSystemWrapper(text.slice(userPrefix.length));
153
+ const inner = text.slice(userPrefix.length);
154
+ if (isSystemWrapper(inner))
155
+ return true;
156
+ // Strip the trailing quote of the `User responded: "…"` wrapper, then
157
+ // normalize and require an EXACT match — reuses the existing approval
158
+ // vocabulary instead of duplicating it.
159
+ const approval = inner.replace(/"$/u, '');
160
+ if (EXCELLENT_RESPONSES.has(normalizeCheckpointText(approval)))
161
+ return true;
162
+ return false;
88
163
  }
89
164
  return false;
90
165
  }
@@ -98,7 +173,10 @@ export function isNoiseInsight(text) {
98
173
  * NOISE GATE (a real-store audit found ~85 of 91 records were junk):
99
174
  * - Tool invocations are NOT harvested — a `tool_use` is telemetry, not a learning.
100
175
  * - System wrappers arriving as user messages ({@link isSystemWrapper}) are skipped.
101
- * - Substring classification only fires on checkpoint-like messages (≤ 200 chars).
176
+ * - Verdict classification only fires on checkpoint-like messages (≤ 200 chars,
177
+ * ≤ 100 for failed) and only on ANCHORED standalone words/phrases, never bare
178
+ * substrings — "в stop hook" and "не изобретать заново" are live-store
179
+ * misfires that bare substrings produced.
102
180
  */
103
181
  export function harvestDreamPatterns(options) {
104
182
  const { sessionsDir, since } = options;
@@ -135,17 +213,19 @@ export function harvestDreamPatterns(options) {
135
213
  score = 1.0;
136
214
  }
137
215
  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')) {
216
+ // Anchored verdicts only on checkpoint-like (short) messages.
217
+ // PRECISION over recall: a missed downgrade costs nothing (the
218
+ // 'good' outcome is not recorded), a false failed@0.0 poisons the
219
+ // reward store. When in doubt, do not classify.
220
+ if (trimmed.length <= FAILED_MAX_LENGTH && FAILED_LEADING_RE.test(normalized)) {
146
221
  outcome = 'failed';
147
222
  score = 0.0;
148
223
  }
224
+ else if (NEEDS_WORK_WORDS.some((word) => hasStandaloneWord(trimmed, word)) ||
225
+ ZANOVO_IMPERATIVE_RE.test(trimmed)) {
226
+ outcome = 'needs_work';
227
+ score = 0.3;
228
+ }
149
229
  }
150
230
  if (outcome !== 'good') {
151
231
  patterns.push({
@@ -1 +1 @@
1
- {"version":3,"file":"dreaming.js","sourceRoot":"","sources":["../src/dreaming.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAsBjC;;;;GAIG;AACH,MAAM,uBAAuB,GAAsB;IACjD,oBAAoB;IACpB,kBAAkB;IAClB,uBAAuB;IACvB,eAAe;IACf,aAAa;IACb,cAAc;IACd,sBAAsB;IACtB,sBAAsB;CACvB,CAAC;AAEF,0EAA0E;AAC1E,MAAM,uBAAuB,GAAG,mCAAmC,CAAC;AAEpE;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IACpC,OAAO,CACL,uBAAuB,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QAClE,uBAAuB,CAAC,IAAI,CAAC,OAAO,CAAC,CACtC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,mBAAmB,GAAwB,IAAI,GAAG,CAAC;IACvD,IAAI;IACJ,IAAI;IACJ,MAAM;IACN,WAAW;IACX,IAAI;IACJ,QAAQ;IACR,SAAS;IACT,QAAQ;IACR,SAAS;IACT,MAAM;IACN,MAAM;IACN,eAAe;CAChB,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAElC,oFAAoF;AACpF,SAAS,uBAAuB,CAAC,IAAY;IAC3C,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,IAAI,uCAAuC,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACpE,MAAM,UAAU,GAAG,mBAAmB,CAAC;IACvC,IAAI,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAChC,OAAO,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAqB;IACxD,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IACvC,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO,EAAE,CAAC;IAExC,MAAM,QAAQ,GAAmB,EAAE,CAAC;IACpC,MAAM,KAAK,GAAG,WAAW,CAAC,WAAW,CAAC;SACnC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;SACnC,IAAI,EAAE,CAAC;IAEV,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QACzC,MAAM,KAAK,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAE7F,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAE/B,kCAAkC;gBAClC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,GAAG,KAAK;oBAAE,SAAS;gBAEhF,iEAAiE;gBACjE,mEAAmE;gBACnE,gDAAgD;gBAEhD,gDAAgD;gBAChD,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,KAAK,CAAC,OAAO,EAAE,OAAO,KAAK,QAAQ,EAAE,CAAC;oBACxE,MAAM,GAAG,GAAW,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;oBAE1C,+DAA+D;oBAC/D,IAAI,eAAe,CAAC,GAAG,CAAC;wBAAE,SAAS;oBAEnC,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;oBAC3B,MAAM,UAAU,GAAG,uBAAuB,CAAC,GAAG,CAAC,CAAC;oBAChD,IAAI,OAAO,GAA4B,MAAM,CAAC;oBAC9C,IAAI,KAAK,GAAG,GAAG,CAAC;oBAChB,IAAI,mBAAmB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;wBACxC,OAAO,GAAG,WAAW,CAAC;wBACtB,KAAK,GAAG,GAAG,CAAC;oBACd,CAAC;yBAAM,IAAI,OAAO,CAAC,MAAM,IAAI,qBAAqB,EAAE,CAAC;wBACnD,+DAA+D;wBAC/D,8DAA8D;wBAC9D,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;wBACnC,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;4BACrF,OAAO,GAAG,YAAY,CAAC;4BACvB,KAAK,GAAG,GAAG,CAAC;wBACd,CAAC;6BAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;4BACpF,OAAO,GAAG,QAAQ,CAAC;4BACnB,KAAK,GAAG,GAAG,CAAC;wBACd,CAAC;oBACH,CAAC;oBAED,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;wBACvB,QAAQ,CAAC,IAAI,CAAC;4BACZ,OAAO,EAAE,qBAAqB;4BAC9B,OAAO;4BACP,KAAK;4BACL,OAAO,EAAE,oBAAoB,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG;4BACnE,WAAW,EAAE,IAAI;4BACjB,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;yBACvD,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,6BAA6B;YAC/B,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;;GAQG;AACH,IAAI,cAAc,GAAG,CAAC,CAAC;AAEvB,kFAAkF;AAClF,MAAM,UAAU,oBAAoB,CAAC,OAAqB;IACxD,MAAM,GAAG,GAAG,CAAC,cAAc,GAAG,CAAC,cAAc,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;IAC9E,OAAO;QACL,EAAE,EAAE,SAAS,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,GAAG,EAAE;QAC1E,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,IAAI,EAAE,OAAO,CAAC,OAAO;QACrB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,QAAQ,EAAE,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE;KAC/C,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"dreaming.js","sourceRoot":"","sources":["../src/dreaming.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAsBjC;;;;GAIG;AACH,MAAM,uBAAuB,GAAsB;IACjD,oBAAoB;IACpB,kBAAkB;IAClB,uBAAuB;IACvB,eAAe;IACf,aAAa;IACb,cAAc;IACd,sBAAsB;IACtB,sBAAsB;CACvB,CAAC;AAEF,0EAA0E;AAC1E,MAAM,uBAAuB,GAAG,mCAAmC,CAAC;AAEpE;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IACpC,OAAO,CACL,uBAAuB,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QAClE,uBAAuB,CAAC,IAAI,CAAC,OAAO,CAAC,CACtC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,mBAAmB,GAAwB,IAAI,GAAG,CAAC;IACvD,IAAI;IACJ,IAAI;IACJ,MAAM;IACN,WAAW;IACX,IAAI;IACJ,QAAQ;IACR,SAAS;IACT,QAAQ;IACR,SAAS;IACT,MAAM;IACN,MAAM;IACN,eAAe;CAChB,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAElC;;;;GAIG;AACH,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAE9B,oFAAoF;AACpF,SAAS,uBAAuB,CAAC,IAAY;IAC3C,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED,gEAAgE;AAChE,SAAS,YAAY,CAAC,OAAe;IACnC,OAAO,OAAO,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AACxD,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,GAAG,sBAAsB,CAAC;AAC3C,MAAM,UAAU,GAAG,qBAAqB,CAAC;AAEzC,8EAA8E;AAC9E,SAAS,iBAAiB,CAAC,IAAY,EAAE,IAAY;IACnD,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,GAAG,WAAW,GAAG,YAAY,CAAC,IAAI,CAAC,GAAG,UAAU,EAAE,EAAE,IAAI,CAAC,CAAC;IAChF,OAAO,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACvB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,iBAAiB,GAAG,IAAI,MAAM,CAClC,0DAA0D,UAAU,EAAE,EACtE,GAAG,CACJ,CAAC;AAEF;;;GAGG;AACH,MAAM,gBAAgB,GAAsB;IAC1C,WAAW;IACX,YAAY;IACZ,MAAM;IACN,QAAQ;IACR,YAAY;CACb,CAAC;AAEF;;;;;GAKG;AACH,MAAM,oBAAoB,GAAG,IAAI,MAAM,CACrC,GAAG,WAAW,2EAA2E,UAAU,EAAE,EACrG,IAAI,CACL,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,IAAI,uCAAuC,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACpE,MAAM,UAAU,GAAG,mBAAmB,CAAC;IACvC,IAAI,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAChC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QAC5C,IAAI,eAAe,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACxC,sEAAsE;QACtE,sEAAsE;QACtE,wCAAwC;QACxC,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC1C,IAAI,mBAAmB,CAAC,GAAG,CAAC,uBAAuB,CAAC,QAAQ,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QAC5E,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAqB;IACxD,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IACvC,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO,EAAE,CAAC;IAExC,MAAM,QAAQ,GAAmB,EAAE,CAAC;IACpC,MAAM,KAAK,GAAG,WAAW,CAAC,WAAW,CAAC;SACnC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;SACnC,IAAI,EAAE,CAAC;IAEV,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QACzC,MAAM,KAAK,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAE7F,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAE/B,kCAAkC;gBAClC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,GAAG,KAAK;oBAAE,SAAS;gBAEhF,iEAAiE;gBACjE,mEAAmE;gBACnE,gDAAgD;gBAEhD,gDAAgD;gBAChD,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,KAAK,CAAC,OAAO,EAAE,OAAO,KAAK,QAAQ,EAAE,CAAC;oBACxE,MAAM,GAAG,GAAW,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;oBAE1C,+DAA+D;oBAC/D,IAAI,eAAe,CAAC,GAAG,CAAC;wBAAE,SAAS;oBAEnC,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;oBAC3B,MAAM,UAAU,GAAG,uBAAuB,CAAC,GAAG,CAAC,CAAC;oBAChD,IAAI,OAAO,GAA4B,MAAM,CAAC;oBAC9C,IAAI,KAAK,GAAG,GAAG,CAAC;oBAChB,IAAI,mBAAmB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;wBACxC,OAAO,GAAG,WAAW,CAAC;wBACtB,KAAK,GAAG,GAAG,CAAC;oBACd,CAAC;yBAAM,IAAI,OAAO,CAAC,MAAM,IAAI,qBAAqB,EAAE,CAAC;wBACnD,8DAA8D;wBAC9D,+DAA+D;wBAC/D,kEAAkE;wBAClE,gDAAgD;wBAChD,IAAI,OAAO,CAAC,MAAM,IAAI,iBAAiB,IAAI,iBAAiB,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;4BAC9E,OAAO,GAAG,QAAQ,CAAC;4BACnB,KAAK,GAAG,GAAG,CAAC;wBACd,CAAC;6BAAM,IACL,gBAAgB,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,iBAAiB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;4BACjE,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,EAClC,CAAC;4BACD,OAAO,GAAG,YAAY,CAAC;4BACvB,KAAK,GAAG,GAAG,CAAC;wBACd,CAAC;oBACH,CAAC;oBAED,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;wBACvB,QAAQ,CAAC,IAAI,CAAC;4BACZ,OAAO,EAAE,qBAAqB;4BAC9B,OAAO;4BACP,KAAK;4BACL,OAAO,EAAE,oBAAoB,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG;4BACnE,WAAW,EAAE,IAAI;4BACjB,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;yBACvD,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,6BAA6B;YAC/B,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;;GAQG;AACH,IAAI,cAAc,GAAG,CAAC,CAAC;AAEvB,kFAAkF;AAClF,MAAM,UAAU,oBAAoB,CAAC,OAAqB;IACxD,MAAM,GAAG,GAAG,CAAC,cAAc,GAAG,CAAC,cAAc,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;IAC9E,OAAO;QACL,EAAE,EAAE,SAAS,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,GAAG,EAAE;QAC1E,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,IAAI,EAAE,OAAO,CAAC,OAAO;QACrB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,QAAQ,EAAE,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE;KAC/C,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dzhechkov/memory",
3
- "version": "0.2.6",
3
+ "version": "0.2.9",
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,23 +89,102 @@ 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
+
102
163
  /**
103
- * True for insights matching the LEGACY noise this harvester used to emit,
104
- * so a prune step can retro-clean existing stores:
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
+
174
+ /**
175
+ * True for insights that are NOISE rather than reusable learnings, so both the
176
+ * ingest gate and the retro-prune step can drop them:
105
177
  *
106
178
  * - `Tool mcp__* invoked during session` (telemetry, no longer harvested)
107
179
  * - `User responded: "<system wrapper…"` for every wrapper prefix recognized
108
180
  * by {@link isSystemWrapper}, including session-continuation forms.
181
+ * - `User responded: "<bare approval>"` — a checkpoint approval echoed as a
182
+ * pattern (e.g. `продолжай`, `ok`, `да`). These are REWARD signal (which
183
+ * phase was approved), captured separately; as a recallable "pattern" they
184
+ * carry zero reusable insight and clutter `dz recall`. Gated by EXACT match
185
+ * (after {@link normalizeCheckpointText}) against {@link EXCELLENT_RESPONSES},
186
+ * so a SUBSTANTIVE response ("продолжай, но добавь X") is NOT flagged and is
187
+ * preserved — it may carry real signal.
109
188
  *
110
189
  * This predicate is the shared contract for the prune step.
111
190
  */
@@ -113,7 +192,14 @@ export function isNoiseInsight(text: string): boolean {
113
192
  if (/^Tool mcp__.+ invoked during session$/.test(text)) return true;
114
193
  const userPrefix = 'User responded: "';
115
194
  if (text.startsWith(userPrefix)) {
116
- return isSystemWrapper(text.slice(userPrefix.length));
195
+ const inner = text.slice(userPrefix.length);
196
+ if (isSystemWrapper(inner)) return true;
197
+ // Strip the trailing quote of the `User responded: "…"` wrapper, then
198
+ // normalize and require an EXACT match — reuses the existing approval
199
+ // vocabulary instead of duplicating it.
200
+ const approval = inner.replace(/"$/u, '');
201
+ if (EXCELLENT_RESPONSES.has(normalizeCheckpointText(approval))) return true;
202
+ return false;
117
203
  }
118
204
  return false;
119
205
  }
@@ -128,7 +214,10 @@ export function isNoiseInsight(text: string): boolean {
128
214
  * NOISE GATE (a real-store audit found ~85 of 91 records were junk):
129
215
  * - Tool invocations are NOT harvested — a `tool_use` is telemetry, not a learning.
130
216
  * - System wrappers arriving as user messages ({@link isSystemWrapper}) are skipped.
131
- * - Substring classification only fires on checkpoint-like messages (≤ 200 chars).
217
+ * - Verdict classification only fires on checkpoint-like messages (≤ 200 chars,
218
+ * ≤ 100 for failed) and only on ANCHORED standalone words/phrases, never bare
219
+ * substrings — "в stop hook" and "не изобретать заново" are live-store
220
+ * misfires that bare substrings produced.
132
221
  */
133
222
  export function harvestDreamPatterns(options: DreamOptions): DreamPattern[] {
134
223
  const { sessionsDir, since } = options;
@@ -169,15 +258,19 @@ export function harvestDreamPatterns(options: DreamOptions): DreamPattern[] {
169
258
  outcome = 'excellent';
170
259
  score = 1.0;
171
260
  } 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')) {
261
+ // Anchored verdicts only on checkpoint-like (short) messages.
262
+ // PRECISION over recall: a missed downgrade costs nothing (the
263
+ // 'good' outcome is not recorded), a false failed@0.0 poisons the
264
+ // reward store. When in doubt, do not classify.
265
+ if (trimmed.length <= FAILED_MAX_LENGTH && FAILED_LEADING_RE.test(normalized)) {
179
266
  outcome = 'failed';
180
267
  score = 0.0;
268
+ } else if (
269
+ NEEDS_WORK_WORDS.some((word) => hasStandaloneWord(trimmed, word)) ||
270
+ ZANOVO_IMPERATIVE_RE.test(trimmed)
271
+ ) {
272
+ outcome = 'needs_work';
273
+ score = 0.3;
181
274
  }
182
275
  }
183
276