@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-recall.js
CHANGED
|
@@ -3,26 +3,12 @@
|
|
|
3
3
|
const { resolveApplicableRecords } = require('./memory-bootstrap');
|
|
4
4
|
const { assertAllowedScopeRequest } = require('./memory-serving');
|
|
5
5
|
const { hybridRank } = require('./hybrid-rank');
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
decision: 50,
|
|
13
|
-
fact: 40,
|
|
14
|
-
conclusion: 30,
|
|
15
|
-
entity_note: 20,
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const FEEDBACK_WEIGHT = {
|
|
19
|
-
helpful: 0.15,
|
|
20
|
-
confirm: 0.10,
|
|
21
|
-
irrelevant: -0.20,
|
|
22
|
-
scope_mismatch: -0.25,
|
|
23
|
-
stale: -0.30,
|
|
24
|
-
incorrect: -0.50,
|
|
25
|
-
};
|
|
6
|
+
const {
|
|
7
|
+
feedbackWeight,
|
|
8
|
+
feedbackWeightSql,
|
|
9
|
+
memoryTypeRecallRank,
|
|
10
|
+
memoryTypeRecallRankSql,
|
|
11
|
+
} = require('./memory-type-policy');
|
|
26
12
|
|
|
27
13
|
const RETRIEVAL_TYPE_BOOST = 0.05;
|
|
28
14
|
const SIGNAL_PRIORITY = {
|
|
@@ -31,34 +17,14 @@ const SIGNAL_PRIORITY = {
|
|
|
31
17
|
memory_row: 3,
|
|
32
18
|
};
|
|
33
19
|
|
|
34
|
-
const TYPE_RANK_SQL =
|
|
35
|
-
CASE m.memory_type
|
|
36
|
-
WHEN 'constraint' THEN 0.80
|
|
37
|
-
WHEN 'preference' THEN 0.70
|
|
38
|
-
WHEN 'state' THEN 0.60
|
|
39
|
-
WHEN 'open_loop' THEN 0.55
|
|
40
|
-
WHEN 'decision' THEN 0.50
|
|
41
|
-
WHEN 'fact' THEN 0.40
|
|
42
|
-
WHEN 'conclusion' THEN 0.30
|
|
43
|
-
WHEN 'entity_note' THEN 0.20
|
|
44
|
-
ELSE 0
|
|
45
|
-
END`;
|
|
46
|
-
|
|
20
|
+
const TYPE_RANK_SQL = memoryTypeRecallRankSql('m.memory_type');
|
|
47
21
|
const TYPE_BOOST_SQL = `(${TYPE_RANK_SQL}) * ${RETRIEVAL_TYPE_BOOST}`;
|
|
48
22
|
|
|
49
23
|
function feedbackScoreSql(schema) {
|
|
50
24
|
return `
|
|
51
25
|
COALESCE((
|
|
52
26
|
SELECT SUM(
|
|
53
|
-
|
|
54
|
-
WHEN 'helpful' THEN 0.15
|
|
55
|
-
WHEN 'confirm' THEN 0.10
|
|
56
|
-
WHEN 'irrelevant' THEN -0.20
|
|
57
|
-
WHEN 'scope_mismatch' THEN -0.25
|
|
58
|
-
WHEN 'stale' THEN -0.30
|
|
59
|
-
WHEN 'incorrect' THEN -0.50
|
|
60
|
-
ELSE 0
|
|
61
|
-
END
|
|
27
|
+
${feedbackWeightSql('f.feedback_type')}
|
|
62
28
|
)
|
|
63
29
|
FROM ${schema}.feedback f
|
|
64
30
|
WHERE f.tenant_id = $1
|
|
@@ -223,7 +189,7 @@ function feedbackScore(record, feedbackEvents = []) {
|
|
|
223
189
|
const targetId = String(event.targetId || event.target_id || '');
|
|
224
190
|
if (targetId !== id) continue;
|
|
225
191
|
const type = event.feedbackType || event.feedback_type || event.verdict;
|
|
226
|
-
score +=
|
|
192
|
+
score += feedbackWeight(type);
|
|
227
193
|
}
|
|
228
194
|
return score;
|
|
229
195
|
}
|
|
@@ -260,7 +226,7 @@ function recallMemoryRecords(records = [], query, opts = {}) {
|
|
|
260
226
|
.map(record => {
|
|
261
227
|
const haystack = textOf(record).toLowerCase();
|
|
262
228
|
const lexical = lexicalScore(haystack, q);
|
|
263
|
-
const typeRank = ((
|
|
229
|
+
const typeRank = (memoryTypeRecallRank(record.memoryType || record.memory_type) / 100) * RETRIEVAL_TYPE_BOOST;
|
|
264
230
|
const feedback = feedbackScore(record, feedbackEvents);
|
|
265
231
|
return {
|
|
266
232
|
...record,
|
|
@@ -330,6 +296,9 @@ function createMemoryRecall({ pool, schema, defaultTenantId }) {
|
|
|
330
296
|
const where = [
|
|
331
297
|
`m.tenant_id = $1`,
|
|
332
298
|
`m.status = 'active'`,
|
|
299
|
+
`m.revoked_at IS NULL`,
|
|
300
|
+
`m.superseded_at IS NULL`,
|
|
301
|
+
`m.superseded_by IS NULL`,
|
|
333
302
|
`m.visible_in_recall = true`,
|
|
334
303
|
`(m.search_tsv @@ plainto_tsquery('${cfg}', $2)
|
|
335
304
|
OR m.title ILIKE '%' || $2 || '%'
|
|
@@ -352,7 +321,7 @@ function createMemoryRecall({ pool, schema, defaultTenantId }) {
|
|
|
352
321
|
+ ${TYPE_BOOST_SQL}
|
|
353
322
|
+ ${feedbackScoreExpr} AS recall_score
|
|
354
323
|
FROM ${schema}.memory_records m
|
|
355
|
-
JOIN ${schema}.scopes s ON s.id = m.scope_id
|
|
324
|
+
JOIN ${schema}.scopes s ON s.tenant_id = m.tenant_id AND s.id = m.scope_id
|
|
356
325
|
WHERE ${where.join(' AND ')}
|
|
357
326
|
ORDER BY
|
|
358
327
|
title_match DESC,
|
|
@@ -383,6 +352,9 @@ function createMemoryRecall({ pool, schema, defaultTenantId }) {
|
|
|
383
352
|
const where = [
|
|
384
353
|
`m.tenant_id = $1`,
|
|
385
354
|
`m.status = 'active'`,
|
|
355
|
+
`m.revoked_at IS NULL`,
|
|
356
|
+
`m.superseded_at IS NULL`,
|
|
357
|
+
`m.superseded_by IS NULL`,
|
|
386
358
|
`m.visible_in_recall = true`,
|
|
387
359
|
];
|
|
388
360
|
applyCurrentMemoryFilters(where, params, scopedOpts);
|
|
@@ -403,7 +375,7 @@ function createMemoryRecall({ pool, schema, defaultTenantId }) {
|
|
|
403
375
|
`WITH eligible_memories AS (
|
|
404
376
|
SELECT m.*, s.scope_kind, s.scope_key, s.inheritance_mode AS scope_inheritance_mode
|
|
405
377
|
FROM ${schema}.memory_records m
|
|
406
|
-
JOIN ${schema}.scopes s ON s.id = m.scope_id
|
|
378
|
+
JOIN ${schema}.scopes s ON s.tenant_id = m.tenant_id AND s.id = m.scope_id
|
|
407
379
|
WHERE ${where.join(' AND ')}
|
|
408
380
|
),
|
|
409
381
|
evidence_hits AS (
|
|
@@ -469,6 +441,9 @@ function createMemoryRecall({ pool, schema, defaultTenantId }) {
|
|
|
469
441
|
const where = [
|
|
470
442
|
`m.tenant_id = $1`,
|
|
471
443
|
`m.status = 'active'`,
|
|
444
|
+
`m.revoked_at IS NULL`,
|
|
445
|
+
`m.superseded_at IS NULL`,
|
|
446
|
+
`m.superseded_by IS NULL`,
|
|
472
447
|
`m.visible_in_recall = true`,
|
|
473
448
|
`m.embedding IS NOT NULL`,
|
|
474
449
|
];
|
|
@@ -488,7 +463,7 @@ function createMemoryRecall({ pool, schema, defaultTenantId }) {
|
|
|
488
463
|
+ ${TYPE_BOOST_SQL}
|
|
489
464
|
+ ${feedbackScoreExpr} AS recall_score
|
|
490
465
|
FROM ${schema}.memory_records m
|
|
491
|
-
JOIN ${schema}.scopes s ON s.id = m.scope_id
|
|
466
|
+
JOIN ${schema}.scopes s ON s.tenant_id = m.tenant_id AND s.id = m.scope_id
|
|
492
467
|
WHERE ${where.join(' AND ')}
|
|
493
468
|
ORDER BY
|
|
494
469
|
m.embedding <=> $2::vector ASC,
|
|
@@ -518,6 +493,9 @@ function createMemoryRecall({ pool, schema, defaultTenantId }) {
|
|
|
518
493
|
const where = [
|
|
519
494
|
`m.tenant_id = $1`,
|
|
520
495
|
`m.status = 'active'`,
|
|
496
|
+
`m.revoked_at IS NULL`,
|
|
497
|
+
`m.superseded_at IS NULL`,
|
|
498
|
+
`m.superseded_by IS NULL`,
|
|
521
499
|
`m.visible_in_recall = true`,
|
|
522
500
|
];
|
|
523
501
|
applyCurrentMemoryFilters(where, params, scopedOpts);
|
|
@@ -526,7 +504,7 @@ function createMemoryRecall({ pool, schema, defaultTenantId }) {
|
|
|
526
504
|
`WITH eligible_memories AS (
|
|
527
505
|
SELECT m.*, s.scope_kind, s.scope_key, s.inheritance_mode AS scope_inheritance_mode
|
|
528
506
|
FROM ${schema}.memory_records m
|
|
529
|
-
JOIN ${schema}.scopes s ON s.id = m.scope_id
|
|
507
|
+
JOIN ${schema}.scopes s ON s.tenant_id = m.tenant_id AND s.id = m.scope_id
|
|
530
508
|
WHERE ${where.join(' AND ')}
|
|
531
509
|
),
|
|
532
510
|
linked_summary_hits AS (
|
package/core/memory-records.js
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
const crypto = require('crypto');
|
|
4
4
|
const { resolveApplicableRecords } = require('./memory-bootstrap');
|
|
5
5
|
const { assertAllowedScopeRequest } = require('./memory-serving');
|
|
6
|
+
const {
|
|
7
|
+
authoritySortPriority,
|
|
8
|
+
authoritySortPrioritySql,
|
|
9
|
+
feedbackWeightSql,
|
|
10
|
+
memoryTypeBootstrapPriority,
|
|
11
|
+
memoryTypeBootstrapPrioritySql,
|
|
12
|
+
memoryTypeRuntimeContract,
|
|
13
|
+
} = require('./memory-type-policy');
|
|
6
14
|
|
|
7
15
|
function requireField(obj, field) {
|
|
8
16
|
if (!obj || obj[field] === undefined || obj[field] === null || obj[field] === '') {
|
|
@@ -31,57 +39,33 @@ function advisoryLockKeys(namespace, value) {
|
|
|
31
39
|
return [digest.readInt32BE(0), digest.readInt32BE(4)];
|
|
32
40
|
}
|
|
33
41
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
WHEN 'decision' THEN 3
|
|
40
|
-
WHEN 'preference' THEN 4
|
|
41
|
-
WHEN 'fact' THEN 5
|
|
42
|
-
WHEN 'conclusion' THEN 6
|
|
43
|
-
WHEN 'entity_note' THEN 7
|
|
44
|
-
ELSE 99
|
|
45
|
-
END ASC,
|
|
46
|
-
CASE m.authority
|
|
47
|
-
WHEN 'user_explicit' THEN 0
|
|
48
|
-
WHEN 'executable_evidence' THEN 1
|
|
49
|
-
WHEN 'manual' THEN 2
|
|
50
|
-
WHEN 'system' THEN 3
|
|
51
|
-
WHEN 'verified_summary' THEN 4
|
|
52
|
-
WHEN 'llm_inference' THEN 5
|
|
53
|
-
WHEN 'raw_transcript' THEN 6
|
|
54
|
-
ELSE 99
|
|
55
|
-
END ASC,
|
|
42
|
+
function bootstrapOrderSql(feedbackScoreExpr = '0') {
|
|
43
|
+
return `
|
|
44
|
+
${memoryTypeBootstrapPrioritySql('m.memory_type')} ASC,
|
|
45
|
+
${authoritySortPrioritySql('m.authority')} ASC,
|
|
46
|
+
COALESCE((${feedbackScoreExpr}), 0) DESC,
|
|
56
47
|
m.accepted_at DESC NULLS LAST,
|
|
57
48
|
m.id ASC`;
|
|
58
|
-
|
|
59
|
-
const CURRENT_TYPE_PRIORITY = {
|
|
60
|
-
constraint: 0,
|
|
61
|
-
state: 1,
|
|
62
|
-
open_loop: 2,
|
|
63
|
-
decision: 3,
|
|
64
|
-
preference: 4,
|
|
65
|
-
fact: 5,
|
|
66
|
-
conclusion: 6,
|
|
67
|
-
entity_note: 7,
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
const CURRENT_AUTHORITY_PRIORITY = {
|
|
71
|
-
user_explicit: 0,
|
|
72
|
-
executable_evidence: 1,
|
|
73
|
-
manual: 2,
|
|
74
|
-
system: 3,
|
|
75
|
-
verified_summary: 4,
|
|
76
|
-
llm_inference: 5,
|
|
77
|
-
raw_transcript: 6,
|
|
78
|
-
};
|
|
49
|
+
}
|
|
79
50
|
|
|
80
51
|
function parseTime(value) {
|
|
81
52
|
const parsed = Date.parse(value || '');
|
|
82
53
|
return Number.isFinite(parsed) ? parsed : null;
|
|
83
54
|
}
|
|
84
55
|
|
|
56
|
+
function memoryFeedbackScoreSql(feedbackTable, feedbackTableAlias = 'f') {
|
|
57
|
+
return `
|
|
58
|
+
COALESCE((
|
|
59
|
+
SELECT SUM(
|
|
60
|
+
${feedbackWeightSql(`${feedbackTableAlias}.feedback_type`)}
|
|
61
|
+
)
|
|
62
|
+
FROM ${feedbackTable} ${feedbackTableAlias}
|
|
63
|
+
WHERE ${feedbackTableAlias}.tenant_id = m.tenant_id
|
|
64
|
+
AND ${feedbackTableAlias}.target_kind = 'memory_record'
|
|
65
|
+
AND ${feedbackTableAlias}.target_id = m.id::text
|
|
66
|
+
), 0)`;
|
|
67
|
+
}
|
|
68
|
+
|
|
85
69
|
function normalizeScopePath(activeScopePath, activeScopeKey) {
|
|
86
70
|
const source = Array.isArray(activeScopePath)
|
|
87
71
|
? activeScopePath
|
|
@@ -128,6 +112,7 @@ function normalizeCurrentMemoryRow(row = {}) {
|
|
|
128
112
|
inheritanceMode: row.inheritanceMode ?? row.inheritance_mode ?? row.scope_inheritance_mode ?? null,
|
|
129
113
|
visibleInBootstrap: row.visibleInBootstrap ?? row.visible_in_bootstrap ?? false,
|
|
130
114
|
visibleInRecall: row.visibleInRecall ?? row.visible_in_recall ?? false,
|
|
115
|
+
feedbackScore: Number(row.feedbackScore ?? row.feedback_score) || 0,
|
|
131
116
|
acceptedAt: row.acceptedAt ?? row.accepted_at ?? null,
|
|
132
117
|
validFrom: row.validFrom ?? row.valid_from ?? null,
|
|
133
118
|
validTo: row.validTo ?? row.valid_to ?? null,
|
|
@@ -142,18 +127,26 @@ function currentScopePriority(record, positions) {
|
|
|
142
127
|
}
|
|
143
128
|
|
|
144
129
|
function sortCurrentMemoryRecords(a, b, positions) {
|
|
130
|
+
const leftRuntimeContract = memoryTypeRuntimeContract(a.memoryType ?? a.memory_type);
|
|
131
|
+
const rightRuntimeContract = memoryTypeRuntimeContract(b.memoryType ?? b.memory_type);
|
|
132
|
+
if (leftRuntimeContract !== rightRuntimeContract) return leftRuntimeContract ? -1 : 1;
|
|
133
|
+
|
|
145
134
|
const leftScope = currentScopePriority(a, positions);
|
|
146
135
|
const rightScope = currentScopePriority(b, positions);
|
|
147
136
|
if (rightScope !== leftScope) return rightScope - leftScope;
|
|
148
137
|
|
|
149
|
-
const leftType =
|
|
150
|
-
const rightType =
|
|
138
|
+
const leftType = memoryTypeBootstrapPriority(a.memoryType ?? a.memory_type);
|
|
139
|
+
const rightType = memoryTypeBootstrapPriority(b.memoryType ?? b.memory_type);
|
|
151
140
|
if (leftType !== rightType) return leftType - rightType;
|
|
152
141
|
|
|
153
|
-
const leftAuthority =
|
|
154
|
-
const rightAuthority =
|
|
142
|
+
const leftAuthority = authoritySortPriority(a.authority);
|
|
143
|
+
const rightAuthority = authoritySortPriority(b.authority);
|
|
155
144
|
if (leftAuthority !== rightAuthority) return leftAuthority - rightAuthority;
|
|
156
145
|
|
|
146
|
+
const leftFeedback = Number(a.feedbackScore ?? a.feedback_score) || 0;
|
|
147
|
+
const rightFeedback = Number(b.feedbackScore ?? b.feedback_score) || 0;
|
|
148
|
+
if (leftFeedback !== rightFeedback) return rightFeedback - leftFeedback;
|
|
149
|
+
|
|
157
150
|
const leftAccepted = parseTime(a.acceptedAt ?? a.accepted_at);
|
|
158
151
|
const rightAccepted = parseTime(b.acceptedAt ?? b.accepted_at);
|
|
159
152
|
if (leftAccepted !== rightAccepted) return (rightAccepted ?? 0) - (leftAccepted ?? 0);
|
|
@@ -270,29 +263,7 @@ function createMemoryRecords({ pool, schema, defaultTenantId, inTransaction = fa
|
|
|
270
263
|
COALESCE($10,'candidate'),COALESCE($11,'llm_inference'),$12,$13,$14,$15,
|
|
271
264
|
$16,$17,$18,$19,$20,$21,COALESCE($22,false),COALESCE($23,false),COALESCE($24::jsonb,'{}'::jsonb),$25,$26,$27::vector
|
|
272
265
|
)
|
|
273
|
-
ON CONFLICT (tenant_id, canonical_key) WHERE status = 'active' DO
|
|
274
|
-
scope_id = EXCLUDED.scope_id,
|
|
275
|
-
context_key = COALESCE(EXCLUDED.context_key, ${memories}.context_key),
|
|
276
|
-
topic_key = COALESCE(EXCLUDED.topic_key, ${memories}.topic_key),
|
|
277
|
-
title = COALESCE(EXCLUDED.title, ${memories}.title),
|
|
278
|
-
summary = COALESCE(NULLIF(EXCLUDED.summary, ''), ${memories}.summary),
|
|
279
|
-
payload = COALESCE(NULLIF(EXCLUDED.payload, '{}'::jsonb), ${memories}.payload),
|
|
280
|
-
authority = EXCLUDED.authority,
|
|
281
|
-
accepted_at = COALESCE(EXCLUDED.accepted_at, ${memories}.accepted_at),
|
|
282
|
-
valid_from = COALESCE(EXCLUDED.valid_from, ${memories}.valid_from),
|
|
283
|
-
valid_to = COALESCE(EXCLUDED.valid_to, ${memories}.valid_to),
|
|
284
|
-
stale_after = COALESCE(EXCLUDED.stale_after, ${memories}.stale_after),
|
|
285
|
-
version_id = COALESCE(EXCLUDED.version_id, ${memories}.version_id),
|
|
286
|
-
backing_fact_id = COALESCE(EXCLUDED.backing_fact_id, ${memories}.backing_fact_id),
|
|
287
|
-
observed_at = COALESCE(EXCLUDED.observed_at, ${memories}.observed_at),
|
|
288
|
-
revoked_at = COALESCE(EXCLUDED.revoked_at, ${memories}.revoked_at),
|
|
289
|
-
superseded_at = COALESCE(EXCLUDED.superseded_at, ${memories}.superseded_at),
|
|
290
|
-
visible_in_bootstrap = EXCLUDED.visible_in_bootstrap,
|
|
291
|
-
visible_in_recall = EXCLUDED.visible_in_recall,
|
|
292
|
-
rank_features = COALESCE(NULLIF(EXCLUDED.rank_features, '{}'::jsonb), ${memories}.rank_features),
|
|
293
|
-
created_by_finalization_id = COALESCE(${memories}.created_by_finalization_id, EXCLUDED.created_by_finalization_id),
|
|
294
|
-
created_by_compaction_run_id = COALESCE(${memories}.created_by_compaction_run_id, EXCLUDED.created_by_compaction_run_id),
|
|
295
|
-
embedding = COALESCE(EXCLUDED.embedding, ${memories}.embedding)
|
|
266
|
+
ON CONFLICT (tenant_id, canonical_key) WHERE status = 'active' DO NOTHING
|
|
296
267
|
RETURNING *`,
|
|
297
268
|
[
|
|
298
269
|
tenantId,
|
|
@@ -349,28 +320,7 @@ function createMemoryRecords({ pool, schema, defaultTenantId, inTransaction = fa
|
|
|
349
320
|
$10,$11,$12,$13,$14,$15,$16,COALESCE($17,'active'),COALESCE($18,'verified_summary'),
|
|
350
321
|
$19,$20,$21,COALESCE($22::jsonb,'{}'::jsonb),$23,$24
|
|
351
322
|
)
|
|
352
|
-
ON CONFLICT (tenant_id, canonical_key) WHERE status = 'active' DO
|
|
353
|
-
scope_id = EXCLUDED.scope_id,
|
|
354
|
-
subject_entity_id = COALESCE(EXCLUDED.subject_entity_id, ${factAssertions}.subject_entity_id),
|
|
355
|
-
predicate = EXCLUDED.predicate,
|
|
356
|
-
object_kind = EXCLUDED.object_kind,
|
|
357
|
-
object_entity_id = COALESCE(EXCLUDED.object_entity_id, ${factAssertions}.object_entity_id),
|
|
358
|
-
object_value_json = EXCLUDED.object_value_json,
|
|
359
|
-
qualifiers_json = COALESCE(NULLIF(EXCLUDED.qualifiers_json, '{}'::jsonb), ${factAssertions}.qualifiers_json),
|
|
360
|
-
valid_from = COALESCE(EXCLUDED.valid_from, ${factAssertions}.valid_from),
|
|
361
|
-
valid_to = COALESCE(EXCLUDED.valid_to, ${factAssertions}.valid_to),
|
|
362
|
-
observed_at = COALESCE(EXCLUDED.observed_at, ${factAssertions}.observed_at),
|
|
363
|
-
stale_after = COALESCE(EXCLUDED.stale_after, ${factAssertions}.stale_after),
|
|
364
|
-
accepted_at = COALESCE(EXCLUDED.accepted_at, ${factAssertions}.accepted_at),
|
|
365
|
-
revoked_at = COALESCE(EXCLUDED.revoked_at, ${factAssertions}.revoked_at),
|
|
366
|
-
superseded_at = COALESCE(EXCLUDED.superseded_at, ${factAssertions}.superseded_at),
|
|
367
|
-
authority = EXCLUDED.authority,
|
|
368
|
-
assertion_hash = EXCLUDED.assertion_hash,
|
|
369
|
-
version_id = COALESCE(EXCLUDED.version_id, ${factAssertions}.version_id),
|
|
370
|
-
metadata = COALESCE(NULLIF(EXCLUDED.metadata, '{}'::jsonb), ${factAssertions}.metadata),
|
|
371
|
-
created_by_finalization_id = COALESCE(${factAssertions}.created_by_finalization_id, EXCLUDED.created_by_finalization_id),
|
|
372
|
-
created_by_compaction_run_id = COALESCE(${factAssertions}.created_by_compaction_run_id, EXCLUDED.created_by_compaction_run_id),
|
|
373
|
-
updated_at = now()
|
|
323
|
+
ON CONFLICT (tenant_id, canonical_key) WHERE status = 'active' DO NOTHING
|
|
374
324
|
RETURNING *`,
|
|
375
325
|
[
|
|
376
326
|
tenantId,
|
|
@@ -524,10 +474,13 @@ function createMemoryRecords({ pool, schema, defaultTenantId, inTransaction = fa
|
|
|
524
474
|
const result = await pool.query(
|
|
525
475
|
`SELECT m.*, s.scope_kind, s.scope_key, s.inheritance_mode AS scope_inheritance_mode
|
|
526
476
|
FROM ${memories} m
|
|
527
|
-
JOIN ${scopes} s ON s.id = m.scope_id
|
|
477
|
+
JOIN ${scopes} s ON s.tenant_id = m.tenant_id AND s.id = m.scope_id
|
|
528
478
|
WHERE m.tenant_id = $1
|
|
529
479
|
AND m.canonical_key = $2
|
|
530
480
|
AND m.status = 'active'
|
|
481
|
+
AND m.revoked_at IS NULL
|
|
482
|
+
AND m.superseded_at IS NULL
|
|
483
|
+
AND m.superseded_by IS NULL
|
|
531
484
|
ORDER BY m.accepted_at DESC NULLS LAST, m.id ASC
|
|
532
485
|
${lockClause}`,
|
|
533
486
|
[tenantId, input.canonicalKey]
|
|
@@ -542,10 +495,13 @@ function createMemoryRecords({ pool, schema, defaultTenantId, inTransaction = fa
|
|
|
542
495
|
const result = await pool.query(
|
|
543
496
|
`SELECT f.*, s.scope_kind, s.scope_key, s.inheritance_mode AS scope_inheritance_mode
|
|
544
497
|
FROM ${factAssertions} f
|
|
545
|
-
JOIN ${scopes} s ON s.id = f.scope_id
|
|
498
|
+
JOIN ${scopes} s ON s.tenant_id = f.tenant_id AND s.id = f.scope_id
|
|
546
499
|
WHERE f.tenant_id = $1
|
|
547
500
|
AND f.canonical_key = $2
|
|
548
501
|
AND f.status = 'active'
|
|
502
|
+
AND f.revoked_at IS NULL
|
|
503
|
+
AND f.superseded_at IS NULL
|
|
504
|
+
AND f.superseded_by IS NULL
|
|
549
505
|
ORDER BY f.accepted_at DESC NULLS LAST, f.id ASC
|
|
550
506
|
${lockClause}`,
|
|
551
507
|
[tenantId, input.canonicalKey]
|
|
@@ -572,14 +528,19 @@ function createMemoryRecords({ pool, schema, defaultTenantId, inTransaction = fa
|
|
|
572
528
|
const result = await pool.query(
|
|
573
529
|
`UPDATE ${memories}
|
|
574
530
|
SET status = $3,
|
|
575
|
-
superseded_by =
|
|
531
|
+
superseded_by = CASE
|
|
532
|
+
WHEN $3 = 'active' THEN NULL
|
|
533
|
+
ELSE COALESCE($4, superseded_by)
|
|
534
|
+
END,
|
|
576
535
|
valid_to = COALESCE($5, valid_to),
|
|
577
536
|
superseded_at = CASE
|
|
578
537
|
WHEN $3 = 'superseded' THEN COALESCE($8, superseded_at, now())
|
|
538
|
+
WHEN $3 = 'active' THEN NULL
|
|
579
539
|
ELSE superseded_at
|
|
580
540
|
END,
|
|
581
541
|
revoked_at = CASE
|
|
582
542
|
WHEN $3 = 'revoked' THEN COALESCE($9, revoked_at, now())
|
|
543
|
+
WHEN $3 = 'active' THEN NULL
|
|
583
544
|
ELSE revoked_at
|
|
584
545
|
END,
|
|
585
546
|
visible_in_bootstrap = $6,
|
|
@@ -612,14 +573,19 @@ function createMemoryRecords({ pool, schema, defaultTenantId, inTransaction = fa
|
|
|
612
573
|
const result = await pool.query(
|
|
613
574
|
`UPDATE ${memories}
|
|
614
575
|
SET status = $4,
|
|
615
|
-
superseded_by =
|
|
576
|
+
superseded_by = CASE
|
|
577
|
+
WHEN $4 = 'active' THEN NULL
|
|
578
|
+
ELSE COALESCE($5, superseded_by)
|
|
579
|
+
END,
|
|
616
580
|
valid_to = COALESCE($6, valid_to),
|
|
617
581
|
superseded_at = CASE
|
|
618
582
|
WHEN $4 = 'superseded' THEN COALESCE($9, superseded_at, now())
|
|
583
|
+
WHEN $4 = 'active' THEN NULL
|
|
619
584
|
ELSE superseded_at
|
|
620
585
|
END,
|
|
621
586
|
revoked_at = CASE
|
|
622
587
|
WHEN $4 = 'revoked' THEN COALESCE($10, revoked_at, now())
|
|
588
|
+
WHEN $4 = 'active' THEN NULL
|
|
623
589
|
ELSE revoked_at
|
|
624
590
|
END,
|
|
625
591
|
visible_in_bootstrap = $7,
|
|
@@ -650,14 +616,19 @@ function createMemoryRecords({ pool, schema, defaultTenantId, inTransaction = fa
|
|
|
650
616
|
const result = await pool.query(
|
|
651
617
|
`UPDATE ${factAssertions}
|
|
652
618
|
SET status = $3,
|
|
653
|
-
superseded_by =
|
|
619
|
+
superseded_by = CASE
|
|
620
|
+
WHEN $3 = 'active' THEN NULL
|
|
621
|
+
ELSE COALESCE($4, superseded_by)
|
|
622
|
+
END,
|
|
654
623
|
valid_to = COALESCE($5, valid_to),
|
|
655
624
|
superseded_at = CASE
|
|
656
625
|
WHEN $3 = 'superseded' THEN COALESCE($6, superseded_at, now())
|
|
626
|
+
WHEN $3 = 'active' THEN NULL
|
|
657
627
|
ELSE superseded_at
|
|
658
628
|
END,
|
|
659
629
|
revoked_at = CASE
|
|
660
630
|
WHEN $3 = 'revoked' THEN COALESCE($7, revoked_at, now())
|
|
631
|
+
WHEN $3 = 'active' THEN NULL
|
|
661
632
|
ELSE revoked_at
|
|
662
633
|
END,
|
|
663
634
|
updated_at = now()
|
|
@@ -679,7 +650,13 @@ function createMemoryRecords({ pool, schema, defaultTenantId, inTransaction = fa
|
|
|
679
650
|
async function listActive(input = {}) {
|
|
680
651
|
const tenantId = input.tenantId || defaultTenantId;
|
|
681
652
|
const params = [tenantId];
|
|
682
|
-
const where = [
|
|
653
|
+
const where = [
|
|
654
|
+
`m.tenant_id = $1`,
|
|
655
|
+
`m.status = 'active'`,
|
|
656
|
+
`m.revoked_at IS NULL`,
|
|
657
|
+
`m.superseded_at IS NULL`,
|
|
658
|
+
`m.superseded_by IS NULL`,
|
|
659
|
+
];
|
|
683
660
|
if (input.asOf) {
|
|
684
661
|
params.push(input.asOf);
|
|
685
662
|
const at = `$${params.length}::timestamptz`;
|
|
@@ -707,13 +684,18 @@ function createMemoryRecords({ pool, schema, defaultTenantId, inTransaction = fa
|
|
|
707
684
|
where.push(`m.embedding IS NULL`);
|
|
708
685
|
}
|
|
709
686
|
params.push(Math.max(1, Math.min(200, input.limit || 50)));
|
|
687
|
+
const feedbackScoreExpr = input.includeFeedbackScore === true
|
|
688
|
+
? memoryFeedbackScoreSql(feedback)
|
|
689
|
+
: '0';
|
|
690
|
+
const feedbackScoreSelect = `${feedbackScoreExpr}::real AS feedback_score`;
|
|
710
691
|
const orderBy = input.visibleInBootstrap === true
|
|
711
|
-
?
|
|
692
|
+
? bootstrapOrderSql(feedbackScoreExpr)
|
|
712
693
|
: `m.accepted_at DESC NULLS LAST, m.id ASC`;
|
|
713
694
|
const result = await pool.query(
|
|
714
|
-
`SELECT m.*, s.scope_kind, s.scope_key, s.inheritance_mode AS scope_inheritance_mode
|
|
695
|
+
`SELECT m.*, s.scope_kind, s.scope_key, s.inheritance_mode AS scope_inheritance_mode,
|
|
696
|
+
${feedbackScoreSelect}
|
|
715
697
|
FROM ${memories} m
|
|
716
|
-
JOIN ${scopes} s ON s.id = m.scope_id
|
|
698
|
+
JOIN ${scopes} s ON s.tenant_id = m.tenant_id AND s.id = m.scope_id
|
|
717
699
|
WHERE ${where.join(' AND ')}
|
|
718
700
|
ORDER BY ${orderBy}
|
|
719
701
|
LIMIT $${params.length}`,
|
|
@@ -802,6 +784,9 @@ function createMemoryRecords({ pool, schema, defaultTenantId, inTransaction = fa
|
|
|
802
784
|
const where = [
|
|
803
785
|
`m.tenant_id = $1`,
|
|
804
786
|
`m.status = 'active'`,
|
|
787
|
+
`m.revoked_at IS NULL`,
|
|
788
|
+
`m.superseded_at IS NULL`,
|
|
789
|
+
`m.superseded_by IS NULL`,
|
|
805
790
|
`s.scope_key = ANY($2::text[])`,
|
|
806
791
|
`(m.visible_in_bootstrap = true OR m.visible_in_recall = true)`,
|
|
807
792
|
`(m.valid_from IS NULL OR m.valid_from <= $3::timestamptz)`,
|
|
@@ -836,18 +821,21 @@ function createMemoryRecords({ pool, schema, defaultTenantId, inTransaction = fa
|
|
|
836
821
|
), '[]'::jsonb)`
|
|
837
822
|
: `'[]'::jsonb`;
|
|
838
823
|
|
|
824
|
+
const feedbackScoreExpr = memoryFeedbackScoreSql(feedback);
|
|
825
|
+
const feedbackScoreSelect = `${feedbackScoreExpr}::real AS feedback_score`;
|
|
839
826
|
const result = await pool.query(
|
|
840
827
|
`SELECT
|
|
841
828
|
m.*,
|
|
842
829
|
s.scope_kind,
|
|
843
830
|
s.scope_key,
|
|
844
831
|
s.inheritance_mode AS scope_inheritance_mode,
|
|
832
|
+
${feedbackScoreSelect},
|
|
845
833
|
${evidenceRefsSelect} AS evidence_refs
|
|
846
834
|
FROM ${memories} m
|
|
847
|
-
JOIN ${scopes} s ON s.id = m.scope_id
|
|
835
|
+
JOIN ${scopes} s ON s.tenant_id = m.tenant_id AND s.id = m.scope_id
|
|
848
836
|
WHERE ${where.join(' AND ')}
|
|
849
837
|
ORDER BY array_position($2::text[], s.scope_key) DESC NULLS LAST,
|
|
850
|
-
${
|
|
838
|
+
${bootstrapOrderSql(feedbackScoreExpr)}
|
|
851
839
|
LIMIT ${limitParam}`,
|
|
852
840
|
params,
|
|
853
841
|
);
|