@shadowforge0/aquifer-memory 1.8.1 → 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 (57) hide show
  1. package/.env.example +1 -0
  2. package/README.md +82 -26
  3. package/README_CN.md +33 -23
  4. package/README_TW.md +25 -24
  5. package/aquifer.config.example.json +2 -1
  6. package/consumers/cli.js +587 -33
  7. package/consumers/codex-active-checkpoint.js +3 -1
  8. package/consumers/codex-current-memory.js +10 -6
  9. package/consumers/codex.js +6 -3
  10. package/consumers/default/daily-entries.js +2 -2
  11. package/consumers/default/index.js +40 -30
  12. package/consumers/default/prompts/summary.js +2 -2
  13. package/consumers/mcp.js +56 -46
  14. package/consumers/openclaw-ext/index.js +65 -7
  15. package/consumers/openclaw-ext/openclaw.plugin.json +1 -1
  16. package/consumers/openclaw-ext/package.json +1 -1
  17. package/consumers/openclaw-install.js +326 -0
  18. package/consumers/openclaw-plugin.js +105 -24
  19. package/consumers/shared/compat-recall.js +101 -0
  20. package/consumers/shared/config.js +2 -0
  21. package/consumers/shared/openclaw-product-tools.js +130 -0
  22. package/consumers/shared/recall-format.js +2 -2
  23. package/core/aquifer.js +553 -41
  24. package/core/backends/local.js +169 -1
  25. package/core/doctor.js +924 -0
  26. package/core/finalization-inspector.js +164 -0
  27. package/core/finalization-review.js +88 -42
  28. package/core/interface.js +629 -0
  29. package/core/mcp-manifest.js +11 -3
  30. package/core/memory-bootstrap.js +25 -27
  31. package/core/memory-consolidation.js +564 -42
  32. package/core/memory-explain.js +593 -0
  33. package/core/memory-promotion.js +392 -55
  34. package/core/memory-recall.js +75 -71
  35. package/core/memory-records.js +107 -108
  36. package/core/memory-review.js +891 -0
  37. package/core/memory-serving.js +61 -4
  38. package/core/memory-type-policy.js +298 -0
  39. package/core/operator-observability.js +249 -0
  40. package/core/postgres-migrations.js +22 -0
  41. package/core/session-checkpoint-producer.js +3 -1
  42. package/core/session-checkpoints.js +1 -1
  43. package/core/session-finalization.js +78 -3
  44. package/core/storage.js +124 -8
  45. package/docs/getting-started.md +50 -4
  46. package/docs/setup.md +163 -24
  47. package/package.json +5 -4
  48. package/schema/004-completion.sql +4 -4
  49. package/schema/010-v1-finalization-review.sql +72 -0
  50. package/schema/019-v1-memory-review-resolutions.sql +53 -0
  51. package/schema/020-v1-assistant-shaping-memory.sql +30 -0
  52. package/scripts/backfill-canonical-key.js +1 -1
  53. package/scripts/codex-checkpoint-commands.js +28 -0
  54. package/scripts/codex-checkpoint-runtime.js +109 -0
  55. package/scripts/codex-recovery.js +16 -4
  56. package/scripts/diagnose-fts-zh.js +1 -1
  57. package/scripts/extract-insights-from-recent-sessions.js +4 -4
@@ -2,6 +2,15 @@
2
2
 
3
3
  const crypto = require('crypto');
4
4
  const { resolveApplicableRecords } = require('./memory-bootstrap');
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');
5
14
 
6
15
  function requireField(obj, field) {
7
16
  if (!obj || obj[field] === undefined || obj[field] === null || obj[field] === '') {
@@ -30,57 +39,33 @@ function advisoryLockKeys(namespace, value) {
30
39
  return [digest.readInt32BE(0), digest.readInt32BE(4)];
31
40
  }
32
41
 
33
- const BOOTSTRAP_ORDER_SQL = `
34
- CASE m.memory_type
35
- WHEN 'constraint' THEN 0
36
- WHEN 'state' THEN 1
37
- WHEN 'open_loop' THEN 2
38
- WHEN 'decision' THEN 3
39
- WHEN 'preference' THEN 4
40
- WHEN 'fact' THEN 5
41
- WHEN 'conclusion' THEN 6
42
- WHEN 'entity_note' THEN 7
43
- ELSE 99
44
- END ASC,
45
- CASE m.authority
46
- WHEN 'user_explicit' THEN 0
47
- WHEN 'executable_evidence' THEN 1
48
- WHEN 'manual' THEN 2
49
- WHEN 'system' THEN 3
50
- WHEN 'verified_summary' THEN 4
51
- WHEN 'llm_inference' THEN 5
52
- WHEN 'raw_transcript' THEN 6
53
- ELSE 99
54
- 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,
55
47
  m.accepted_at DESC NULLS LAST,
56
48
  m.id ASC`;
57
-
58
- const CURRENT_TYPE_PRIORITY = {
59
- constraint: 0,
60
- state: 1,
61
- open_loop: 2,
62
- decision: 3,
63
- preference: 4,
64
- fact: 5,
65
- conclusion: 6,
66
- entity_note: 7,
67
- };
68
-
69
- const CURRENT_AUTHORITY_PRIORITY = {
70
- user_explicit: 0,
71
- executable_evidence: 1,
72
- manual: 2,
73
- system: 3,
74
- verified_summary: 4,
75
- llm_inference: 5,
76
- raw_transcript: 6,
77
- };
49
+ }
78
50
 
79
51
  function parseTime(value) {
80
52
  const parsed = Date.parse(value || '');
81
53
  return Number.isFinite(parsed) ? parsed : null;
82
54
  }
83
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
+
84
69
  function normalizeScopePath(activeScopePath, activeScopeKey) {
85
70
  const source = Array.isArray(activeScopePath)
86
71
  ? activeScopePath
@@ -127,6 +112,7 @@ function normalizeCurrentMemoryRow(row = {}) {
127
112
  inheritanceMode: row.inheritanceMode ?? row.inheritance_mode ?? row.scope_inheritance_mode ?? null,
128
113
  visibleInBootstrap: row.visibleInBootstrap ?? row.visible_in_bootstrap ?? false,
129
114
  visibleInRecall: row.visibleInRecall ?? row.visible_in_recall ?? false,
115
+ feedbackScore: Number(row.feedbackScore ?? row.feedback_score) || 0,
130
116
  acceptedAt: row.acceptedAt ?? row.accepted_at ?? null,
131
117
  validFrom: row.validFrom ?? row.valid_from ?? null,
132
118
  validTo: row.validTo ?? row.valid_to ?? null,
@@ -141,18 +127,26 @@ function currentScopePriority(record, positions) {
141
127
  }
142
128
 
143
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
+
144
134
  const leftScope = currentScopePriority(a, positions);
145
135
  const rightScope = currentScopePriority(b, positions);
146
136
  if (rightScope !== leftScope) return rightScope - leftScope;
147
137
 
148
- const leftType = CURRENT_TYPE_PRIORITY[a.memoryType ?? a.memory_type] ?? 99;
149
- 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);
150
140
  if (leftType !== rightType) return leftType - rightType;
151
141
 
152
- const leftAuthority = CURRENT_AUTHORITY_PRIORITY[a.authority] ?? 99;
153
- const rightAuthority = CURRENT_AUTHORITY_PRIORITY[b.authority] ?? 99;
142
+ const leftAuthority = authoritySortPriority(a.authority);
143
+ const rightAuthority = authoritySortPriority(b.authority);
154
144
  if (leftAuthority !== rightAuthority) return leftAuthority - rightAuthority;
155
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
+
156
150
  const leftAccepted = parseTime(a.acceptedAt ?? a.accepted_at);
157
151
  const rightAccepted = parseTime(b.acceptedAt ?? b.accepted_at);
158
152
  if (leftAccepted !== rightAccepted) return (rightAccepted ?? 0) - (leftAccepted ?? 0);
@@ -269,29 +263,7 @@ function createMemoryRecords({ pool, schema, defaultTenantId, inTransaction = fa
269
263
  COALESCE($10,'candidate'),COALESCE($11,'llm_inference'),$12,$13,$14,$15,
270
264
  $16,$17,$18,$19,$20,$21,COALESCE($22,false),COALESCE($23,false),COALESCE($24::jsonb,'{}'::jsonb),$25,$26,$27::vector
271
265
  )
272
- ON CONFLICT (tenant_id, canonical_key) WHERE status = 'active' DO UPDATE SET
273
- scope_id = EXCLUDED.scope_id,
274
- context_key = COALESCE(EXCLUDED.context_key, ${memories}.context_key),
275
- topic_key = COALESCE(EXCLUDED.topic_key, ${memories}.topic_key),
276
- title = COALESCE(EXCLUDED.title, ${memories}.title),
277
- summary = COALESCE(NULLIF(EXCLUDED.summary, ''), ${memories}.summary),
278
- payload = COALESCE(NULLIF(EXCLUDED.payload, '{}'::jsonb), ${memories}.payload),
279
- authority = EXCLUDED.authority,
280
- accepted_at = COALESCE(EXCLUDED.accepted_at, ${memories}.accepted_at),
281
- valid_from = COALESCE(EXCLUDED.valid_from, ${memories}.valid_from),
282
- valid_to = COALESCE(EXCLUDED.valid_to, ${memories}.valid_to),
283
- stale_after = COALESCE(EXCLUDED.stale_after, ${memories}.stale_after),
284
- version_id = COALESCE(EXCLUDED.version_id, ${memories}.version_id),
285
- backing_fact_id = COALESCE(EXCLUDED.backing_fact_id, ${memories}.backing_fact_id),
286
- observed_at = COALESCE(EXCLUDED.observed_at, ${memories}.observed_at),
287
- revoked_at = COALESCE(EXCLUDED.revoked_at, ${memories}.revoked_at),
288
- superseded_at = COALESCE(EXCLUDED.superseded_at, ${memories}.superseded_at),
289
- visible_in_bootstrap = EXCLUDED.visible_in_bootstrap,
290
- visible_in_recall = EXCLUDED.visible_in_recall,
291
- rank_features = COALESCE(NULLIF(EXCLUDED.rank_features, '{}'::jsonb), ${memories}.rank_features),
292
- created_by_finalization_id = COALESCE(${memories}.created_by_finalization_id, EXCLUDED.created_by_finalization_id),
293
- created_by_compaction_run_id = COALESCE(${memories}.created_by_compaction_run_id, EXCLUDED.created_by_compaction_run_id),
294
- embedding = COALESCE(EXCLUDED.embedding, ${memories}.embedding)
266
+ ON CONFLICT (tenant_id, canonical_key) WHERE status = 'active' DO NOTHING
295
267
  RETURNING *`,
296
268
  [
297
269
  tenantId,
@@ -348,28 +320,7 @@ function createMemoryRecords({ pool, schema, defaultTenantId, inTransaction = fa
348
320
  $10,$11,$12,$13,$14,$15,$16,COALESCE($17,'active'),COALESCE($18,'verified_summary'),
349
321
  $19,$20,$21,COALESCE($22::jsonb,'{}'::jsonb),$23,$24
350
322
  )
351
- ON CONFLICT (tenant_id, canonical_key) WHERE status = 'active' DO UPDATE SET
352
- scope_id = EXCLUDED.scope_id,
353
- subject_entity_id = COALESCE(EXCLUDED.subject_entity_id, ${factAssertions}.subject_entity_id),
354
- predicate = EXCLUDED.predicate,
355
- object_kind = EXCLUDED.object_kind,
356
- object_entity_id = COALESCE(EXCLUDED.object_entity_id, ${factAssertions}.object_entity_id),
357
- object_value_json = EXCLUDED.object_value_json,
358
- qualifiers_json = COALESCE(NULLIF(EXCLUDED.qualifiers_json, '{}'::jsonb), ${factAssertions}.qualifiers_json),
359
- valid_from = COALESCE(EXCLUDED.valid_from, ${factAssertions}.valid_from),
360
- valid_to = COALESCE(EXCLUDED.valid_to, ${factAssertions}.valid_to),
361
- observed_at = COALESCE(EXCLUDED.observed_at, ${factAssertions}.observed_at),
362
- stale_after = COALESCE(EXCLUDED.stale_after, ${factAssertions}.stale_after),
363
- accepted_at = COALESCE(EXCLUDED.accepted_at, ${factAssertions}.accepted_at),
364
- revoked_at = COALESCE(EXCLUDED.revoked_at, ${factAssertions}.revoked_at),
365
- superseded_at = COALESCE(EXCLUDED.superseded_at, ${factAssertions}.superseded_at),
366
- authority = EXCLUDED.authority,
367
- assertion_hash = EXCLUDED.assertion_hash,
368
- version_id = COALESCE(EXCLUDED.version_id, ${factAssertions}.version_id),
369
- metadata = COALESCE(NULLIF(EXCLUDED.metadata, '{}'::jsonb), ${factAssertions}.metadata),
370
- created_by_finalization_id = COALESCE(${factAssertions}.created_by_finalization_id, EXCLUDED.created_by_finalization_id),
371
- created_by_compaction_run_id = COALESCE(${factAssertions}.created_by_compaction_run_id, EXCLUDED.created_by_compaction_run_id),
372
- updated_at = now()
323
+ ON CONFLICT (tenant_id, canonical_key) WHERE status = 'active' DO NOTHING
373
324
  RETURNING *`,
374
325
  [
375
326
  tenantId,
@@ -523,10 +474,13 @@ function createMemoryRecords({ pool, schema, defaultTenantId, inTransaction = fa
523
474
  const result = await pool.query(
524
475
  `SELECT m.*, s.scope_kind, s.scope_key, s.inheritance_mode AS scope_inheritance_mode
525
476
  FROM ${memories} m
526
- 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
527
478
  WHERE m.tenant_id = $1
528
479
  AND m.canonical_key = $2
529
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
530
484
  ORDER BY m.accepted_at DESC NULLS LAST, m.id ASC
531
485
  ${lockClause}`,
532
486
  [tenantId, input.canonicalKey]
@@ -541,10 +495,13 @@ function createMemoryRecords({ pool, schema, defaultTenantId, inTransaction = fa
541
495
  const result = await pool.query(
542
496
  `SELECT f.*, s.scope_kind, s.scope_key, s.inheritance_mode AS scope_inheritance_mode
543
497
  FROM ${factAssertions} f
544
- 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
545
499
  WHERE f.tenant_id = $1
546
500
  AND f.canonical_key = $2
547
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
548
505
  ORDER BY f.accepted_at DESC NULLS LAST, f.id ASC
549
506
  ${lockClause}`,
550
507
  [tenantId, input.canonicalKey]
@@ -571,14 +528,19 @@ function createMemoryRecords({ pool, schema, defaultTenantId, inTransaction = fa
571
528
  const result = await pool.query(
572
529
  `UPDATE ${memories}
573
530
  SET status = $3,
574
- superseded_by = COALESCE($4, superseded_by),
531
+ superseded_by = CASE
532
+ WHEN $3 = 'active' THEN NULL
533
+ ELSE COALESCE($4, superseded_by)
534
+ END,
575
535
  valid_to = COALESCE($5, valid_to),
576
536
  superseded_at = CASE
577
537
  WHEN $3 = 'superseded' THEN COALESCE($8, superseded_at, now())
538
+ WHEN $3 = 'active' THEN NULL
578
539
  ELSE superseded_at
579
540
  END,
580
541
  revoked_at = CASE
581
542
  WHEN $3 = 'revoked' THEN COALESCE($9, revoked_at, now())
543
+ WHEN $3 = 'active' THEN NULL
582
544
  ELSE revoked_at
583
545
  END,
584
546
  visible_in_bootstrap = $6,
@@ -611,14 +573,19 @@ function createMemoryRecords({ pool, schema, defaultTenantId, inTransaction = fa
611
573
  const result = await pool.query(
612
574
  `UPDATE ${memories}
613
575
  SET status = $4,
614
- superseded_by = COALESCE($5, superseded_by),
576
+ superseded_by = CASE
577
+ WHEN $4 = 'active' THEN NULL
578
+ ELSE COALESCE($5, superseded_by)
579
+ END,
615
580
  valid_to = COALESCE($6, valid_to),
616
581
  superseded_at = CASE
617
582
  WHEN $4 = 'superseded' THEN COALESCE($9, superseded_at, now())
583
+ WHEN $4 = 'active' THEN NULL
618
584
  ELSE superseded_at
619
585
  END,
620
586
  revoked_at = CASE
621
587
  WHEN $4 = 'revoked' THEN COALESCE($10, revoked_at, now())
588
+ WHEN $4 = 'active' THEN NULL
622
589
  ELSE revoked_at
623
590
  END,
624
591
  visible_in_bootstrap = $7,
@@ -649,14 +616,19 @@ function createMemoryRecords({ pool, schema, defaultTenantId, inTransaction = fa
649
616
  const result = await pool.query(
650
617
  `UPDATE ${factAssertions}
651
618
  SET status = $3,
652
- superseded_by = COALESCE($4, superseded_by),
619
+ superseded_by = CASE
620
+ WHEN $3 = 'active' THEN NULL
621
+ ELSE COALESCE($4, superseded_by)
622
+ END,
653
623
  valid_to = COALESCE($5, valid_to),
654
624
  superseded_at = CASE
655
625
  WHEN $3 = 'superseded' THEN COALESCE($6, superseded_at, now())
626
+ WHEN $3 = 'active' THEN NULL
656
627
  ELSE superseded_at
657
628
  END,
658
629
  revoked_at = CASE
659
630
  WHEN $3 = 'revoked' THEN COALESCE($7, revoked_at, now())
631
+ WHEN $3 = 'active' THEN NULL
660
632
  ELSE revoked_at
661
633
  END,
662
634
  updated_at = now()
@@ -678,7 +650,13 @@ function createMemoryRecords({ pool, schema, defaultTenantId, inTransaction = fa
678
650
  async function listActive(input = {}) {
679
651
  const tenantId = input.tenantId || defaultTenantId;
680
652
  const params = [tenantId];
681
- 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
+ ];
682
660
  if (input.asOf) {
683
661
  params.push(input.asOf);
684
662
  const at = `$${params.length}::timestamptz`;
@@ -706,13 +684,18 @@ function createMemoryRecords({ pool, schema, defaultTenantId, inTransaction = fa
706
684
  where.push(`m.embedding IS NULL`);
707
685
  }
708
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`;
709
691
  const orderBy = input.visibleInBootstrap === true
710
- ? BOOTSTRAP_ORDER_SQL
692
+ ? bootstrapOrderSql(feedbackScoreExpr)
711
693
  : `m.accepted_at DESC NULLS LAST, m.id ASC`;
712
694
  const result = await pool.query(
713
- `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}
714
697
  FROM ${memories} m
715
- 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
716
699
  WHERE ${where.join(' AND ')}
717
700
  ORDER BY ${orderBy}
718
701
  LIMIT $${params.length}`,
@@ -773,17 +756,27 @@ function createMemoryRecords({ pool, schema, defaultTenantId, inTransaction = fa
773
756
  const tenantId = input.tenantId || defaultTenantId;
774
757
  let activeScopePath = normalizeScopePath(input.activeScopePath, input.activeScopeKey);
775
758
  let activeScopeKey = input.activeScopeKey || activeScopePath[activeScopePath.length - 1] || null;
776
- if (input.scopeId && !input.activeScopeKey && !input.activeScopePath) {
759
+ let resolvedScopeKey = null;
760
+ if (input.scopeId) {
777
761
  const scopeResult = await pool.query(
778
762
  `SELECT scope_key FROM ${scopes} WHERE tenant_id = $1 AND id = $2 LIMIT 1`,
779
763
  [tenantId, input.scopeId],
780
764
  );
781
- const scopedKey = scopeResult.rows[0]?.scope_key || null;
782
- if (scopedKey) {
783
- activeScopePath = [scopedKey];
784
- activeScopeKey = scopedKey;
765
+ resolvedScopeKey = scopeResult.rows[0]?.scope_key || null;
766
+ if (resolvedScopeKey && !input.activeScopeKey && !input.activeScopePath) {
767
+ activeScopePath = [resolvedScopeKey];
768
+ activeScopeKey = resolvedScopeKey;
785
769
  }
786
770
  }
771
+ assertAllowedScopeRequest({
772
+ ...input,
773
+ activeScopeKey,
774
+ activeScopePath,
775
+ resolvedScopeKey,
776
+ });
777
+ if (input.includeEvidenceRefs === true && input.operator !== true && input.internal !== true) {
778
+ throw new Error('memory.current includeEvidenceRefs requires operator=true or internal=true');
779
+ }
787
780
  const limit = Math.max(1, Math.min(100, input.limit || 50));
788
781
  const fetchLimit = Math.max(limit + 1, Math.min(200, Math.max(limit * 4, 40)));
789
782
  const asOf = input.asOf || new Date().toISOString();
@@ -791,6 +784,9 @@ function createMemoryRecords({ pool, schema, defaultTenantId, inTransaction = fa
791
784
  const where = [
792
785
  `m.tenant_id = $1`,
793
786
  `m.status = 'active'`,
787
+ `m.revoked_at IS NULL`,
788
+ `m.superseded_at IS NULL`,
789
+ `m.superseded_by IS NULL`,
794
790
  `s.scope_key = ANY($2::text[])`,
795
791
  `(m.visible_in_bootstrap = true OR m.visible_in_recall = true)`,
796
792
  `(m.valid_from IS NULL OR m.valid_from <= $3::timestamptz)`,
@@ -825,18 +821,21 @@ function createMemoryRecords({ pool, schema, defaultTenantId, inTransaction = fa
825
821
  ), '[]'::jsonb)`
826
822
  : `'[]'::jsonb`;
827
823
 
824
+ const feedbackScoreExpr = memoryFeedbackScoreSql(feedback);
825
+ const feedbackScoreSelect = `${feedbackScoreExpr}::real AS feedback_score`;
828
826
  const result = await pool.query(
829
827
  `SELECT
830
828
  m.*,
831
829
  s.scope_kind,
832
830
  s.scope_key,
833
831
  s.inheritance_mode AS scope_inheritance_mode,
832
+ ${feedbackScoreSelect},
834
833
  ${evidenceRefsSelect} AS evidence_refs
835
834
  FROM ${memories} m
836
- 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
837
836
  WHERE ${where.join(' AND ')}
838
837
  ORDER BY array_position($2::text[], s.scope_key) DESC NULLS LAST,
839
- ${BOOTSTRAP_ORDER_SQL}
838
+ ${bootstrapOrderSql(feedbackScoreExpr)}
840
839
  LIMIT ${limitParam}`,
841
840
  params,
842
841
  );