@shadowforge0/aquifer-memory 1.8.0 → 1.9.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.
- package/.env.example +1 -0
- package/README.md +49 -22
- package/README_CN.md +24 -22
- package/README_TW.md +20 -22
- package/aquifer.config.example.json +2 -1
- package/consumers/cli.js +560 -4
- package/consumers/codex.js +1 -1
- package/consumers/mcp.js +3 -0
- package/consumers/openclaw-ext/index.js +64 -6
- package/consumers/openclaw-ext/openclaw.plugin.json +1 -1
- package/consumers/openclaw-ext/package.json +1 -1
- package/consumers/openclaw-install.js +326 -0
- package/consumers/openclaw-plugin.js +39 -1
- package/consumers/shared/config.js +2 -0
- package/core/aquifer.js +180 -33
- package/core/backends/local.js +109 -0
- package/core/doctor.js +924 -0
- package/core/finalization-inspector.js +164 -0
- package/core/memory-explain.js +624 -0
- package/core/memory-recall.js +49 -23
- package/core/memory-records.js +16 -5
- package/core/memory-review.js +891 -0
- package/core/memory-serving.js +61 -4
- package/core/operator-observability.js +249 -0
- package/core/postgres-migrations.js +13 -0
- package/core/session-finalization.js +76 -1
- package/core/storage.js +124 -8
- package/docs/getting-started.md +34 -1
- package/docs/setup.md +102 -22
- package/package.json +5 -4
- package/schema/019-v1-memory-review-resolutions.sql +53 -0
- package/scripts/codex-checkpoint-commands.js +28 -0
- package/scripts/codex-checkpoint-runtime.js +109 -0
- package/scripts/codex-recovery.js +16 -4
package/core/memory-recall.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { resolveApplicableRecords } = require('./memory-bootstrap');
|
|
4
|
+
const { assertAllowedScopeRequest } = require('./memory-serving');
|
|
4
5
|
const { hybridRank } = require('./hybrid-rank');
|
|
5
6
|
|
|
6
7
|
const TYPE_RANK = {
|
|
@@ -104,6 +105,7 @@ function isActiveVisible(record, opts = {}) {
|
|
|
104
105
|
}
|
|
105
106
|
|
|
106
107
|
function activeScopeKeys(opts = {}) {
|
|
108
|
+
assertAllowedScopeRequest(opts);
|
|
107
109
|
if (Array.isArray(opts.activeScopePath) && opts.activeScopePath.length > 0) {
|
|
108
110
|
return opts.activeScopePath.map(value => String(value)).filter(Boolean);
|
|
109
111
|
}
|
|
@@ -274,6 +276,26 @@ function recallMemoryRecords(records = [], query, opts = {}) {
|
|
|
274
276
|
}
|
|
275
277
|
|
|
276
278
|
function createMemoryRecall({ pool, schema, defaultTenantId }) {
|
|
279
|
+
async function withResolvedScopeKey(opts = {}) {
|
|
280
|
+
const next = { ...opts };
|
|
281
|
+
if (!next.scopeId) return next;
|
|
282
|
+
const tenantId = next.tenantId || defaultTenantId;
|
|
283
|
+
const result = await pool.query(
|
|
284
|
+
`SELECT scope_key FROM ${schema}.scopes WHERE tenant_id = $1 AND id = $2 LIMIT 1`,
|
|
285
|
+
[tenantId, next.scopeId],
|
|
286
|
+
);
|
|
287
|
+
const resolvedScopeKey = result.rows?.[0]?.scope_key || null;
|
|
288
|
+
if (resolvedScopeKey) {
|
|
289
|
+
next.resolvedScopeKey = resolvedScopeKey;
|
|
290
|
+
if (!next.activeScopeKey && !next.activeScopePath) {
|
|
291
|
+
next.activeScopePath = [resolvedScopeKey];
|
|
292
|
+
next.activeScopeKey = resolvedScopeKey;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
assertAllowedScopeRequest(next);
|
|
296
|
+
return next;
|
|
297
|
+
}
|
|
298
|
+
|
|
277
299
|
function applyCurrentMemoryFilters(where, params, opts = {}) {
|
|
278
300
|
const scopeKeys = activeScopeKeys(opts);
|
|
279
301
|
if (opts.scopeId) {
|
|
@@ -295,12 +317,13 @@ function createMemoryRecall({ pool, schema, defaultTenantId }) {
|
|
|
295
317
|
}
|
|
296
318
|
|
|
297
319
|
async function recall(query, opts = {}) {
|
|
320
|
+
const scopedOpts = await withResolvedScopeKey(opts);
|
|
298
321
|
const q = String(query || '').trim();
|
|
299
322
|
if (!q) throw new Error('memory.recall(query): query must be a non-empty string');
|
|
300
|
-
const tenantId =
|
|
301
|
-
const limit = Math.max(1, Math.min(50,
|
|
302
|
-
const cfg = (
|
|
303
|
-
const scopeKeys = activeScopeKeys(
|
|
323
|
+
const tenantId = scopedOpts.tenantId || defaultTenantId;
|
|
324
|
+
const limit = Math.max(1, Math.min(50, scopedOpts.limit || 10));
|
|
325
|
+
const cfg = (scopedOpts.ftsConfig === 'zhcfg' || scopedOpts.ftsConfig === 'simple') ? scopedOpts.ftsConfig : 'simple';
|
|
326
|
+
const scopeKeys = activeScopeKeys(scopedOpts);
|
|
304
327
|
const fetchLimit = Math.max(limit, Math.min(200, scopeKeys ? limit * 4 : limit));
|
|
305
328
|
const feedbackScoreExpr = feedbackScoreSql(schema);
|
|
306
329
|
const params = [tenantId, q];
|
|
@@ -314,7 +337,7 @@ function createMemoryRecall({ pool, schema, defaultTenantId }) {
|
|
|
314
337
|
OR m.context_key ILIKE '%' || $2 || '%'
|
|
315
338
|
OR m.topic_key ILIKE '%' || $2 || '%')`,
|
|
316
339
|
];
|
|
317
|
-
applyCurrentMemoryFilters(where, params,
|
|
340
|
+
applyCurrentMemoryFilters(where, params, scopedOpts);
|
|
318
341
|
params.push(fetchLimit);
|
|
319
342
|
const result = await pool.query(
|
|
320
343
|
`SELECT
|
|
@@ -340,7 +363,7 @@ function createMemoryRecall({ pool, schema, defaultTenantId }) {
|
|
|
340
363
|
params
|
|
341
364
|
);
|
|
342
365
|
const applicableRows = scopeKeys
|
|
343
|
-
? resolveApplicableRecords(result.rows,
|
|
366
|
+
? resolveApplicableRecords(result.rows, scopedOpts)
|
|
344
367
|
: result.rows;
|
|
345
368
|
return applicableRows
|
|
346
369
|
.sort(sortRecallRows)
|
|
@@ -348,11 +371,12 @@ function createMemoryRecall({ pool, schema, defaultTenantId }) {
|
|
|
348
371
|
}
|
|
349
372
|
|
|
350
373
|
async function recallViaEvidenceItems(query, opts = {}) {
|
|
374
|
+
const scopedOpts = await withResolvedScopeKey(opts);
|
|
351
375
|
const q = String(query || '').trim();
|
|
352
376
|
if (!q) throw new Error('memory.recall(query): query must be a non-empty string');
|
|
353
|
-
const tenantId =
|
|
354
|
-
const limit = Math.max(1, Math.min(50,
|
|
355
|
-
const scopeKeys = activeScopeKeys(
|
|
377
|
+
const tenantId = scopedOpts.tenantId || defaultTenantId;
|
|
378
|
+
const limit = Math.max(1, Math.min(50, scopedOpts.limit || 10));
|
|
379
|
+
const scopeKeys = activeScopeKeys(scopedOpts);
|
|
356
380
|
const fetchLimit = Math.max(limit, Math.min(200, scopeKeys ? limit * 4 : limit));
|
|
357
381
|
const feedbackScoreExpr = feedbackScoreSql(schema);
|
|
358
382
|
const params = [tenantId, q];
|
|
@@ -361,8 +385,8 @@ function createMemoryRecall({ pool, schema, defaultTenantId }) {
|
|
|
361
385
|
`m.status = 'active'`,
|
|
362
386
|
`m.visible_in_recall = true`,
|
|
363
387
|
];
|
|
364
|
-
applyCurrentMemoryFilters(where, params,
|
|
365
|
-
const queryVec = vecToStr(
|
|
388
|
+
applyCurrentMemoryFilters(where, params, scopedOpts);
|
|
389
|
+
const queryVec = vecToStr(scopedOpts.queryVec);
|
|
366
390
|
let vectorScoreExpr = '0';
|
|
367
391
|
let evidencePredicate = `(ei.excerpt_text ILIKE '%' || $2 || '%'
|
|
368
392
|
OR ei.search_tsv @@ plainto_tsquery('simple', $2))`;
|
|
@@ -370,7 +394,7 @@ function createMemoryRecall({ pool, schema, defaultTenantId }) {
|
|
|
370
394
|
params.push(queryVec);
|
|
371
395
|
const vecPos = params.length;
|
|
372
396
|
vectorScoreExpr = `COALESCE(1.0 - (ei.embedding <=> $${vecPos}::vector), 0)`;
|
|
373
|
-
evidencePredicate =
|
|
397
|
+
evidencePredicate = scopedOpts.vectorOnly === true
|
|
374
398
|
? `ei.embedding IS NOT NULL`
|
|
375
399
|
: `(${evidencePredicate} OR ei.embedding IS NOT NULL)`;
|
|
376
400
|
}
|
|
@@ -425,7 +449,7 @@ function createMemoryRecall({ pool, schema, defaultTenantId }) {
|
|
|
425
449
|
params,
|
|
426
450
|
);
|
|
427
451
|
const applicableRows = scopeKeys
|
|
428
|
-
? resolveApplicableRecords(result.rows,
|
|
452
|
+
? resolveApplicableRecords(result.rows, scopedOpts)
|
|
429
453
|
: result.rows;
|
|
430
454
|
return applicableRows
|
|
431
455
|
.sort(sortRecallRows)
|
|
@@ -433,11 +457,12 @@ function createMemoryRecall({ pool, schema, defaultTenantId }) {
|
|
|
433
457
|
}
|
|
434
458
|
|
|
435
459
|
async function recallViaMemoryEmbeddings(queryVec, opts = {}) {
|
|
460
|
+
const scopedOpts = await withResolvedScopeKey(opts);
|
|
436
461
|
const vector = vecToStr(queryVec);
|
|
437
462
|
if (!vector) return [];
|
|
438
|
-
const tenantId =
|
|
439
|
-
const limit = Math.max(1, Math.min(50,
|
|
440
|
-
const scopeKeys = activeScopeKeys(
|
|
463
|
+
const tenantId = scopedOpts.tenantId || defaultTenantId;
|
|
464
|
+
const limit = Math.max(1, Math.min(50, scopedOpts.limit || 10));
|
|
465
|
+
const scopeKeys = activeScopeKeys(scopedOpts);
|
|
441
466
|
const fetchLimit = Math.max(limit, Math.min(200, scopeKeys ? limit * 4 : limit));
|
|
442
467
|
const feedbackScoreExpr = feedbackScoreSql(schema);
|
|
443
468
|
const params = [tenantId, vector];
|
|
@@ -447,7 +472,7 @@ function createMemoryRecall({ pool, schema, defaultTenantId }) {
|
|
|
447
472
|
`m.visible_in_recall = true`,
|
|
448
473
|
`m.embedding IS NOT NULL`,
|
|
449
474
|
];
|
|
450
|
-
applyCurrentMemoryFilters(where, params,
|
|
475
|
+
applyCurrentMemoryFilters(where, params, scopedOpts);
|
|
451
476
|
params.push(fetchLimit);
|
|
452
477
|
const result = await pool.query(
|
|
453
478
|
`SELECT
|
|
@@ -473,7 +498,7 @@ function createMemoryRecall({ pool, schema, defaultTenantId }) {
|
|
|
473
498
|
params,
|
|
474
499
|
);
|
|
475
500
|
const applicableRows = scopeKeys
|
|
476
|
-
? resolveApplicableRecords(result.rows,
|
|
501
|
+
? resolveApplicableRecords(result.rows, scopedOpts)
|
|
477
502
|
: result.rows;
|
|
478
503
|
return applicableRows
|
|
479
504
|
.sort(sortRecallRows)
|
|
@@ -481,11 +506,12 @@ function createMemoryRecall({ pool, schema, defaultTenantId }) {
|
|
|
481
506
|
}
|
|
482
507
|
|
|
483
508
|
async function recallViaLinkedSummaryEmbeddings(queryVec, opts = {}) {
|
|
509
|
+
const scopedOpts = await withResolvedScopeKey(opts);
|
|
484
510
|
const vector = vecToStr(queryVec);
|
|
485
511
|
if (!vector) return [];
|
|
486
|
-
const tenantId =
|
|
487
|
-
const limit = Math.max(1, Math.min(50,
|
|
488
|
-
const scopeKeys = activeScopeKeys(
|
|
512
|
+
const tenantId = scopedOpts.tenantId || defaultTenantId;
|
|
513
|
+
const limit = Math.max(1, Math.min(50, scopedOpts.limit || 10));
|
|
514
|
+
const scopeKeys = activeScopeKeys(scopedOpts);
|
|
489
515
|
const fetchLimit = Math.max(limit, Math.min(200, scopeKeys ? limit * 4 : limit));
|
|
490
516
|
const feedbackScoreExpr = feedbackScoreSql(schema);
|
|
491
517
|
const params = [tenantId, vector];
|
|
@@ -494,7 +520,7 @@ function createMemoryRecall({ pool, schema, defaultTenantId }) {
|
|
|
494
520
|
`m.status = 'active'`,
|
|
495
521
|
`m.visible_in_recall = true`,
|
|
496
522
|
];
|
|
497
|
-
applyCurrentMemoryFilters(where, params,
|
|
523
|
+
applyCurrentMemoryFilters(where, params, scopedOpts);
|
|
498
524
|
params.push(fetchLimit);
|
|
499
525
|
const result = await pool.query(
|
|
500
526
|
`WITH eligible_memories AS (
|
|
@@ -543,7 +569,7 @@ function createMemoryRecall({ pool, schema, defaultTenantId }) {
|
|
|
543
569
|
params,
|
|
544
570
|
);
|
|
545
571
|
const applicableRows = scopeKeys
|
|
546
|
-
? resolveApplicableRecords(result.rows,
|
|
572
|
+
? resolveApplicableRecords(result.rows, scopedOpts)
|
|
547
573
|
: result.rows;
|
|
548
574
|
return applicableRows
|
|
549
575
|
.sort(sortRecallRows)
|
package/core/memory-records.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const crypto = require('crypto');
|
|
4
4
|
const { resolveApplicableRecords } = require('./memory-bootstrap');
|
|
5
|
+
const { assertAllowedScopeRequest } = require('./memory-serving');
|
|
5
6
|
|
|
6
7
|
function requireField(obj, field) {
|
|
7
8
|
if (!obj || obj[field] === undefined || obj[field] === null || obj[field] === '') {
|
|
@@ -773,17 +774,27 @@ function createMemoryRecords({ pool, schema, defaultTenantId, inTransaction = fa
|
|
|
773
774
|
const tenantId = input.tenantId || defaultTenantId;
|
|
774
775
|
let activeScopePath = normalizeScopePath(input.activeScopePath, input.activeScopeKey);
|
|
775
776
|
let activeScopeKey = input.activeScopeKey || activeScopePath[activeScopePath.length - 1] || null;
|
|
776
|
-
|
|
777
|
+
let resolvedScopeKey = null;
|
|
778
|
+
if (input.scopeId) {
|
|
777
779
|
const scopeResult = await pool.query(
|
|
778
780
|
`SELECT scope_key FROM ${scopes} WHERE tenant_id = $1 AND id = $2 LIMIT 1`,
|
|
779
781
|
[tenantId, input.scopeId],
|
|
780
782
|
);
|
|
781
|
-
|
|
782
|
-
if (
|
|
783
|
-
activeScopePath = [
|
|
784
|
-
activeScopeKey =
|
|
783
|
+
resolvedScopeKey = scopeResult.rows[0]?.scope_key || null;
|
|
784
|
+
if (resolvedScopeKey && !input.activeScopeKey && !input.activeScopePath) {
|
|
785
|
+
activeScopePath = [resolvedScopeKey];
|
|
786
|
+
activeScopeKey = resolvedScopeKey;
|
|
785
787
|
}
|
|
786
788
|
}
|
|
789
|
+
assertAllowedScopeRequest({
|
|
790
|
+
...input,
|
|
791
|
+
activeScopeKey,
|
|
792
|
+
activeScopePath,
|
|
793
|
+
resolvedScopeKey,
|
|
794
|
+
});
|
|
795
|
+
if (input.includeEvidenceRefs === true && input.operator !== true && input.internal !== true) {
|
|
796
|
+
throw new Error('memory.current includeEvidenceRefs requires operator=true or internal=true');
|
|
797
|
+
}
|
|
787
798
|
const limit = Math.max(1, Math.min(100, input.limit || 50));
|
|
788
799
|
const fetchLimit = Math.max(limit + 1, Math.min(200, Math.max(limit * 4, 40)));
|
|
789
800
|
const asOf = input.asOf || new Date().toISOString();
|