@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.
Files changed (43) hide show
  1. package/README.md +33 -4
  2. package/README_CN.md +9 -1
  3. package/README_TW.md +5 -2
  4. package/consumers/cli.js +55 -34
  5. package/consumers/codex-active-checkpoint.js +3 -1
  6. package/consumers/codex-current-memory.js +10 -6
  7. package/consumers/codex.js +5 -2
  8. package/consumers/default/daily-entries.js +2 -2
  9. package/consumers/default/index.js +40 -30
  10. package/consumers/default/prompts/summary.js +2 -2
  11. package/consumers/mcp.js +56 -49
  12. package/consumers/openclaw-ext/index.js +1 -1
  13. package/consumers/openclaw-ext/openclaw.plugin.json +1 -1
  14. package/consumers/openclaw-ext/package.json +1 -1
  15. package/consumers/openclaw-plugin.js +66 -23
  16. package/consumers/shared/compat-recall.js +101 -0
  17. package/consumers/shared/openclaw-product-tools.js +130 -0
  18. package/consumers/shared/recall-format.js +2 -2
  19. package/core/aquifer.js +385 -20
  20. package/core/backends/local.js +60 -1
  21. package/core/finalization-review.js +88 -42
  22. package/core/interface.js +629 -0
  23. package/core/mcp-manifest.js +11 -3
  24. package/core/memory-bootstrap.js +25 -27
  25. package/core/memory-consolidation.js +564 -42
  26. package/core/memory-explain.js +20 -51
  27. package/core/memory-promotion.js +392 -55
  28. package/core/memory-recall.js +26 -48
  29. package/core/memory-records.js +91 -103
  30. package/core/memory-type-policy.js +298 -0
  31. package/core/postgres-migrations.js +9 -0
  32. package/core/session-checkpoint-producer.js +3 -1
  33. package/core/session-checkpoints.js +1 -1
  34. package/core/session-finalization.js +2 -2
  35. package/docs/getting-started.md +16 -3
  36. package/docs/setup.md +61 -2
  37. package/package.json +2 -2
  38. package/schema/004-completion.sql +4 -4
  39. package/schema/010-v1-finalization-review.sql +72 -0
  40. package/schema/020-v1-assistant-shaping-memory.sql +30 -0
  41. package/scripts/backfill-canonical-key.js +1 -1
  42. package/scripts/diagnose-fts-zh.js +1 -1
  43. package/scripts/extract-insights-from-recent-sessions.js +4 -4
@@ -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
- const TYPE_RANK = {
8
- constraint: 80,
9
- preference: 70,
10
- state: 60,
11
- open_loop: 55,
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
- CASE f.feedback_type
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 += FEEDBACK_WEIGHT[type] || 0;
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 = ((TYPE_RANK[record.memoryType || record.memory_type] || 0) / 100) * RETRIEVAL_TYPE_BOOST;
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 (
@@ -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
- const BOOTSTRAP_ORDER_SQL = `
35
- CASE m.memory_type
36
- WHEN 'constraint' THEN 0
37
- WHEN 'state' THEN 1
38
- WHEN 'open_loop' THEN 2
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 = CURRENT_TYPE_PRIORITY[a.memoryType ?? a.memory_type] ?? 99;
150
- const rightType = CURRENT_TYPE_PRIORITY[b.memoryType ?? b.memory_type] ?? 99;
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 = CURRENT_AUTHORITY_PRIORITY[a.authority] ?? 99;
154
- const rightAuthority = CURRENT_AUTHORITY_PRIORITY[b.authority] ?? 99;
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 UPDATE SET
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 UPDATE SET
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 = COALESCE($4, 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 = COALESCE($5, 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 = COALESCE($4, 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 = [`m.tenant_id = $1`, `m.status = 'active'`];
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
- ? BOOTSTRAP_ORDER_SQL
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
- ${BOOTSTRAP_ORDER_SQL}
838
+ ${bootstrapOrderSql(feedbackScoreExpr)}
851
839
  LIMIT ${limitParam}`,
852
840
  params,
853
841
  );