blun-king-cli 9.1.405 → 9.1.406

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/LIESMICH.txt CHANGED
@@ -404,6 +404,19 @@ Bei identischem Inhalt bleiben Dateidentität und Änderungszeit erhalten; die
404
404
  Prüfung und Härtung der privaten Dateirechte läuft trotzdem weiter. Echte
405
405
  Änderungen verwenden unverändert den crash-sicheren atomaren Schreibweg.
406
406
 
407
+ Verlässliche Beziehungskontinuität
408
+ ----------------------------------
409
+
410
+ Ab BLUN King 9.1.406 wird ein erneut zugestelltes privates Telegram-Ereignis
411
+ mit demselben Zeitstempel und Text nur einmal gespeichert. Eine tatsächlich
412
+ später wiederholte Aussage bleibt dagegen Teil des Verlaufs.
413
+
414
+ Natürliche ausdrückliche Hinweise wie „Dieter ist dein Boss, bitte merken“
415
+ werden auch dann erkannt, wenn die Bitte um Erinnerung am Ende steht. Sie
416
+ gelangen als Referenzdaten in einen begrenzten, nur ergänzbaren
417
+ Beziehungskontext. Sie erteilen oder ändern niemals Berechtigungen;
418
+ Gruppeninhalte, Geheimnisse und Zugriffsaussagen bleiben ausgeschlossen.
419
+
407
420
  Der eigentliche Anhang und die vollständige Nutzernachricht bleiben für den Zug
408
421
  unverändert verfügbar. Die Begrenzung betrifft ausschließlich die kleine
409
422
  lexikalische Vorauswahl von höchstens zwei Werkzeugschemas; ToolSearch und alle
package/README.md CHANGED
@@ -412,6 +412,18 @@ Bei identischem Inhalt bleiben Dateidentität und Änderungszeit erhalten; die
412
412
  Prüfung und Härtung der privaten Dateirechte läuft trotzdem weiter. Echte
413
413
  Änderungen verwenden unverändert den crash-sicheren atomaren Schreibweg.
414
414
 
415
+ ## Verlässliche Beziehungskontinuität
416
+
417
+ Ab BLUN King 9.1.406 wird ein erneut zugestelltes privates Telegram-Ereignis
418
+ mit demselben Zeitstempel und Text nur einmal gespeichert. Eine tatsächlich
419
+ später wiederholte Aussage bleibt dagegen Teil des Verlaufs.
420
+
421
+ Natürliche ausdrückliche Hinweise wie „Dieter ist dein Boss, bitte merken“
422
+ werden auch dann erkannt, wenn die Bitte um Erinnerung am Ende steht. Sie
423
+ gelangen als Referenzdaten in einen begrenzten, nur ergänzbaren
424
+ Beziehungskontext. Sie erteilen oder ändern niemals Berechtigungen;
425
+ Gruppeninhalte, Geheimnisse und Zugriffsaussagen bleiben ausgeschlossen.
426
+
415
427
  Der eigentliche Anhang und die vollständige Nutzernachricht bleiben für den Zug
416
428
  unverändert verfügbar. Die Begrenzung betrifft ausschließlich die kleine
417
429
  lexikalische Vorauswahl von höchstens zwei Werkzeugschemas; ToolSearch und alle
@@ -86,6 +86,22 @@ function attentionForReturn({ root, agentId, actorId, previousAt, currentAt }) {
86
86
  return writeJsonAtomic(root, target, attention) ? attention : undefined;
87
87
  }
88
88
 
89
+ function normalizedJournalEntries(value) {
90
+ if (!Array.isArray(value)) return [];
91
+ const entries = [];
92
+ const seen = new Set();
93
+ for (const entry of value) {
94
+ const occurredAt = trustedTimestamp(entry?.occurred_at);
95
+ const text = redactSecrets(entry?.text);
96
+ if (!occurredAt || !text) continue;
97
+ const key = `${occurredAt}\0${text}`;
98
+ if (seen.has(key)) continue;
99
+ seen.add(key);
100
+ entries.push({ occurred_at: occurredAt, text });
101
+ }
102
+ return entries.slice(-MAX_ENTRIES);
103
+ }
104
+
89
105
  function prepareRelationshipTurn(input = {}) {
90
106
  const root = path.resolve(String(input.root ?? ''));
91
107
  const agentId = safeId(input.agentId);
@@ -98,14 +114,14 @@ function prepareRelationshipTurn(input = {}) {
98
114
  const text = redactSecrets(input.text);
99
115
  const target = path.resolve(root, 'agents', agentId, 'journal', 'recent', `${actorId}.json`);
100
116
  const existing = readJson(root, target);
101
- const entries = Array.isArray(existing?.entries)
102
- ? existing.entries.filter((entry) => trustedTimestamp(entry?.occurred_at) && typeof entry?.text === 'string').slice(-MAX_ENTRIES)
103
- : [];
104
- const previous = entries.at(-1);
105
- const recent = entries.slice(-MAX_RECENT_CONTEXT).map((entry) => ({
106
- occurred_at: trustedTimestamp(entry.occurred_at),
107
- text: redactSecrets(entry.text),
108
- }));
117
+ const rawEntries = Array.isArray(existing?.entries) ? existing.entries.slice(-MAX_ENTRIES) : [];
118
+ const entries = normalizedJournalEntries(rawEntries);
119
+ const currentKey = text ? `${occurredAt}\0${text}` : '';
120
+ const historical = currentKey
121
+ ? entries.filter((entry) => `${entry.occurred_at}\0${entry.text}` !== currentKey)
122
+ : entries;
123
+ const previous = historical.at(-1);
124
+ const recent = historical.slice(-MAX_RECENT_CONTEXT);
109
125
  const attention = attentionForReturn({
110
126
  root,
111
127
  agentId,
@@ -115,8 +131,11 @@ function prepareRelationshipTurn(input = {}) {
115
131
  });
116
132
 
117
133
  if (text) {
118
- const next = [...entries, { occurred_at: occurredAt, text }].slice(-MAX_ENTRIES);
119
- writeJsonAtomic(root, target, { version: 1, actor_id: actorId, entries: next });
134
+ const alreadyStored = entries.some((entry) => `${entry.occurred_at}\0${entry.text}` === currentKey);
135
+ const next = alreadyStored ? entries : [...entries, { occurred_at: occurredAt, text }].slice(-MAX_ENTRIES);
136
+ if (JSON.stringify(next) !== JSON.stringify(rawEntries)) {
137
+ writeJsonAtomic(root, target, { version: 1, actor_id: actorId, entries: next });
138
+ }
120
139
  }
121
140
  return { recent, attention };
122
141
  }
@@ -30,6 +30,7 @@ const CANDIDATE_PATTERNS = [
30
30
  kind: 'explicit_relationship_note',
31
31
  patterns: [
32
32
  /^(?:bitte\s+)?merk(?:e)?\s+dir\s*(?::|,|\s+dass\s+)?\s*(.+)$/iu,
33
+ /^(.+?)(?:\s*[,;:\u2014-]\s*|\s+)bitte\s+merken[.!?]*$/iu,
33
34
  /^(?:please\s+)?remember(?:\s+that)?\s*(?::|,)?\s*(.+)$/iu,
34
35
  ],
35
36
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.405",
3
+ "version": "9.1.406",
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": {