@shadowforge0/aquifer-memory 1.6.0 → 1.8.0

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 (44) hide show
  1. package/.env.example +8 -0
  2. package/README.md +72 -0
  3. package/README_CN.md +17 -0
  4. package/README_TW.md +4 -0
  5. package/aquifer.config.example.json +19 -0
  6. package/consumers/cli.js +259 -12
  7. package/consumers/codex-active-checkpoint.js +186 -0
  8. package/consumers/codex-current-memory.js +106 -0
  9. package/consumers/codex-handoff.js +551 -6
  10. package/consumers/codex.js +209 -25
  11. package/consumers/mcp.js +144 -6
  12. package/consumers/shared/config.js +60 -1
  13. package/consumers/shared/factory.js +10 -3
  14. package/core/aquifer.js +357 -838
  15. package/core/backends/capabilities.js +89 -0
  16. package/core/backends/local.js +430 -0
  17. package/core/legacy-bootstrap.js +140 -0
  18. package/core/mcp-manifest.js +66 -2
  19. package/core/memory-bootstrap.js +20 -8
  20. package/core/memory-consolidation.js +365 -11
  21. package/core/memory-promotion.js +157 -26
  22. package/core/memory-recall.js +341 -22
  23. package/core/memory-records.js +347 -11
  24. package/core/memory-serving.js +132 -0
  25. package/core/postgres-migrations.js +533 -0
  26. package/core/public-session-filter.js +40 -0
  27. package/core/recall-runtime.js +115 -0
  28. package/core/scope-attribution.js +279 -0
  29. package/core/session-checkpoint-producer.js +412 -0
  30. package/core/session-checkpoints.js +432 -0
  31. package/core/session-finalization.js +98 -2
  32. package/core/storage-checkpoints.js +546 -0
  33. package/core/storage.js +121 -8
  34. package/docs/getting-started.md +6 -0
  35. package/docs/setup.md +66 -3
  36. package/package.json +8 -4
  37. package/schema/014-v1-checkpoint-runs.sql +349 -0
  38. package/schema/015-v1-evidence-items.sql +92 -0
  39. package/schema/016-v1-evidence-ref-multi-item.sql +19 -0
  40. package/schema/017-v1-memory-record-embeddings.sql +25 -0
  41. package/schema/018-v1-finalization-candidate-envelope.sql +39 -0
  42. package/scripts/codex-checkpoint-commands.js +464 -0
  43. package/scripts/codex-checkpoint-runtime.js +520 -0
  44. package/scripts/codex-recovery.js +246 -1
@@ -123,6 +123,61 @@ function normalizeEvidenceRefs(candidate = {}) {
123
123
  return candidate.evidenceRefs || candidate.evidence_refs || [];
124
124
  }
125
125
 
126
+ function normalizeEvidenceTexts(candidate = {}) {
127
+ const raw = candidate.evidenceItems || candidate.evidence_items || candidate.evidenceTexts || candidate.evidence_texts;
128
+ const values = Array.isArray(raw) ? raw : [];
129
+ const direct = [
130
+ candidate.evidenceText,
131
+ candidate.evidence_text,
132
+ candidate.evidenceExcerpt,
133
+ candidate.evidence_excerpt,
134
+ candidate.sourceText,
135
+ candidate.source_text,
136
+ candidate.quote,
137
+ ];
138
+ for (const value of direct) {
139
+ if (value) values.push(value);
140
+ }
141
+ return values
142
+ .map(value => {
143
+ if (typeof value === 'string') return { excerptText: normalizeText(value), metadata: {} };
144
+ if (!value || typeof value !== 'object') return null;
145
+ const excerptText = normalizeText(value.excerptText || value.excerpt_text || value.text || value.quote || value.summary);
146
+ if (!excerptText) return null;
147
+ return {
148
+ ...value,
149
+ excerptText,
150
+ metadata: value.metadata || {},
151
+ };
152
+ })
153
+ .filter(value => value && value.excerptText);
154
+ }
155
+
156
+ function buildMemoryEmbeddingText(candidate = {}) {
157
+ const fields = [
158
+ ['title', candidate.title],
159
+ ['summary', candidate.summary],
160
+ ['context', candidate.contextKey || candidate.context_key],
161
+ ['topic', candidate.topicKey || candidate.topic_key],
162
+ ]
163
+ .map(([label, value]) => {
164
+ const text = normalizeText(value);
165
+ return text ? `${label}: ${text}` : '';
166
+ })
167
+ .filter(Boolean);
168
+ return fields.join('\n');
169
+ }
170
+
171
+ function assignEmbeddedVectors(items, vectors, errorPrefix) {
172
+ if (!Array.isArray(vectors) || vectors.length !== items.length) {
173
+ throw new Error(`${errorPrefix} returned ${Array.isArray(vectors) ? vectors.length : 'invalid'} vectors for ${items.length} texts`);
174
+ }
175
+ for (let i = 0; i < items.length; i++) {
176
+ const vector = vectors[i];
177
+ if (Array.isArray(vector) && vector.length > 0) items[i].embedding = vector;
178
+ }
179
+ }
180
+
126
181
  function buildFactAssertion(candidate = {}, opts = {}) {
127
182
  const memoryType = normalizeType(candidate.memoryType || candidate.memory_type);
128
183
  if (memoryType !== 'fact') return null;
@@ -217,6 +272,15 @@ function pushStructuredCandidates(candidates, items, spec) {
217
272
  const text = textFromItem(item, spec.keys);
218
273
  if (!text) continue;
219
274
  const itemObj = item && typeof item === 'object' ? item : null;
275
+ const evidenceText = normalizeText(itemObj && (
276
+ itemObj.evidenceText
277
+ || itemObj.evidence_text
278
+ || itemObj.evidenceExcerpt
279
+ || itemObj.evidence_excerpt
280
+ || itemObj.sourceText
281
+ || itemObj.source_text
282
+ || itemObj.quote
283
+ ));
220
284
  const explicitSubject = normalizeText(itemObj && (itemObj.subject || itemObj.entity || itemObj.name));
221
285
  const explicitAspect = normalizeText(itemObj && (itemObj.aspect || itemObj.predicate || itemObj.attribute));
222
286
  const subject = explicitSubject || normalizeText(spec.subject);
@@ -242,6 +306,7 @@ function pushStructuredCandidates(candidates, items, spec) {
242
306
  payload: typeof item === 'string' ? { [spec.payloadKey]: text } : { ...item, [spec.payloadKey]: text },
243
307
  authority: spec.authority,
244
308
  evidenceRefs: spec.evidenceRefs,
309
+ evidenceText: evidenceText || undefined,
245
310
  visibleInBootstrap: true,
246
311
  visibleInRecall: true,
247
312
  });
@@ -355,7 +420,45 @@ function assessCandidate(candidate = {}, opts = {}) {
355
420
  return { action: 'promote', reason: 'v1_foundation_allowed' };
356
421
  }
357
422
 
358
- function createMemoryPromotion({ records }) {
423
+ function createMemoryPromotion({ records, embedFn = null }) {
424
+ async function prepareCandidates(candidates = []) {
425
+ const prepared = candidates.map(candidate => ({ ...candidate }));
426
+ if (typeof embedFn !== 'function' || prepared.length === 0) return prepared;
427
+
428
+ const pendingMemoryRows = [];
429
+ const memoryTexts = [];
430
+ for (const candidate of prepared) {
431
+ if (Array.isArray(candidate.embedding) && candidate.embedding.length > 0) continue;
432
+ const text = buildMemoryEmbeddingText(candidate);
433
+ if (!text) continue;
434
+ pendingMemoryRows.push(candidate);
435
+ memoryTexts.push(text);
436
+ }
437
+ if (memoryTexts.length > 0) {
438
+ const vectors = await embedFn(memoryTexts);
439
+ assignEmbeddedVectors(pendingMemoryRows, vectors, 'memory promotion embedFn');
440
+ }
441
+
442
+ const pendingEvidenceItems = [];
443
+ const evidenceTexts = [];
444
+ for (const candidate of prepared) {
445
+ const normalizedEvidenceTexts = normalizeEvidenceTexts(candidate);
446
+ candidate._preparedEvidenceTexts = normalizedEvidenceTexts;
447
+ for (const item of normalizedEvidenceTexts) {
448
+ if (Array.isArray(item.embedding) && item.embedding.length > 0) continue;
449
+ if (!item.excerptText) continue;
450
+ pendingEvidenceItems.push(item);
451
+ evidenceTexts.push(item.excerptText);
452
+ }
453
+ }
454
+ if (evidenceTexts.length > 0) {
455
+ const vectors = await embedFn(evidenceTexts);
456
+ assignEmbeddedVectors(pendingEvidenceItems, vectors, 'memory evidence embedFn');
457
+ }
458
+
459
+ return prepared;
460
+ }
461
+
359
462
  async function promoteOne(candidate, opts = {}, candidateRecords = records, tx = {}) {
360
463
  if (tx.inTransaction && candidateRecords.lockCanonicalKey && candidate.canonicalKey) {
361
464
  await candidateRecords.lockCanonicalKey({
@@ -386,6 +489,7 @@ function createMemoryPromotion({ records }) {
386
489
 
387
490
  const memoryType = normalizeType(candidate.memoryType || candidate.memory_type);
388
491
  const acceptedAt = candidate.acceptedAt || opts.acceptedAt || new Date().toISOString();
492
+ const evidenceTexts = candidate._preparedEvidenceTexts || normalizeEvidenceTexts(candidate);
389
493
  let scopeId = candidate.scopeId || candidate.scope_id || null;
390
494
  if (!scopeId) {
391
495
  const scope = await candidateRecords.upsertScope({
@@ -399,6 +503,52 @@ function createMemoryPromotion({ records }) {
399
503
  scopeId = scope.id;
400
504
  }
401
505
 
506
+ async function linkCandidateEvidence(ownerKind, ownerId, ref) {
507
+ const base = {
508
+ tenantId: opts.tenantId,
509
+ ownerKind,
510
+ ownerId,
511
+ sourceKind: ref.sourceKind || ref.source_kind,
512
+ sourceRef: ref.sourceRef || ref.source_ref,
513
+ relationKind: ref.relationKind || ref.relation_kind || 'supporting',
514
+ weight: ref.weight ?? 1.0,
515
+ metadata: ref.metadata || {},
516
+ createdByFinalizationId: opts.createdByFinalizationId || opts.created_by_finalization_id || null,
517
+ createdByCompactionRunId: opts.createdByCompactionRunId || opts.created_by_compaction_run_id || null,
518
+ };
519
+
520
+ if (!candidateRecords.upsertEvidenceItem || evidenceTexts.length === 0) {
521
+ await candidateRecords.linkEvidence(base);
522
+ return;
523
+ }
524
+
525
+ for (const item of evidenceTexts) {
526
+ const evidenceItem = await candidateRecords.upsertEvidenceItem({
527
+ tenantId: opts.tenantId,
528
+ sourceKind: item.sourceKind || item.source_kind || base.sourceKind,
529
+ sourceRef: item.sourceRef || item.source_ref || base.sourceRef,
530
+ sessionRowId: item.sessionRowId || item.session_row_id || null,
531
+ turnEmbeddingId: item.turnEmbeddingId || item.turn_embedding_id || null,
532
+ summaryRowId: item.summaryRowId || item.summary_row_id || null,
533
+ createdByFinalizationId: base.createdByFinalizationId,
534
+ excerptText: item.excerptText,
535
+ excerptHash: item.excerptHash || item.excerpt_hash || null,
536
+ embedding: item.embedding || null,
537
+ metadata: {
538
+ ...(item.metadata || {}),
539
+ memoryType,
540
+ canonicalKey: candidate.canonicalKey,
541
+ },
542
+ });
543
+ await candidateRecords.linkEvidence({
544
+ ...base,
545
+ sourceKind: item.sourceKind || item.source_kind || base.sourceKind,
546
+ sourceRef: item.sourceRef || item.source_ref || base.sourceRef,
547
+ evidenceItemId: evidenceItem ? evidenceItem.id : null,
548
+ });
549
+ }
550
+ }
551
+
402
552
  let backingFact = null;
403
553
  const factAssertion = buildFactAssertion(candidate, {
404
554
  tenantId: opts.tenantId,
@@ -444,18 +594,7 @@ function createMemoryPromotion({ records }) {
444
594
  }
445
595
 
446
596
  for (const ref of normalizeEvidenceRefs(candidate)) {
447
- await candidateRecords.linkEvidence({
448
- tenantId: opts.tenantId,
449
- ownerKind: 'fact',
450
- ownerId: backingFact.id,
451
- sourceKind: ref.sourceKind || ref.source_kind,
452
- sourceRef: ref.sourceRef || ref.source_ref,
453
- relationKind: ref.relationKind || ref.relation_kind || 'supporting',
454
- weight: ref.weight ?? 1.0,
455
- metadata: ref.metadata || {},
456
- createdByFinalizationId: opts.createdByFinalizationId || opts.created_by_finalization_id || null,
457
- createdByCompactionRunId: opts.createdByCompactionRunId || opts.created_by_compaction_run_id || null,
458
- });
597
+ await linkCandidateEvidence('fact', backingFact.id, ref);
459
598
  }
460
599
  }
461
600
 
@@ -482,6 +621,7 @@ function createMemoryPromotion({ records }) {
482
621
  visibleInBootstrap: candidate.visibleInBootstrap !== false,
483
622
  visibleInRecall: candidate.visibleInRecall !== false,
484
623
  rankFeatures: candidate.rankFeatures || {},
624
+ embedding: candidate.embedding || null,
485
625
  });
486
626
 
487
627
  if (assessment.supersedeId && candidateRecords.updateMemoryStatus) {
@@ -495,26 +635,16 @@ function createMemoryPromotion({ records }) {
495
635
  }
496
636
 
497
637
  for (const ref of normalizeEvidenceRefs(candidate)) {
498
- await candidateRecords.linkEvidence({
499
- tenantId: opts.tenantId,
500
- ownerKind: 'memory_record',
501
- ownerId: memory.id,
502
- sourceKind: ref.sourceKind || ref.source_kind,
503
- sourceRef: ref.sourceRef || ref.source_ref,
504
- relationKind: ref.relationKind || ref.relation_kind || 'supporting',
505
- weight: ref.weight ?? 1.0,
506
- metadata: ref.metadata || {},
507
- createdByFinalizationId: opts.createdByFinalizationId || opts.created_by_finalization_id || null,
508
- createdByCompactionRunId: opts.createdByCompactionRunId || opts.created_by_compaction_run_id || null,
509
- });
638
+ await linkCandidateEvidence('memory_record', memory.id, ref);
510
639
  }
511
640
 
512
641
  return { candidate, action: 'promote', reason: assessment.reason, memory, backingFact };
513
642
  }
514
643
 
515
644
  async function promote(candidates = [], opts = {}) {
645
+ const preparedCandidates = await prepareCandidates(candidates);
516
646
  const results = [];
517
- for (const candidate of candidates) {
647
+ for (const candidate of preparedCandidates) {
518
648
  const result = records.withTransaction
519
649
  ? await records.withTransaction((txRecords, meta = {}) => promoteOne(candidate, opts, txRecords, {
520
650
  inTransaction: meta.transactional !== false,
@@ -538,6 +668,7 @@ module.exports = {
538
668
  AUTHORITY_RANK,
539
669
  defaultInheritanceForType,
540
670
  buildCanonicalKey,
671
+ buildMemoryEmbeddingText,
541
672
  extractCandidatesFromStructuredSummary,
542
673
  assessCandidate,
543
674
  createMemoryPromotion,