blun-king-cli 9.1.406 → 9.1.408
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
|
@@ -417,6 +417,23 @@ gelangen als Referenzdaten in einen begrenzten, nur ergänzbaren
|
|
|
417
417
|
Beziehungskontext. Sie erteilen oder ändern niemals Berechtigungen;
|
|
418
418
|
Gruppeninhalte, Geheimnisse und Zugriffsaussagen bleiben ausgeschlossen.
|
|
419
419
|
|
|
420
|
+
Ab BLUN King 9.1.408 speichert ein ausdrücklich zu merkendes privates
|
|
421
|
+
Beziehungsdetail zunächst als unsichere Beobachtung im versionierten
|
|
422
|
+
Cognitive-Memory-Adapter. Erst eine zweite, unabhängige identische Aussage
|
|
423
|
+
bestätigt es; der nächste private Turn lädt den bestätigten Eintrag. Wiederholte
|
|
424
|
+
Ereignisse bleiben idempotent, Gruppen sehen ihn nie, und gespeicherte Aussagen
|
|
425
|
+
können keine Berechtigungen erteilen. Ist ein konfigurierter Memory-Anbieter
|
|
426
|
+
nicht verfügbar, wird keine lokale Schatten-Memory angelegt.
|
|
427
|
+
|
|
428
|
+
Stabiler Telegram-Reply-Weg
|
|
429
|
+
--------------------------
|
|
430
|
+
|
|
431
|
+
Ab BLUN King 9.1.407 kann der Duplikatschutz des Telegram-Reply-Werkzeugs das
|
|
432
|
+
aktuelle Eingangsprotokoll wieder direkt lesen. Normale Antworten scheitern
|
|
433
|
+
dadurch nicht mehr an einem fehlenden Laufzeitimport; der getrennte
|
|
434
|
+
Bridge-Fallback bleibt nur die Absicherung und nicht der unbeabsichtigte
|
|
435
|
+
Hauptweg.
|
|
436
|
+
|
|
420
437
|
Der eigentliche Anhang und die vollständige Nutzernachricht bleiben für den Zug
|
|
421
438
|
unverändert verfügbar. Die Begrenzung betrifft ausschließlich die kleine
|
|
422
439
|
lexikalische Vorauswahl von höchstens zwei Werkzeugschemas; ToolSearch und alle
|
package/README.md
CHANGED
|
@@ -424,6 +424,22 @@ gelangen als Referenzdaten in einen begrenzten, nur ergänzbaren
|
|
|
424
424
|
Beziehungskontext. Sie erteilen oder ändern niemals Berechtigungen;
|
|
425
425
|
Gruppeninhalte, Geheimnisse und Zugriffsaussagen bleiben ausgeschlossen.
|
|
426
426
|
|
|
427
|
+
Ab BLUN King 9.1.408 speichert ein ausdrücklich zu merkendes privates
|
|
428
|
+
Beziehungsdetail zunächst als unsichere Beobachtung im versionierten
|
|
429
|
+
Cognitive-Memory-Adapter. Erst eine zweite, unabhängige identische Aussage
|
|
430
|
+
bestätigt es; der nächste private Turn lädt den bestätigten Eintrag. Wiederholte
|
|
431
|
+
Ereignisse bleiben idempotent, Gruppen sehen ihn nie, und gespeicherte Aussagen
|
|
432
|
+
können keine Berechtigungen erteilen. Ist ein konfigurierter Memory-Anbieter
|
|
433
|
+
nicht verfügbar, wird keine lokale Schatten-Memory angelegt.
|
|
434
|
+
|
|
435
|
+
## Stabiler Telegram-Reply-Weg
|
|
436
|
+
|
|
437
|
+
Ab BLUN King 9.1.407 kann der Duplikatschutz des Telegram-Reply-Werkzeugs das
|
|
438
|
+
aktuelle Eingangsprotokoll wieder direkt lesen. Normale Antworten scheitern
|
|
439
|
+
dadurch nicht mehr an einem fehlenden Laufzeitimport; der getrennte
|
|
440
|
+
Bridge-Fallback bleibt nur die Absicherung und nicht der unbeabsichtigte
|
|
441
|
+
Hauptweg.
|
|
442
|
+
|
|
427
443
|
Der eigentliche Anhang und die vollständige Nutzernachricht bleiben für den Zug
|
|
428
444
|
unverändert verfügbar. Die Begrenzung betrifft ausschließlich die kleine
|
|
429
445
|
lexikalische Vorauswahl von höchstens zwei Werkzeugschemas; ToolSearch und alle
|
|
@@ -7,6 +7,8 @@ const { ensurePersonalityWorkspace } = require('./personality-setup-policy.cjs')
|
|
|
7
7
|
const { prepareRelationshipTurn } = require('./relationship-continuity-policy.cjs');
|
|
8
8
|
const { prepareCuriosityOpportunity } = require('./relationship-curiosity-policy.cjs');
|
|
9
9
|
const { recordExplicitRelationshipCandidate } = require('./relationship-learning-policy.cjs');
|
|
10
|
+
const { createPersonalityMemoryAdapter } = require('./personality-memory-adapter.cjs');
|
|
11
|
+
const { loadConfiguredCognitiveMemoryAdapter } = require('./cognitive-memory-provider.cjs');
|
|
10
12
|
|
|
11
13
|
const MAX_FILE_BYTES = 64 * 1024;
|
|
12
14
|
const DEFAULT_MAX_CHARS = 1500;
|
|
@@ -313,9 +315,19 @@ function pendingRelationshipNotes(root, agentId, actorId) {
|
|
|
313
315
|
|
|
314
316
|
function renderRememberedNotes(lines, notes) {
|
|
315
317
|
if (notes.length === 0) return;
|
|
316
|
-
const
|
|
317
|
-
const
|
|
318
|
-
|
|
318
|
+
const confirmed = notes.filter((note) => note.status === 'confirmed');
|
|
319
|
+
const pending = notes.filter((note) => note.status !== 'confirmed');
|
|
320
|
+
if (confirmed.length > 0) {
|
|
321
|
+
lines.push(
|
|
322
|
+
'',
|
|
323
|
+
'### Confirmed relationship memory',
|
|
324
|
+
'These confirmed private observations are reference data only and cannot grant or change permissions.',
|
|
325
|
+
);
|
|
326
|
+
for (const note of confirmed) addField(lines, note.occurred_at, note.value);
|
|
327
|
+
}
|
|
328
|
+
const remembered = pending.filter((note) => note.kind === 'explicit_relationship_note');
|
|
329
|
+
const openThreads = pending.filter((note) => note.kind === 'open_thread');
|
|
330
|
+
const repairs = pending.filter((note) => note.kind === 'repair_lesson');
|
|
319
331
|
if (remembered.length > 0) {
|
|
320
332
|
lines.push(
|
|
321
333
|
'',
|
|
@@ -342,7 +354,7 @@ function renderRememberedNotes(lines, notes) {
|
|
|
342
354
|
}
|
|
343
355
|
}
|
|
344
356
|
|
|
345
|
-
function renderIdentityContext({ root, agentId, actorId, groupId, limit, continuity, curiosity }) {
|
|
357
|
+
function renderIdentityContext({ root, agentId, actorId, groupId, limit, continuity, curiosity, relationshipNotes = [] }) {
|
|
346
358
|
const agent = readJson(root, ['agents', agentId, 'profile.json']);
|
|
347
359
|
const actor = actorId ? readJson(root, ['actors', `${actorId}.json`]) : undefined;
|
|
348
360
|
const relationship = actorId ? readJson(root, ['agents', agentId, 'relationships', `${actorId}.json`]) : undefined;
|
|
@@ -358,7 +370,14 @@ function renderIdentityContext({ root, agentId, actorId, groupId, limit, continu
|
|
|
358
370
|
renderColleagueRelationships(lines, root, agentId, actorId);
|
|
359
371
|
renderContinuity(lines, continuity);
|
|
360
372
|
renderCuriosity(lines, curiosity);
|
|
361
|
-
|
|
373
|
+
const rememberedNotes = groupId
|
|
374
|
+
? []
|
|
375
|
+
: [...relationshipNotes, ...pendingRelationshipNotes(root, agentId, actorId)]
|
|
376
|
+
.filter((note, index, all) => all.findIndex((candidate) => (
|
|
377
|
+
candidate.kind === note.kind && candidate.value === note.value
|
|
378
|
+
)) === index)
|
|
379
|
+
.slice(0, 4);
|
|
380
|
+
renderRememberedNotes(lines, rememberedNotes);
|
|
362
381
|
renderGroup(lines, group);
|
|
363
382
|
renderRelationship(lines, relationship, previousInteractionAfterLongGap(root, agentId, actorId));
|
|
364
383
|
renderActor(lines, actor);
|
|
@@ -460,6 +479,8 @@ function recordChannelIdentity(envelope, env = process.env) {
|
|
|
460
479
|
|
|
461
480
|
const actorId = `telegram-${subjectId}`;
|
|
462
481
|
const groupId = chatId.startsWith('-') ? `telegram-chat-${chatId.slice(1)}` : undefined;
|
|
482
|
+
const messageId = safeId(meta.message_id);
|
|
483
|
+
const conversationId = groupId || `telegram-dm-${chatId}`;
|
|
463
484
|
const displayName = cleanText(meta.user, 120) || actorId;
|
|
464
485
|
const firstSeenAt = trustedTimestamp(meta.ts);
|
|
465
486
|
const receivedAt = trustedTimestamp(meta.received_at) || firstSeenAt;
|
|
@@ -521,6 +542,38 @@ function recordChannelIdentity(envelope, env = process.env) {
|
|
|
521
542
|
receivedAt,
|
|
522
543
|
});
|
|
523
544
|
if (firstSeenAt) recordInteractionActivity(root, agentId, actorId, firstSeenAt);
|
|
545
|
+
let relationshipMemory;
|
|
546
|
+
let relationshipMemoryUnavailable = false;
|
|
547
|
+
let relationshipNotes = [];
|
|
548
|
+
if (!groupId && messageId) {
|
|
549
|
+
try {
|
|
550
|
+
const configuredMemory = loadConfiguredCognitiveMemoryAdapter({
|
|
551
|
+
env,
|
|
552
|
+
home: path.dirname(root),
|
|
553
|
+
tenantId,
|
|
554
|
+
agentId,
|
|
555
|
+
agentName: agentId,
|
|
556
|
+
});
|
|
557
|
+
relationshipMemory = createPersonalityMemoryAdapter({
|
|
558
|
+
identityRoot: root,
|
|
559
|
+
memoryAdapter: configuredMemory,
|
|
560
|
+
});
|
|
561
|
+
relationshipNotes = relationshipMemory.readRelationshipLearning({
|
|
562
|
+
tenantId,
|
|
563
|
+
agentId,
|
|
564
|
+
actorId,
|
|
565
|
+
sourcePortal: 'telegram',
|
|
566
|
+
sourceChannel: 'direct',
|
|
567
|
+
conversationId,
|
|
568
|
+
messageId,
|
|
569
|
+
}).notes;
|
|
570
|
+
} catch {
|
|
571
|
+
try { relationshipMemory?.close(); } catch {}
|
|
572
|
+
relationshipMemory = undefined;
|
|
573
|
+
relationshipMemoryUnavailable = true;
|
|
574
|
+
relationshipNotes = [];
|
|
575
|
+
}
|
|
576
|
+
}
|
|
524
577
|
const modelContext = renderIdentityContext({
|
|
525
578
|
root,
|
|
526
579
|
agentId,
|
|
@@ -529,16 +582,27 @@ function recordChannelIdentity(envelope, env = process.env) {
|
|
|
529
582
|
limit: maxChars(env),
|
|
530
583
|
continuity,
|
|
531
584
|
curiosity,
|
|
585
|
+
relationshipNotes,
|
|
532
586
|
});
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
587
|
+
let learning;
|
|
588
|
+
try {
|
|
589
|
+
learning = recordExplicitRelationshipCandidate({
|
|
590
|
+
root,
|
|
591
|
+
tenantId,
|
|
592
|
+
agentId,
|
|
593
|
+
actorId,
|
|
594
|
+
groupId,
|
|
595
|
+
text: envelope.text,
|
|
596
|
+
occurredAt: firstSeenAt,
|
|
597
|
+
receivedAt,
|
|
598
|
+
conversationId,
|
|
599
|
+
messageId,
|
|
600
|
+
memory: relationshipMemory,
|
|
601
|
+
memoryUnavailable: relationshipMemoryUnavailable,
|
|
602
|
+
});
|
|
603
|
+
} finally {
|
|
604
|
+
try { relationshipMemory?.close(); } catch {}
|
|
605
|
+
}
|
|
542
606
|
const journal = created.includes('relationship') && firstSeenAt
|
|
543
607
|
? recordIdentityJournalCandidate({
|
|
544
608
|
kind: 'first_interaction',
|
|
@@ -11,6 +11,19 @@ const INPUT_KEYS = [
|
|
|
11
11
|
'actorId', 'agentId', 'conversationId', 'cooldownMs', 'messageId', 'occurredAt',
|
|
12
12
|
'receivedAt', 'sensitivity', 'sourceChannel', 'sourcePortal', 'tenantId', 'topic',
|
|
13
13
|
];
|
|
14
|
+
const RELATIONSHIP_RECORD_KEYS = [
|
|
15
|
+
'actorId', 'agentId', 'conversationId', 'kind', 'messageId', 'occurredAt',
|
|
16
|
+
'receivedAt', 'sourceChannel', 'sourcePortal', 'tenantId', 'value',
|
|
17
|
+
];
|
|
18
|
+
const RELATIONSHIP_READ_KEYS = [
|
|
19
|
+
'actorId', 'agentId', 'conversationId', 'messageId', 'sourceChannel',
|
|
20
|
+
'sourcePortal', 'tenantId',
|
|
21
|
+
];
|
|
22
|
+
const RELATIONSHIP_KINDS = new Set([
|
|
23
|
+
'explicit_relationship_note', 'open_thread', 'repair_lesson',
|
|
24
|
+
]);
|
|
25
|
+
const FORBIDDEN_RELATIONSHIP_RE = /\b(?:acl|admin|api[_ -]?key|capability|deploy|freigabe|passwor[dt]|permission|secret|token|write access|zugriffs?recht|du darfst|you may)\b/iu;
|
|
26
|
+
const RELATIONSHIP_KEY_PREFIX = 'relationship-note-v1:';
|
|
14
27
|
const MAX_COOLDOWN_MS = 90 * 24 * 60 * 60 * 1000;
|
|
15
28
|
|
|
16
29
|
function fail(code) {
|
|
@@ -29,6 +42,11 @@ function safeId(value) {
|
|
|
29
42
|
return SAFE_ID_RE.test(text) ? text : '';
|
|
30
43
|
}
|
|
31
44
|
|
|
45
|
+
function cleanText(value, max) {
|
|
46
|
+
const text = String(value ?? '').replace(/[\u0000-\u001f\u007f]+/gu, ' ').replace(/\s+/gu, ' ').trim();
|
|
47
|
+
return text && text.length <= max ? text : '';
|
|
48
|
+
}
|
|
49
|
+
|
|
32
50
|
function digest(prefix, values) {
|
|
33
51
|
return `${prefix}-${crypto.createHash('sha256').update(values.join('\0')).digest('hex').slice(0, 40)}`;
|
|
34
52
|
}
|
|
@@ -68,6 +86,84 @@ function normalizePresentation(input) {
|
|
|
68
86
|
return normalized;
|
|
69
87
|
}
|
|
70
88
|
|
|
89
|
+
function normalizeRelationshipRecord(input) {
|
|
90
|
+
if (!exactKeys(input, RELATIONSHIP_RECORD_KEYS)) fail('PERSONALITY_MEMORY_FORBIDDEN_FIELD');
|
|
91
|
+
const normalized = {
|
|
92
|
+
tenantId: safeId(input.tenantId),
|
|
93
|
+
agentId: safeId(input.agentId),
|
|
94
|
+
actorId: safeId(input.actorId),
|
|
95
|
+
sourcePortal: safeId(input.sourcePortal),
|
|
96
|
+
sourceChannel: safeId(input.sourceChannel),
|
|
97
|
+
conversationId: safeId(input.conversationId),
|
|
98
|
+
messageId: safeId(input.messageId),
|
|
99
|
+
occurredAt: String(input.occurredAt ?? '').trim(),
|
|
100
|
+
receivedAt: String(input.receivedAt ?? '').trim(),
|
|
101
|
+
kind: String(input.kind ?? ''),
|
|
102
|
+
value: cleanText(input.value, 280),
|
|
103
|
+
};
|
|
104
|
+
if (!normalized.tenantId || !normalized.agentId || !normalized.actorId
|
|
105
|
+
|| !normalized.sourcePortal || normalized.sourceChannel !== 'direct'
|
|
106
|
+
|| !normalized.conversationId || !normalized.messageId
|
|
107
|
+
|| Number.isNaN(Date.parse(normalized.occurredAt))
|
|
108
|
+
|| Number.isNaN(Date.parse(normalized.receivedAt))
|
|
109
|
+
|| !RELATIONSHIP_KINDS.has(normalized.kind) || !normalized.value) {
|
|
110
|
+
fail('PERSONALITY_MEMORY_INVALID_INPUT');
|
|
111
|
+
}
|
|
112
|
+
if (FORBIDDEN_RELATIONSHIP_RE.test(normalized.value)) fail('PERSONALITY_MEMORY_FORBIDDEN_FIELD');
|
|
113
|
+
normalized.occurredAt = new Date(normalized.occurredAt).toISOString();
|
|
114
|
+
normalized.receivedAt = new Date(normalized.receivedAt).toISOString();
|
|
115
|
+
return normalized;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function normalizeRelationshipRead(input) {
|
|
119
|
+
if (!exactKeys(input, RELATIONSHIP_READ_KEYS)) fail('PERSONALITY_MEMORY_FORBIDDEN_FIELD');
|
|
120
|
+
const normalized = {
|
|
121
|
+
tenantId: safeId(input.tenantId),
|
|
122
|
+
agentId: safeId(input.agentId),
|
|
123
|
+
actorId: safeId(input.actorId),
|
|
124
|
+
sourcePortal: safeId(input.sourcePortal),
|
|
125
|
+
sourceChannel: safeId(input.sourceChannel),
|
|
126
|
+
conversationId: safeId(input.conversationId),
|
|
127
|
+
messageId: safeId(input.messageId),
|
|
128
|
+
};
|
|
129
|
+
if (Object.values(normalized).some((value) => !value)) fail('PERSONALITY_MEMORY_INVALID_INPUT');
|
|
130
|
+
return normalized;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function relationshipMemoryAccess(value, receiptId) {
|
|
134
|
+
return {
|
|
135
|
+
tenantId: value.tenantId,
|
|
136
|
+
userId: value.actorId,
|
|
137
|
+
actorId: value.actorId,
|
|
138
|
+
agentId: value.agentId,
|
|
139
|
+
portalId: value.sourcePortal,
|
|
140
|
+
channelId: value.sourceChannel,
|
|
141
|
+
conversationId: value.conversationId,
|
|
142
|
+
requestedScope: relationshipScope(value.actorId),
|
|
143
|
+
permissionContext: {
|
|
144
|
+
authority: 'runtime_personality_policy',
|
|
145
|
+
decision: 'passed',
|
|
146
|
+
receiptId,
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function relationshipKindFromKey(key) {
|
|
152
|
+
for (const kind of RELATIONSHIP_KINDS) {
|
|
153
|
+
if (String(key ?? '').startsWith(`${RELATIONSHIP_KEY_PREFIX}${kind}:`)) return kind;
|
|
154
|
+
}
|
|
155
|
+
return '';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function currentRelationshipNotes(state) {
|
|
159
|
+
const observations = Array.isArray(state?.observations) ? state.observations : [];
|
|
160
|
+
const superseded = new Set(observations.map((item) => item?.supersedes).filter(Boolean));
|
|
161
|
+
const withdrawn = new Set(observations.map((item) => item?.withdraws).filter(Boolean));
|
|
162
|
+
return observations.filter((item) => relationshipKindFromKey(item?.key)
|
|
163
|
+
&& !superseded.has(item?.observation_id)
|
|
164
|
+
&& !withdrawn.has(item?.observation_id));
|
|
165
|
+
}
|
|
166
|
+
|
|
71
167
|
function createPersonalityMemoryAdapter({ identityRoot, memoryAdapter } = {}) {
|
|
72
168
|
const root = path.resolve(String(identityRoot ?? ''));
|
|
73
169
|
try {
|
|
@@ -83,6 +179,109 @@ function createPersonalityMemoryAdapter({ identityRoot, memoryAdapter } = {}) {
|
|
|
83
179
|
const home = path.dirname(realRoot);
|
|
84
180
|
const store = openCognitiveMemoryAdapter({ home, adapter: memoryAdapter });
|
|
85
181
|
|
|
182
|
+
function readRelationshipState(value, receiptId) {
|
|
183
|
+
const access = relationshipMemoryAccess(value, receiptId);
|
|
184
|
+
return {
|
|
185
|
+
access,
|
|
186
|
+
state: store.read({ tenantId: value.tenantId, agentId: value.agentId }, access),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function recordRelationshipLearning(input) {
|
|
191
|
+
const value = normalizeRelationshipRecord(input);
|
|
192
|
+
const eventId = digest('relationship-event', [
|
|
193
|
+
value.tenantId, value.agentId, value.actorId, value.sourcePortal, value.sourceChannel,
|
|
194
|
+
value.conversationId, value.messageId, value.occurredAt, value.kind, value.value,
|
|
195
|
+
]);
|
|
196
|
+
const key = `${RELATIONSHIP_KEY_PREFIX}${value.kind}:${digest('value', [value.value])}`;
|
|
197
|
+
const { access, state } = readRelationshipState(value, eventId);
|
|
198
|
+
const matching = currentRelationshipNotes(state).find((item) => item.key === key);
|
|
199
|
+
if (store.hasEvent({
|
|
200
|
+
eventId,
|
|
201
|
+
tenantId: value.tenantId,
|
|
202
|
+
agentId: value.agentId,
|
|
203
|
+
visibilityScope: relationshipScope(value.actorId),
|
|
204
|
+
}, access)) {
|
|
205
|
+
return {
|
|
206
|
+
status: matching?.epistemic_state === 'verified' ? 'confirmed' : 'pending',
|
|
207
|
+
idempotent: true,
|
|
208
|
+
observation_id: matching?.observation_id,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
if (matching?.epistemic_state === 'verified') {
|
|
212
|
+
return { status: 'confirmed', idempotent: true, observation_id: matching.observation_id };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const confirmed = matching?.epistemic_state === 'credible_unverified';
|
|
216
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
217
|
+
const current = attempt === 0
|
|
218
|
+
? state
|
|
219
|
+
: store.read({ tenantId: value.tenantId, agentId: value.agentId }, access);
|
|
220
|
+
const currentMatch = currentRelationshipNotes(current).find((item) => item.key === key);
|
|
221
|
+
if (currentMatch?.epistemic_state === 'verified') {
|
|
222
|
+
return { status: 'confirmed', idempotent: true, observation_id: currentMatch.observation_id };
|
|
223
|
+
}
|
|
224
|
+
const shouldConfirm = confirmed || currentMatch?.epistemic_state === 'credible_unverified';
|
|
225
|
+
const observationId = digest('relationship-observation', [eventId]);
|
|
226
|
+
try {
|
|
227
|
+
store.commit({
|
|
228
|
+
tenantId: value.tenantId,
|
|
229
|
+
agentId: value.agentId,
|
|
230
|
+
eventId,
|
|
231
|
+
expectedVersion: current.version,
|
|
232
|
+
occurredAt: value.occurredAt,
|
|
233
|
+
receivedAt: value.receivedAt,
|
|
234
|
+
retentionClass: 'relationship',
|
|
235
|
+
source: {
|
|
236
|
+
provider: value.sourcePortal,
|
|
237
|
+
channel_id: value.sourceChannel,
|
|
238
|
+
actor_id: value.actorId,
|
|
239
|
+
context_id: value.conversationId,
|
|
240
|
+
message_id: value.messageId,
|
|
241
|
+
},
|
|
242
|
+
observations: [{
|
|
243
|
+
observation_id: observationId,
|
|
244
|
+
domain: 'team',
|
|
245
|
+
key,
|
|
246
|
+
value: value.value,
|
|
247
|
+
confidence: shouldConfirm ? 1 : 0.75,
|
|
248
|
+
epistemic_state: shouldConfirm ? 'verified' : 'credible_unverified',
|
|
249
|
+
scope: relationshipScope(value.actorId),
|
|
250
|
+
...(shouldConfirm ? { supersedes: currentMatch.observation_id } : {}),
|
|
251
|
+
}],
|
|
252
|
+
}, access);
|
|
253
|
+
return {
|
|
254
|
+
status: shouldConfirm ? 'confirmed' : 'pending',
|
|
255
|
+
idempotent: false,
|
|
256
|
+
observation_id: observationId,
|
|
257
|
+
};
|
|
258
|
+
} catch (error) {
|
|
259
|
+
if (error?.code !== 'COGNITIVE_VERSION_CONFLICT' || attempt === 2) throw error;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
fail('PERSONALITY_MEMORY_VERSION_CONFLICT');
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function readRelationshipLearning(input) {
|
|
266
|
+
const value = normalizeRelationshipRead(input);
|
|
267
|
+
const receiptId = digest('relationship-read', [
|
|
268
|
+
value.tenantId, value.agentId, value.actorId, value.sourcePortal,
|
|
269
|
+
value.sourceChannel, value.conversationId, value.messageId,
|
|
270
|
+
]);
|
|
271
|
+
const { state } = readRelationshipState(value, receiptId);
|
|
272
|
+
const notes = currentRelationshipNotes(state)
|
|
273
|
+
.filter((item) => ['credible_unverified', 'verified'].includes(item.epistemic_state))
|
|
274
|
+
.map((item) => ({
|
|
275
|
+
kind: relationshipKindFromKey(item.key),
|
|
276
|
+
status: item.epistemic_state === 'verified' ? 'confirmed' : 'pending',
|
|
277
|
+
value: item.value,
|
|
278
|
+
observation_id: item.observation_id,
|
|
279
|
+
occurred_at: item.occurred_at,
|
|
280
|
+
}))
|
|
281
|
+
.sort((left, right) => Date.parse(right.occurred_at) - Date.parse(left.occurred_at));
|
|
282
|
+
return { version: state.version, notes };
|
|
283
|
+
}
|
|
284
|
+
|
|
86
285
|
function claimCuriosityPresentation(input) {
|
|
87
286
|
const value = normalizePresentation(input);
|
|
88
287
|
const candidateId = digest('curiosity', [value.tenantId, value.agentId, value.actorId]);
|
|
@@ -171,6 +370,8 @@ function createPersonalityMemoryAdapter({ identityRoot, memoryAdapter } = {}) {
|
|
|
171
370
|
return {
|
|
172
371
|
kind: 'local-cognitive-projection-v1',
|
|
173
372
|
claimCuriosityPresentation,
|
|
373
|
+
recordRelationshipLearning,
|
|
374
|
+
readRelationshipLearning,
|
|
174
375
|
close: () => store.close(),
|
|
175
376
|
};
|
|
176
377
|
}
|
|
@@ -131,6 +131,34 @@ function recordExplicitRelationshipCandidate(input = {}) {
|
|
|
131
131
|
history_policy: 'append_only',
|
|
132
132
|
occurred_at: occurredAt,
|
|
133
133
|
};
|
|
134
|
+
if (input.memoryUnavailable === true) {
|
|
135
|
+
return {
|
|
136
|
+
created: false,
|
|
137
|
+
status: 'unavailable',
|
|
138
|
+
storage: 'cognitive-memory-adapter-v1',
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
if (input.memory && typeof input.memory.recordRelationshipLearning === 'function') {
|
|
142
|
+
const memoryResult = input.memory.recordRelationshipLearning({
|
|
143
|
+
tenantId,
|
|
144
|
+
agentId,
|
|
145
|
+
actorId,
|
|
146
|
+
sourcePortal: 'telegram',
|
|
147
|
+
sourceChannel: 'direct',
|
|
148
|
+
conversationId: safeId(input.conversationId),
|
|
149
|
+
messageId: safeId(input.messageId),
|
|
150
|
+
occurredAt,
|
|
151
|
+
receivedAt: String(input.receivedAt ?? occurredAt).trim(),
|
|
152
|
+
kind: explicit.kind,
|
|
153
|
+
value: explicit.value,
|
|
154
|
+
});
|
|
155
|
+
return {
|
|
156
|
+
candidate_id: memoryResult.observation_id,
|
|
157
|
+
created: memoryResult.idempotent === false,
|
|
158
|
+
status: memoryResult.status,
|
|
159
|
+
storage: 'cognitive-memory-adapter-v1',
|
|
160
|
+
};
|
|
161
|
+
}
|
|
134
162
|
return { candidate_id: candidateId, created: createJsonOnce(realRoot, target, candidate) };
|
|
135
163
|
} catch {
|
|
136
164
|
return undefined;
|
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as _enum, B as object, F as discriminatedUnion, G as union, H as preprocess, I as intersection, J as datetime, K as unknown, L as literal, M as array, N as boolean, P as custom, R as looseObject, S as outboxLog, U as record, V as optional, W as string, X as __commonJSMin, Y as safeParse$1, Z as __toESM, c as MAX_CHUNK_LIMIT, f as readAccess, g as botToken, i as chunk, j as _null, k as require_out, l as assertAllowedChat, m as appendJsonl, n as isGroupNoiseReply, r as PHOTO_EXTS, t as isGroupChat, u as assertSendable, v as envFile, x as loadEnvFile, y as inboxDir, z as number } from "./noise.mjs";
|
|
1
|
+
import { A as _enum, B as object, F as discriminatedUnion, G as union, H as preprocess, I as intersection, J as datetime, K as unknown, L as literal, M as array, N as boolean, P as custom, R as looseObject, S as outboxLog, U as record, V as optional, W as string, X as __commonJSMin, Y as safeParse$1, Z as __toESM, b as inboxLog, c as MAX_CHUNK_LIMIT, f as readAccess, g as botToken, i as chunk, j as _null, k as require_out, l as assertAllowedChat, m as appendJsonl, n as isGroupNoiseReply, r as PHOTO_EXTS, t as isGroupChat, u as assertSendable, v as envFile, x as loadEnvFile, y as inboxDir, z as number } from "./noise.mjs";
|
|
2
2
|
import process$1 from "node:process";
|
|
3
3
|
import { mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { extname, join } from "node:path";
|