@shadowforge0/aquifer-memory 1.9.0 → 1.9.1
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/README.md +33 -4
- package/README_CN.md +9 -1
- package/README_TW.md +5 -2
- package/consumers/cli.js +55 -34
- package/consumers/codex-active-checkpoint.js +3 -1
- package/consumers/codex-current-memory.js +10 -6
- package/consumers/codex.js +5 -2
- package/consumers/default/daily-entries.js +2 -2
- package/consumers/default/index.js +40 -30
- package/consumers/default/prompts/summary.js +2 -2
- package/consumers/mcp.js +56 -49
- package/consumers/openclaw-ext/index.js +1 -1
- package/consumers/openclaw-ext/openclaw.plugin.json +1 -1
- package/consumers/openclaw-ext/package.json +1 -1
- package/consumers/openclaw-plugin.js +66 -23
- package/consumers/shared/compat-recall.js +101 -0
- package/consumers/shared/openclaw-product-tools.js +130 -0
- package/consumers/shared/recall-format.js +2 -2
- package/core/aquifer.js +385 -20
- package/core/backends/local.js +60 -1
- package/core/finalization-review.js +88 -42
- package/core/interface.js +629 -0
- package/core/mcp-manifest.js +11 -3
- package/core/memory-bootstrap.js +25 -27
- package/core/memory-consolidation.js +564 -42
- package/core/memory-explain.js +20 -51
- package/core/memory-promotion.js +392 -55
- package/core/memory-recall.js +26 -48
- package/core/memory-records.js +91 -103
- package/core/memory-type-policy.js +298 -0
- package/core/postgres-migrations.js +9 -0
- package/core/session-checkpoint-producer.js +3 -1
- package/core/session-checkpoints.js +1 -1
- package/core/session-finalization.js +2 -2
- package/docs/getting-started.md +16 -3
- package/docs/setup.md +61 -2
- package/package.json +2 -2
- package/schema/004-completion.sql +4 -4
- package/schema/010-v1-finalization-review.sql +72 -0
- package/schema/020-v1-assistant-shaping-memory.sql +30 -0
- package/scripts/backfill-canonical-key.js +1 -1
- package/scripts/diagnose-fts-zh.js +1 -1
- package/scripts/extract-insights-from-recent-sessions.js +4 -4
package/core/memory-explain.js
CHANGED
|
@@ -7,47 +7,14 @@ const {
|
|
|
7
7
|
normalizeScopeList,
|
|
8
8
|
splitScopePath,
|
|
9
9
|
} = require('./memory-serving');
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
conclusion: 6,
|
|
19
|
-
entity_note: 7,
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const BOOTSTRAP_AUTHORITY_PRIORITY = {
|
|
23
|
-
user_explicit: 0,
|
|
24
|
-
executable_evidence: 1,
|
|
25
|
-
manual: 2,
|
|
26
|
-
system: 3,
|
|
27
|
-
verified_summary: 4,
|
|
28
|
-
llm_inference: 5,
|
|
29
|
-
raw_transcript: 6,
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const RECALL_TYPE_RANK = {
|
|
33
|
-
constraint: 80,
|
|
34
|
-
preference: 70,
|
|
35
|
-
state: 60,
|
|
36
|
-
open_loop: 55,
|
|
37
|
-
decision: 50,
|
|
38
|
-
fact: 40,
|
|
39
|
-
conclusion: 30,
|
|
40
|
-
entity_note: 20,
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const FEEDBACK_WEIGHT = {
|
|
44
|
-
helpful: 0.15,
|
|
45
|
-
confirm: 0.10,
|
|
46
|
-
irrelevant: -0.20,
|
|
47
|
-
scope_mismatch: -0.25,
|
|
48
|
-
stale: -0.30,
|
|
49
|
-
incorrect: -0.50,
|
|
50
|
-
};
|
|
10
|
+
const {
|
|
11
|
+
authoritySortPriority,
|
|
12
|
+
feedbackWeight,
|
|
13
|
+
formatRuntimeMemoryLine,
|
|
14
|
+
memoryTypeBootstrapPriority,
|
|
15
|
+
memoryTypeRecallRank,
|
|
16
|
+
memoryTypeRuntimeContract,
|
|
17
|
+
} = require('./memory-type-policy');
|
|
51
18
|
|
|
52
19
|
const RECALL_TYPE_BOOST = 0.05;
|
|
53
20
|
const RECALL_SIGNAL_PRIORITY = {
|
|
@@ -150,16 +117,20 @@ function compareRecordIdAsc(left, right) {
|
|
|
150
117
|
}
|
|
151
118
|
|
|
152
119
|
function compareBootstrapRows(left, right, context) {
|
|
120
|
+
const leftRuntimeContract = memoryTypeRuntimeContract(left.memoryType);
|
|
121
|
+
const rightRuntimeContract = memoryTypeRuntimeContract(right.memoryType);
|
|
122
|
+
if (leftRuntimeContract !== rightRuntimeContract) return leftRuntimeContract ? -1 : 1;
|
|
123
|
+
|
|
153
124
|
const leftScope = context.scopePositions.get(scopeKey(left)) ?? -1;
|
|
154
125
|
const rightScope = context.scopePositions.get(scopeKey(right)) ?? -1;
|
|
155
126
|
if (rightScope !== leftScope) return rightScope - leftScope;
|
|
156
127
|
|
|
157
|
-
const leftType =
|
|
158
|
-
const rightType =
|
|
128
|
+
const leftType = memoryTypeBootstrapPriority(left.memoryType);
|
|
129
|
+
const rightType = memoryTypeBootstrapPriority(right.memoryType);
|
|
159
130
|
if (leftType !== rightType) return leftType - rightType;
|
|
160
131
|
|
|
161
|
-
const leftAuthority =
|
|
162
|
-
const rightAuthority =
|
|
132
|
+
const leftAuthority = authoritySortPriority(left.authority);
|
|
133
|
+
const rightAuthority = authoritySortPriority(right.authority);
|
|
163
134
|
if (leftAuthority !== rightAuthority) return leftAuthority - rightAuthority;
|
|
164
135
|
|
|
165
136
|
const accepted = compareAcceptedDesc(left, right);
|
|
@@ -209,9 +180,7 @@ function compareExplainRows(left, right, context) {
|
|
|
209
180
|
}
|
|
210
181
|
|
|
211
182
|
function lineFor(record) {
|
|
212
|
-
|
|
213
|
-
const text = record.summary || record.title || '';
|
|
214
|
-
return `- ${type}: ${String(text).trim()}`;
|
|
183
|
+
return formatRuntimeMemoryLine(record);
|
|
215
184
|
}
|
|
216
185
|
|
|
217
186
|
function buildBootstrapText(records, meta) {
|
|
@@ -363,7 +332,7 @@ function feedbackScore(record, feedbackEvents = []) {
|
|
|
363
332
|
const targetId = String(event.targetId ?? event.target_id ?? '');
|
|
364
333
|
if (targetId !== id) continue;
|
|
365
334
|
const type = event.feedbackType ?? event.feedback_type ?? event.verdict;
|
|
366
|
-
score +=
|
|
335
|
+
score += feedbackWeight(type);
|
|
367
336
|
}
|
|
368
337
|
return score;
|
|
369
338
|
}
|
|
@@ -394,7 +363,7 @@ function scoreRecallRows(records, query, opts = {}) {
|
|
|
394
363
|
.map(record => {
|
|
395
364
|
const haystack = textOf(record).toLowerCase();
|
|
396
365
|
const lexical = lexicalScore(haystack, q);
|
|
397
|
-
const typeRank = ((
|
|
366
|
+
const typeRank = (memoryTypeRecallRank(record.memoryType) / 100) * RECALL_TYPE_BOOST;
|
|
398
367
|
const feedback = feedbackScore(record, feedbackEvents);
|
|
399
368
|
const title = String(record.title || '').toLowerCase();
|
|
400
369
|
const scored = {
|
|
@@ -542,7 +511,7 @@ function recordsQuery(schema, hasAllowedScopeKeys = false) {
|
|
|
542
511
|
return `SELECT
|
|
543
512
|
m.*, s.scope_kind, s.scope_key, s.inheritance_mode AS scope_inheritance_mode
|
|
544
513
|
FROM ${schema}.memory_records m
|
|
545
|
-
JOIN ${schema}.scopes s ON s.id = m.scope_id
|
|
514
|
+
JOIN ${schema}.scopes s ON s.tenant_id = m.tenant_id AND s.id = m.scope_id
|
|
546
515
|
WHERE m.tenant_id = $1
|
|
547
516
|
${hasAllowedScopeKeys ? 'AND s.scope_key = ANY($4::text[])' : ''}
|
|
548
517
|
ORDER BY
|
package/core/memory-promotion.js
CHANGED
|
@@ -1,49 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const crypto = require('crypto');
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
]);
|
|
15
|
-
|
|
16
|
-
const AUTHORITY_RANK = {
|
|
17
|
-
raw_transcript: 0,
|
|
18
|
-
llm_inference: 1,
|
|
19
|
-
verified_summary: 2,
|
|
20
|
-
manual: 3,
|
|
21
|
-
system: 4,
|
|
22
|
-
executable_evidence: 5,
|
|
23
|
-
user_explicit: 6,
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const DEFAULT_INHERITANCE = {
|
|
27
|
-
constraint: 'additive',
|
|
28
|
-
preference: 'defaultable',
|
|
29
|
-
state: 'defaultable',
|
|
30
|
-
fact: 'defaultable',
|
|
31
|
-
conclusion: 'defaultable',
|
|
32
|
-
entity_note: 'defaultable',
|
|
33
|
-
decision: 'non_inheritable',
|
|
34
|
-
open_loop: 'non_inheritable',
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const DEFAULT_ASPECT = {
|
|
38
|
-
fact: 'fact',
|
|
39
|
-
state: 'state',
|
|
40
|
-
decision: 'decision',
|
|
41
|
-
preference: 'preference',
|
|
42
|
-
constraint: 'constraint',
|
|
43
|
-
entity_note: 'entity_note',
|
|
44
|
-
open_loop: 'open_loop',
|
|
45
|
-
conclusion: 'conclusion',
|
|
46
|
-
};
|
|
4
|
+
const { assessTextForEnrich } = require('./memory-safety-gate');
|
|
5
|
+
const {
|
|
6
|
+
assistantShapingKindAllowed,
|
|
7
|
+
assistantShapingServingImpactAllowed,
|
|
8
|
+
assistantShapingServingImpactKnown,
|
|
9
|
+
authorityStrength,
|
|
10
|
+
defaultAspectForMemoryType,
|
|
11
|
+
defaultInheritanceForMemoryType,
|
|
12
|
+
isActiveMemoryType,
|
|
13
|
+
} = require('./memory-type-policy');
|
|
47
14
|
|
|
48
15
|
const FORBIDDEN_TAGS = new Set([
|
|
49
16
|
'commentary',
|
|
@@ -59,6 +26,28 @@ const FORBIDDEN_TAGS = new Set([
|
|
|
59
26
|
'env_dump',
|
|
60
27
|
]);
|
|
61
28
|
|
|
29
|
+
const ASSISTANT_BEHAVIOR_LANGUAGE_LEVELS = new Set(['user_behavior', 'assistant_behavior']);
|
|
30
|
+
|
|
31
|
+
const CANDIDATE_TEXT_FIELDS = [
|
|
32
|
+
'title',
|
|
33
|
+
'summary',
|
|
34
|
+
'evidenceText',
|
|
35
|
+
'evidence_text',
|
|
36
|
+
'evidenceExcerpt',
|
|
37
|
+
'evidence_excerpt',
|
|
38
|
+
'sourceText',
|
|
39
|
+
'source_text',
|
|
40
|
+
'quote',
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
const CANDIDATE_STRUCTURED_TEXT_FIELDS = [
|
|
44
|
+
'payload',
|
|
45
|
+
'evidenceItems',
|
|
46
|
+
'evidence_items',
|
|
47
|
+
'evidenceTexts',
|
|
48
|
+
'evidence_texts',
|
|
49
|
+
];
|
|
50
|
+
|
|
62
51
|
function normalizeText(value) {
|
|
63
52
|
return String(value || '').trim().replace(/\s+/g, ' ');
|
|
64
53
|
}
|
|
@@ -67,6 +56,185 @@ function normalizeType(value) {
|
|
|
67
56
|
return normalizeText(value).toLowerCase();
|
|
68
57
|
}
|
|
69
58
|
|
|
59
|
+
function normalizePolicyToken(value) {
|
|
60
|
+
return normalizeText(value)
|
|
61
|
+
.toLowerCase()
|
|
62
|
+
.replace(/[\s-]+/g, '_');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function candidatePayload(candidate = {}) {
|
|
66
|
+
return candidate.payload && typeof candidate.payload === 'object' && !Array.isArray(candidate.payload)
|
|
67
|
+
? candidate.payload
|
|
68
|
+
: {};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function pickPolicyToken(candidate = {}, aliases = []) {
|
|
72
|
+
const payload = candidatePayload(candidate);
|
|
73
|
+
for (const key of aliases) {
|
|
74
|
+
const value = payload[key] ?? candidate[key];
|
|
75
|
+
const normalized = normalizePolicyToken(value);
|
|
76
|
+
if (normalized) return normalized;
|
|
77
|
+
}
|
|
78
|
+
return '';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function pickPolicyText(candidate = {}, aliases = []) {
|
|
82
|
+
const payload = candidatePayload(candidate);
|
|
83
|
+
for (const key of aliases) {
|
|
84
|
+
const value = normalizeText(payload[key] ?? candidate[key]);
|
|
85
|
+
if (value) return value;
|
|
86
|
+
}
|
|
87
|
+
return '';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function numericPolicyValue(value) {
|
|
91
|
+
const parsed = Number(value);
|
|
92
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function arrayPolicyCount(value) {
|
|
96
|
+
return Array.isArray(value) ? value.filter(Boolean).length : 0;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function temporalSupportForAssistantShaping(candidate = {}) {
|
|
100
|
+
const payload = candidatePayload(candidate);
|
|
101
|
+
const support = payload.temporalSupport || payload.temporal_support || candidate.temporalSupport || candidate.temporal_support || {};
|
|
102
|
+
const refs = normalizeEvidenceRefs(candidate);
|
|
103
|
+
const distinctRefs = new Set(
|
|
104
|
+
(Array.isArray(refs) ? refs : [])
|
|
105
|
+
.map(ref => `${ref.sourceKind || ref.source_kind || 'source'}:${ref.sourceRef || ref.source_ref || ''}`)
|
|
106
|
+
.filter(value => !value.endsWith(':')),
|
|
107
|
+
);
|
|
108
|
+
const countSignals = [
|
|
109
|
+
support.sessionCount,
|
|
110
|
+
support.session_count,
|
|
111
|
+
support.observationCount,
|
|
112
|
+
support.observation_count,
|
|
113
|
+
payload.sessionCount,
|
|
114
|
+
payload.session_count,
|
|
115
|
+
payload.observationCount,
|
|
116
|
+
payload.observation_count,
|
|
117
|
+
candidate.sessionCount,
|
|
118
|
+
candidate.session_count,
|
|
119
|
+
candidate.observationCount,
|
|
120
|
+
candidate.observation_count,
|
|
121
|
+
].map(numericPolicyValue);
|
|
122
|
+
const arraySignals = [
|
|
123
|
+
support.sessions,
|
|
124
|
+
support.sessionIds,
|
|
125
|
+
support.session_ids,
|
|
126
|
+
support.observations,
|
|
127
|
+
support.evidence,
|
|
128
|
+
payload.observations,
|
|
129
|
+
payload.temporalObservations,
|
|
130
|
+
payload.temporal_observations,
|
|
131
|
+
candidate.observations,
|
|
132
|
+
].map(arrayPolicyCount);
|
|
133
|
+
const maxCount = Math.max(0, distinctRefs.size, ...countSignals, ...arraySignals);
|
|
134
|
+
const consolidationWindow = normalizeText(
|
|
135
|
+
support.window ||
|
|
136
|
+
support.cadence ||
|
|
137
|
+
support.period ||
|
|
138
|
+
payload.distillationWindow ||
|
|
139
|
+
payload.distillation_window ||
|
|
140
|
+
candidate.distillationWindow ||
|
|
141
|
+
candidate.distillation_window,
|
|
142
|
+
);
|
|
143
|
+
const repeated = support.repeated === true
|
|
144
|
+
|| payload.repeated === true
|
|
145
|
+
|| candidate.repeated === true
|
|
146
|
+
|| support.pattern === true
|
|
147
|
+
|| payload.pattern === true
|
|
148
|
+
|| candidate.pattern === true;
|
|
149
|
+
return {
|
|
150
|
+
supported: maxCount >= 2 || repeated || Boolean(consolidationWindow),
|
|
151
|
+
observationCount: maxCount,
|
|
152
|
+
consolidationWindow: consolidationWindow || null,
|
|
153
|
+
repeated,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function assistantBehaviorAbstraction(candidate = {}) {
|
|
158
|
+
const payload = candidatePayload(candidate);
|
|
159
|
+
const distillation = payload.distillation && typeof payload.distillation === 'object' ? payload.distillation : {};
|
|
160
|
+
const promotionTarget = normalizePolicyToken(
|
|
161
|
+
distillation.promotionTarget
|
|
162
|
+
|| distillation.promotion_target
|
|
163
|
+
|| payload.promotionTarget
|
|
164
|
+
|| payload.promotion_target,
|
|
165
|
+
);
|
|
166
|
+
if (promotionTarget !== 'assistant_behavior_memory') {
|
|
167
|
+
return { required: false, eligible: true };
|
|
168
|
+
}
|
|
169
|
+
const abstraction = payload.abstraction && typeof payload.abstraction === 'object'
|
|
170
|
+
? payload.abstraction
|
|
171
|
+
: (payload.generalization && typeof payload.generalization === 'object' ? payload.generalization : {});
|
|
172
|
+
const boolValue = value => {
|
|
173
|
+
if (value === true || value === false) return value;
|
|
174
|
+
if (typeof value === 'string') {
|
|
175
|
+
const normalized = value.trim().toLowerCase();
|
|
176
|
+
if (normalized === 'true') return true;
|
|
177
|
+
if (normalized === 'false') return false;
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
};
|
|
181
|
+
const appliesBeyondSource = boolValue(
|
|
182
|
+
abstraction.appliesBeyondSource
|
|
183
|
+
?? abstraction.applies_beyond_source
|
|
184
|
+
?? payload.appliesBeyondSource
|
|
185
|
+
?? payload.applies_beyond_source,
|
|
186
|
+
);
|
|
187
|
+
const sourceBound = boolValue(
|
|
188
|
+
abstraction.sourceBound
|
|
189
|
+
?? abstraction.source_bound
|
|
190
|
+
?? payload.sourceBound
|
|
191
|
+
?? payload.source_bound,
|
|
192
|
+
);
|
|
193
|
+
const principle = normalizeText(
|
|
194
|
+
abstraction.principle
|
|
195
|
+
|| abstraction.behaviorPrinciple
|
|
196
|
+
|| abstraction.behavior_principle
|
|
197
|
+
|| payload.behaviorPrinciple
|
|
198
|
+
|| payload.behavior_principle,
|
|
199
|
+
);
|
|
200
|
+
const languageLevel = normalizePolicyToken(
|
|
201
|
+
abstraction.languageLevel
|
|
202
|
+
|| abstraction.language_level
|
|
203
|
+
|| payload.languageLevel
|
|
204
|
+
|| payload.language_level,
|
|
205
|
+
);
|
|
206
|
+
const generalized = appliesBeyondSource === true && sourceBound === false && Boolean(principle);
|
|
207
|
+
const behaviorLanguage = ASSISTANT_BEHAVIOR_LANGUAGE_LEVELS.has(languageLevel);
|
|
208
|
+
return {
|
|
209
|
+
required: true,
|
|
210
|
+
eligible: generalized && behaviorLanguage,
|
|
211
|
+
generalized,
|
|
212
|
+
behaviorLanguage,
|
|
213
|
+
appliesBeyondSource,
|
|
214
|
+
sourceBound,
|
|
215
|
+
principle,
|
|
216
|
+
languageLevel,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function authorityBypassesTemporalGate(authority) {
|
|
221
|
+
return authorityStrength(authority) >= authorityStrength('manual');
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function buildAssistantShapingAspect(candidate = {}, subject = '') {
|
|
225
|
+
const shapingKind = pickPolicyToken(candidate, ['shapingKind', 'shaping_kind', 'kind', 'category']);
|
|
226
|
+
const servingImpact = pickPolicyToken(candidate, ['servingImpact', 'serving_impact', 'impact']);
|
|
227
|
+
const policyKey = pickPolicyToken(candidate, ['policyKey', 'policy_key', 'ruleKey', 'rule_key', 'policyId', 'policy_id']);
|
|
228
|
+
const explicitAspect = pickPolicyToken(candidate, ['aspect', 'attribute', 'predicate']);
|
|
229
|
+
const stableSubject = normalizePolicyToken(subject) || 'session';
|
|
230
|
+
const identityToken = policyKey || explicitAspect || stableSubject;
|
|
231
|
+
return [
|
|
232
|
+
shapingKind || defaultAspectForMemoryType('assistant_shaping'),
|
|
233
|
+
servingImpact || 'assistant_behavior',
|
|
234
|
+
identityToken,
|
|
235
|
+
].filter(Boolean).join(':');
|
|
236
|
+
}
|
|
237
|
+
|
|
70
238
|
function stableHash(value) {
|
|
71
239
|
return crypto
|
|
72
240
|
.createHash('sha256')
|
|
@@ -92,11 +260,11 @@ function assertionHash(value) {
|
|
|
92
260
|
}
|
|
93
261
|
|
|
94
262
|
function defaultInheritanceForType(memoryType) {
|
|
95
|
-
return
|
|
263
|
+
return defaultInheritanceForMemoryType(memoryType);
|
|
96
264
|
}
|
|
97
265
|
|
|
98
266
|
function authorityRank(authority) {
|
|
99
|
-
return
|
|
267
|
+
return authorityStrength(authority || 'llm_inference');
|
|
100
268
|
}
|
|
101
269
|
|
|
102
270
|
function buildCanonicalKey({ memoryType, scopeKey, contextKey, topicKey, subject, aspect }) {
|
|
@@ -106,7 +274,7 @@ function buildCanonicalKey({ memoryType, scopeKey, contextKey, topicKey, subject
|
|
|
106
274
|
const topic = normalizeType(topicKey || '');
|
|
107
275
|
const scopePart = [scope, context, topic].filter(Boolean).join('|');
|
|
108
276
|
const subj = normalizeType(subject || 'session');
|
|
109
|
-
const asp = normalizeType(aspect ||
|
|
277
|
+
const asp = normalizeType(aspect || defaultAspectForMemoryType(type));
|
|
110
278
|
return [type, scopePart, subj, asp].join(':');
|
|
111
279
|
}
|
|
112
280
|
|
|
@@ -267,6 +435,87 @@ function sameClaim(a, b) {
|
|
|
267
435
|
=== normalizeText(b && (b.summary || b.title)).toLowerCase();
|
|
268
436
|
}
|
|
269
437
|
|
|
438
|
+
function rowValue(obj, camel, snake = null) {
|
|
439
|
+
if (!obj || typeof obj !== 'object') return null;
|
|
440
|
+
if (obj[camel] !== undefined && obj[camel] !== null && obj[camel] !== '') return obj[camel];
|
|
441
|
+
if (snake && obj[snake] !== undefined && obj[snake] !== null && obj[snake] !== '') return obj[snake];
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function activeIdentityMismatch(candidate = {}, existing = {}) {
|
|
446
|
+
const candidateType = normalizeType(rowValue(candidate, 'memoryType', 'memory_type'));
|
|
447
|
+
const existingType = normalizeType(rowValue(existing, 'memoryType', 'memory_type'));
|
|
448
|
+
if (candidateType && existingType && candidateType !== existingType) return 'memory_type';
|
|
449
|
+
|
|
450
|
+
const candidateScopeId = rowValue(candidate, 'scopeId', 'scope_id');
|
|
451
|
+
const existingScopeId = rowValue(existing, 'scopeId', 'scope_id');
|
|
452
|
+
if (candidateScopeId && existingScopeId && String(candidateScopeId) !== String(existingScopeId)) return 'scope_id';
|
|
453
|
+
|
|
454
|
+
const candidateScopeKey = rowValue(candidate, 'scopeKey', 'scope_key');
|
|
455
|
+
const existingScopeKey = rowValue(existing, 'scopeKey', 'scope_key');
|
|
456
|
+
if (candidateScopeKey && existingScopeKey && String(candidateScopeKey) !== String(existingScopeKey)) return 'scope_key';
|
|
457
|
+
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function sanitizeCandidateText(value, tags) {
|
|
462
|
+
if (typeof value === 'string') {
|
|
463
|
+
const assessment = assessTextForEnrich(value, 'assistant');
|
|
464
|
+
for (const tag of assessment.tags || []) tags.add(tag);
|
|
465
|
+
if (assessment.action === 'drop') return undefined;
|
|
466
|
+
return assessment.text;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
if (Array.isArray(value)) {
|
|
470
|
+
return value
|
|
471
|
+
.map(item => sanitizeCandidateText(item, tags))
|
|
472
|
+
.filter(item => item !== undefined);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (value && typeof value === 'object') {
|
|
476
|
+
const out = {};
|
|
477
|
+
for (const [key, child] of Object.entries(value)) {
|
|
478
|
+
const sanitized = sanitizeCandidateText(child, tags);
|
|
479
|
+
if (sanitized !== undefined) out[key] = sanitized;
|
|
480
|
+
}
|
|
481
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
return value;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function sanitizePromotionCandidate(candidate = {}) {
|
|
488
|
+
const next = { ...candidate };
|
|
489
|
+
const tags = new Set([
|
|
490
|
+
...(candidate.pollutionTags || []),
|
|
491
|
+
...(candidate.tags || []),
|
|
492
|
+
].map(tag => String(tag || '').trim().toLowerCase()).filter(Boolean));
|
|
493
|
+
|
|
494
|
+
for (const field of CANDIDATE_TEXT_FIELDS) {
|
|
495
|
+
if (next[field] === undefined) continue;
|
|
496
|
+
const sanitized = sanitizeCandidateText(next[field], tags);
|
|
497
|
+
if (sanitized === undefined) delete next[field];
|
|
498
|
+
else next[field] = sanitized;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
for (const field of CANDIDATE_STRUCTURED_TEXT_FIELDS) {
|
|
502
|
+
if (next[field] === undefined) continue;
|
|
503
|
+
const sanitized = sanitizeCandidateText(next[field], tags);
|
|
504
|
+
if (sanitized === undefined) delete next[field];
|
|
505
|
+
else next[field] = sanitized;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
if (tags.size > 0) {
|
|
509
|
+
next.pollutionTags = [...tags];
|
|
510
|
+
next._safetyGate = {
|
|
511
|
+
tags: [...tags],
|
|
512
|
+
sanitized: true,
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
return next;
|
|
517
|
+
}
|
|
518
|
+
|
|
270
519
|
function pushStructuredCandidates(candidates, items, spec) {
|
|
271
520
|
for (const item of items) {
|
|
272
521
|
const text = textFromItem(item, spec.keys);
|
|
@@ -285,7 +534,11 @@ function pushStructuredCandidates(candidates, items, spec) {
|
|
|
285
534
|
const explicitAspect = normalizeText(itemObj && (itemObj.aspect || itemObj.predicate || itemObj.attribute));
|
|
286
535
|
const subject = explicitSubject || normalizeText(spec.subject);
|
|
287
536
|
let aspect = explicitAspect || normalizeText(spec.aspect);
|
|
288
|
-
if (
|
|
537
|
+
if (spec.memoryType === 'assistant_shaping') {
|
|
538
|
+
aspect = buildAssistantShapingAspect(itemObj || {}, subject);
|
|
539
|
+
} else if (!explicitAspect) {
|
|
540
|
+
aspect = `${aspect}:${stableHash(text)}`;
|
|
541
|
+
}
|
|
289
542
|
candidates.push({
|
|
290
543
|
memoryType: spec.memoryType,
|
|
291
544
|
canonicalKey: buildCanonicalKey({
|
|
@@ -304,7 +557,7 @@ function pushStructuredCandidates(candidates, items, spec) {
|
|
|
304
557
|
title: text.slice(0, 120),
|
|
305
558
|
summary: text,
|
|
306
559
|
payload: typeof item === 'string' ? { [spec.payloadKey]: text } : { ...item, [spec.payloadKey]: text },
|
|
307
|
-
authority: spec.authority,
|
|
560
|
+
authority: normalizeText(itemObj && (itemObj.authority || itemObj.payload?.authority)) || spec.authority,
|
|
308
561
|
evidenceRefs: spec.evidenceRefs,
|
|
309
562
|
evidenceText: evidenceText || undefined,
|
|
310
563
|
visibleInBootstrap: true,
|
|
@@ -333,6 +586,7 @@ function extractCandidatesFromStructuredSummary(input = {}) {
|
|
|
333
586
|
['fact', ['important_facts', 'facts'], ['statement', 'fact', 'summary', 'text'], 'statement'],
|
|
334
587
|
['preference', ['preferences'], ['preference', 'summary', 'text'], 'preference'],
|
|
335
588
|
['constraint', ['constraints'], ['constraint', 'summary', 'text'], 'constraint'],
|
|
589
|
+
['assistant_shaping', ['assistant_shaping', 'assistant_shaping_memories'], ['policy', 'guidance', 'preference', 'boundary', 'rule', 'summary', 'text'], 'guidance'],
|
|
336
590
|
['conclusion', ['conclusions'], ['conclusion', 'summary', 'text'], 'conclusion'],
|
|
337
591
|
['state', ['states', 'state'], ['state', 'summary', 'text', 'value'], 'state'],
|
|
338
592
|
['entity_note', ['entity_notes'], ['note', 'summary', 'text'], 'note'],
|
|
@@ -355,7 +609,7 @@ function extractCandidatesFromStructuredSummary(input = {}) {
|
|
|
355
609
|
contextKey,
|
|
356
610
|
topicKey,
|
|
357
611
|
subject,
|
|
358
|
-
aspect:
|
|
612
|
+
aspect: defaultAspectForMemoryType(memoryType),
|
|
359
613
|
inheritanceMode,
|
|
360
614
|
authority,
|
|
361
615
|
evidenceRefs,
|
|
@@ -365,8 +619,66 @@ function extractCandidatesFromStructuredSummary(input = {}) {
|
|
|
365
619
|
return candidates;
|
|
366
620
|
}
|
|
367
621
|
|
|
622
|
+
function assessAssistantShapingCandidate(candidate = {}) {
|
|
623
|
+
const shapingKind = pickPolicyToken(candidate, ['shapingKind', 'shaping_kind', 'kind', 'category']);
|
|
624
|
+
if (!shapingKind) return { action: 'quarantine', reason: 'missing_shaping_kind' };
|
|
625
|
+
const knownShapingKind = assistantShapingKindAllowed(shapingKind);
|
|
626
|
+
|
|
627
|
+
const servingImpact = pickPolicyToken(candidate, ['servingImpact', 'serving_impact', 'impact']);
|
|
628
|
+
if (!servingImpact) return { action: 'quarantine', reason: 'missing_serving_impact' };
|
|
629
|
+
const knownServingImpact = assistantShapingServingImpactKnown(servingImpact);
|
|
630
|
+
if (!assistantShapingServingImpactAllowed(servingImpact)) {
|
|
631
|
+
return { action: 'quarantine', reason: 'unsupported_serving_impact' };
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
const userRelevance = pickPolicyText(candidate, ['userRelevance', 'user_relevance', 'whyUserFacing', 'why_user_facing']);
|
|
635
|
+
const payload = candidatePayload(candidate);
|
|
636
|
+
if (!userRelevance && payload.userFacing !== true && candidate.userFacing !== true) {
|
|
637
|
+
return { action: 'quarantine', reason: 'missing_user_relevance' };
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
const authority = String(candidate.authority || 'llm_inference').toLowerCase();
|
|
641
|
+
const temporalSupport = temporalSupportForAssistantShaping(candidate);
|
|
642
|
+
if (!authorityBypassesTemporalGate(authority) && !temporalSupport.supported) {
|
|
643
|
+
return {
|
|
644
|
+
action: 'quarantine',
|
|
645
|
+
reason: 'assistant_shaping_needs_temporal_evidence',
|
|
646
|
+
temporalSupport,
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
const abstraction = assistantBehaviorAbstraction(candidate);
|
|
651
|
+
if (abstraction.required && !abstraction.generalized) {
|
|
652
|
+
return {
|
|
653
|
+
action: 'quarantine',
|
|
654
|
+
reason: 'assistant_behavior_requires_generalized_abstraction',
|
|
655
|
+
abstraction,
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
if (abstraction.required && !abstraction.behaviorLanguage) {
|
|
659
|
+
return {
|
|
660
|
+
action: 'quarantine',
|
|
661
|
+
reason: 'assistant_behavior_requires_behavior_level_language',
|
|
662
|
+
abstraction,
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
return {
|
|
667
|
+
action: 'promote',
|
|
668
|
+
reason: 'assistant_shaping_allowed',
|
|
669
|
+
shapingKind,
|
|
670
|
+
servingImpact,
|
|
671
|
+
knownShapingKind,
|
|
672
|
+
knownServingImpact,
|
|
673
|
+
temporalSupport,
|
|
674
|
+
abstraction,
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
|
|
368
678
|
function assessCandidate(candidate = {}, opts = {}) {
|
|
369
679
|
const memoryType = normalizeType(candidate.memoryType || candidate.memory_type);
|
|
680
|
+
let allowedReason = 'v1_foundation_allowed';
|
|
681
|
+
let allowedDetails = {};
|
|
370
682
|
const tags = new Set([
|
|
371
683
|
...(candidate.pollutionTags || []),
|
|
372
684
|
...(candidate.tags || []),
|
|
@@ -378,7 +690,7 @@ function assessCandidate(candidate = {}, opts = {}) {
|
|
|
378
690
|
}
|
|
379
691
|
}
|
|
380
692
|
|
|
381
|
-
if (!
|
|
693
|
+
if (!isActiveMemoryType(memoryType)) {
|
|
382
694
|
return { action: 'quarantine', reason: 'unsupported_memory_type' };
|
|
383
695
|
}
|
|
384
696
|
|
|
@@ -399,9 +711,33 @@ function assessCandidate(candidate = {}, opts = {}) {
|
|
|
399
711
|
return { action: 'quarantine', reason: 'missing_scope' };
|
|
400
712
|
}
|
|
401
713
|
|
|
714
|
+
if (memoryType === 'assistant_shaping') {
|
|
715
|
+
const shapingAssessment = assessAssistantShapingCandidate(candidate);
|
|
716
|
+
if (shapingAssessment.action !== 'promote') return shapingAssessment;
|
|
717
|
+
allowedReason = shapingAssessment.reason;
|
|
718
|
+
const { action: _action, reason: _reason, ...details } = shapingAssessment;
|
|
719
|
+
void _action;
|
|
720
|
+
void _reason;
|
|
721
|
+
allowedDetails = details;
|
|
722
|
+
}
|
|
723
|
+
|
|
402
724
|
const activeConflicts = opts.existingActiveRecords || [];
|
|
403
725
|
for (const existing of activeConflicts) {
|
|
404
|
-
if (sameClaim(candidate, existing))
|
|
726
|
+
if (sameClaim(candidate, existing)) {
|
|
727
|
+
const mismatch = activeIdentityMismatch(candidate, existing);
|
|
728
|
+
if (mismatch) {
|
|
729
|
+
return {
|
|
730
|
+
action: 'quarantine',
|
|
731
|
+
reason: `active_identity_mismatch_${mismatch}`,
|
|
732
|
+
conflictWith: existing.id || existing.memory_id,
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
return {
|
|
736
|
+
action: 'skip',
|
|
737
|
+
reason: 'already_active',
|
|
738
|
+
activeId: existing.id || existing.memory_id,
|
|
739
|
+
};
|
|
740
|
+
}
|
|
405
741
|
const incomingRank = authorityRank(authority);
|
|
406
742
|
const existingRank = authorityRank(existing.authority);
|
|
407
743
|
if (incomingRank < existingRank) {
|
|
@@ -414,15 +750,16 @@ function assessCandidate(candidate = {}, opts = {}) {
|
|
|
414
750
|
action: 'promote',
|
|
415
751
|
reason: 'higher_authority_supersedes',
|
|
416
752
|
supersedeId: existing.id || existing.memory_id,
|
|
753
|
+
...allowedDetails,
|
|
417
754
|
};
|
|
418
755
|
}
|
|
419
756
|
|
|
420
|
-
return { action: 'promote', reason:
|
|
757
|
+
return { action: 'promote', reason: allowedReason, ...allowedDetails };
|
|
421
758
|
}
|
|
422
759
|
|
|
423
760
|
function createMemoryPromotion({ records, embedFn = null }) {
|
|
424
761
|
async function prepareCandidates(candidates = []) {
|
|
425
|
-
const prepared = candidates.map(candidate => (
|
|
762
|
+
const prepared = candidates.map(candidate => sanitizePromotionCandidate(candidate));
|
|
426
763
|
if (typeof embedFn !== 'function' || prepared.length === 0) return prepared;
|
|
427
764
|
|
|
428
765
|
const pendingMemoryRows = [];
|
|
@@ -663,13 +1000,13 @@ function createMemoryPromotion({ records, embedFn = null }) {
|
|
|
663
1000
|
}
|
|
664
1001
|
|
|
665
1002
|
module.exports = {
|
|
666
|
-
ACTIVE_V1_TYPES,
|
|
667
1003
|
FORBIDDEN_TAGS,
|
|
668
|
-
AUTHORITY_RANK,
|
|
669
1004
|
defaultInheritanceForType,
|
|
670
1005
|
buildCanonicalKey,
|
|
671
1006
|
buildMemoryEmbeddingText,
|
|
672
1007
|
extractCandidatesFromStructuredSummary,
|
|
673
1008
|
assessCandidate,
|
|
1009
|
+
assessAssistantShapingCandidate,
|
|
1010
|
+
sanitizePromotionCandidate,
|
|
674
1011
|
createMemoryPromotion,
|
|
675
1012
|
};
|