@futdevpro/fdp-agent-memory 1.1.166 → 1.1.194

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 (30) hide show
  1. package/build/package.json +8 -2
  2. package/build/src/_cli/_commands/prune-duplicates.command.js +93 -0
  3. package/build/src/_cli/_commands/retire-temporary.command.js +77 -0
  4. package/build/src/_cli/register-commands.js +4 -0
  5. package/build/src/_collections/config-catalog.const.js +75 -0
  6. package/build/src/_models/data-models/fam-entry.data-model.js +6 -0
  7. package/build/src/_models/data-models/fam-memory.data-model.js +3 -0
  8. package/build/src/_modules/embedding/_collections/fam-exact-duplicate-prune.util.js +85 -0
  9. package/build/src/_modules/embedding/_services/fam-embedding-bootstrap.control-service.js +32 -0
  10. package/build/src/_modules/embedding/_services/fam-entry.data-service.js +21 -0
  11. package/build/src/_modules/embedding/_services/fam-exact-duplicate-prune.control-service.js +152 -0
  12. package/build/src/_modules/embedding/index.js +7 -1
  13. package/build/src/_modules/ingest/_services/fam-ingest.control-service.js +21 -1
  14. package/build/src/_modules/ingest/_services/fam-scan-job.control-service.js +55 -1
  15. package/build/src/_modules/ingest/_services/fam-scan-scheduler.control-service.js +26 -0
  16. package/build/src/_modules/mcp/_collections/fam-core-tools.const.js +11 -0
  17. package/build/src/_modules/mcp/_services/fam-read-tool.service.js +2 -0
  18. package/build/src/_modules/mcp/_services/fam-write-tool.service.js +29 -7
  19. package/build/src/_modules/retrieval/_collections/fam-cross-table-note.util.js +136 -0
  20. package/build/src/_modules/retrieval/_collections/fam-lexical-match.util.js +75 -2
  21. package/build/src/_modules/retrieval/_collections/fam-literal-phrase.util.js +74 -0
  22. package/build/src/_modules/retrieval/_collections/fam-memory-retention.util.js +70 -0
  23. package/build/src/_modules/retrieval/_services/fam-memory-reactivation.control-service.js +8 -1
  24. package/build/src/_modules/retrieval/_services/fam-memory-retention.control-service.js +118 -0
  25. package/build/src/_modules/retrieval/_services/fam-retrieval-candidate.data-service.js +33 -0
  26. package/build/src/_modules/retrieval/_services/fam-retrieval.control-service.js +224 -5
  27. package/build/src/_modules/retrieval/index.js +6 -1
  28. package/build/src/_routes/server/api/api.controller.js +77 -15
  29. package/package.json +8 -2
  30. package/build/src/_integration-tests/_helpers/fam-integration-test-setup.util.js +0 -105
@@ -69,6 +69,8 @@ class Api_Controller extends nts_dynamo_1.DyNTS_Controller {
69
69
  this.statsTableEndpoint(),
70
70
  this.ingestRunsEndpoint(),
71
71
  this.duplicatesEndpoint(),
72
+ this.duplicatesPruneEndpoint(),
73
+ this.memoryRetireEndpoint(),
72
74
  this.configGetEndpoint(),
73
75
  this.configSetEndpoint(),
74
76
  this.scanStartEndpoint(),
@@ -521,22 +523,26 @@ class Api_Controller extends nts_dynamo_1.DyNTS_Controller {
521
523
  continue;
522
524
  }
523
525
  const collection = this.entryCollection(registryEntry);
524
- const count = await collection.countDocuments({});
525
- // Embedding-státusz bontás Mongo-aggregációval ($group), NEM a doc-ok memóriába töltésével.
526
+ // FIX (2026-09-02, COLLSCAN → index): a korábbi `countDocuments({})` + `$group embeddingStatus`
527
+ // TELJES collection-átolvasás volt (350k+ doc a nagy tárakon) — meleg Mongo-cache mellett
528
+ // észrevétlen, kihűlt cache + telített lemez mellett viszont 20–50 s/tár, és a `/stats` a UI
529
+ // landing alatt PERCEKIG lógott (mérve: 120 s+ timeout). Most:
530
+ // • darabszám: `estimatedDocumentCount` (collection-metaadat, ~0 ms — a stats-kártyának a
531
+ // nagyságrend kell, nem a tranzakció-pontos szám),
532
+ // • státusz-bontás: per-státusz `countDocuments({embeddingStatus})` — az indexből számol
533
+ // (COUNT_SCAN), doc-olvasás nélkül,
534
+ // • utolsó módosítás: a friss `__lastModified` index tetejéről egyetlen doc.
535
+ const count = await collection.estimatedDocumentCount();
526
536
  const byStatus = { pending: 0, completed: 0, error: 0 };
527
- const grouped = await collection
528
- .aggregate([{ $group: { _id: '$embeddingStatus', n: { $sum: 1 } } }])
529
- .toArray();
530
- for (const group of grouped) {
531
- const status = group._id ?? 'pending';
532
- if (status in byStatus) {
533
- byStatus[status] += group.n;
534
- }
535
- else {
536
- byStatus.pending += group.n;
537
- }
538
- }
539
- // Utolsó módosítás: EGYETLEN doc (a legfrissebb), csak az időbélyeg-mezőre projektálva.
537
+ const [completed, error] = await Promise.all([
538
+ collection.countDocuments({ embeddingStatus: 'completed' }),
539
+ collection.countDocuments({ embeddingStatus: 'error' }),
540
+ ]);
541
+ byStatus.completed = completed;
542
+ byStatus.error = error;
543
+ // A `pending` a maradék (beleértve a státusz-mentes régi doc-okat) — így nem kell harmadik scan.
544
+ byStatus.pending = Math.max(0, count - completed - error);
545
+ // Utolsó módosítás: EGYETLEN doc a `__lastModified` index tetejéről (2026-09-02 óta indexelt).
540
546
  const newest = await collection
541
547
  .find({}, { projection: { __lastModified: 1 } })
542
548
  .sort({ __lastModified: -1 })
@@ -627,6 +633,62 @@ class Api_Controller extends nts_dynamo_1.DyNTS_Controller {
627
633
  });
628
634
  }
629
635
  // =========================================================================
636
+ // /duplicates/prune — BÁJTAZONOS ismétlés-nyesés (dry-run az alapértelmezés)
637
+ // =========================================================================
638
+ /**
639
+ * `POST /duplicates/prune` — az azonos `(absolutePath, chunkIndex, chunkTotal, contentHash)` négyesű
640
+ * példányokból a legfrissebbet megtartja, a többit soft-delete-eli, és a csoport `recallCount` /
641
+ * `lastRecalledAt` MAXIMUMÁT a megtartottra viszi. Body: `table` (opcionális, különben MINDEN tár),
642
+ * `dryRun` (DEFAULT `true` — törölni csak explicit `false`-szal lehet), `maxGroups`.
643
+ *
644
+ * A `GET /duplicates/:table`-lel NEM keverendő: az szemantikus hasonlóságot keres és sosem töröl.
645
+ */
646
+ duplicatesPruneEndpoint() {
647
+ return new nts_dynamo_1.DyNTS_Endpoint_Params({
648
+ name: 'duplicatesPrune',
649
+ type: fsm_dynamo_1.DyFM_HttpCallType.post,
650
+ endpoint: '/duplicates/prune',
651
+ tasks: [
652
+ async (req, res) => {
653
+ await this.run(res, async () => {
654
+ const body = req.body ?? {};
655
+ return embedding_1.FAM_ExactDuplicatePrune_ControlService.getInstance().prune({
656
+ table: body.table ? this.parseTable(body.table) : undefined,
657
+ dryRun: body.dryRun !== false,
658
+ maxGroups: typeof body.maxGroups === 'number' ? body.maxGroups : undefined,
659
+ });
660
+ });
661
+ },
662
+ ],
663
+ });
664
+ }
665
+ // =========================================================================
666
+ // /memory/retire-temporary — az alvó törmelék nyugdíjazása (dry-run az alapértelmezés)
667
+ // =========================================================================
668
+ /**
669
+ * `POST /memory/retire-temporary` — az alvó beszélgetés-törmelék (`user_prompt` / `temporary_context`)
670
+ * nyugdíjazása a `memory` táron: `superseded + retiredAt`, NEM törlés. A szabály és az indoklás a
671
+ * `fam-memory-retention.util.ts`-ben. Body: `dryRun` (DEFAULT `true`), `retireAfterDays` (override).
672
+ */
673
+ memoryRetireEndpoint() {
674
+ return new nts_dynamo_1.DyNTS_Endpoint_Params({
675
+ name: 'memoryRetireTemporary',
676
+ type: fsm_dynamo_1.DyFM_HttpCallType.post,
677
+ endpoint: '/memory/retire-temporary',
678
+ tasks: [
679
+ async (req, res) => {
680
+ await this.run(res, async () => {
681
+ const body = req.body ?? {};
682
+ return retrieval_1.FAM_MemoryRetention_ControlService.getInstance().retire({
683
+ dryRun: body.dryRun !== false,
684
+ retireAfterDays: typeof body.retireAfterDays === 'number' ? body.retireAfterDays : undefined,
685
+ });
686
+ });
687
+ },
688
+ ],
689
+ });
690
+ }
691
+ // =========================================================================
630
692
  // /config — scoped config olvasás/írás (dsgn-007; a UI settings + CLI config)
631
693
  // =========================================================================
632
694
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@futdevpro/fdp-agent-memory",
3
- "version": "1.1.166",
3
+ "version": "1.1.194",
4
4
  "description": "Local-first, vector-backed multi-table agent memory exposed as an MCP server (read/write/capabilities). Public, FDP-Templates-free, no auth.",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -19,7 +19,13 @@
19
19
  "build",
20
20
  "client-dist",
21
21
  "README.md",
22
- "LICENSE"
22
+ "LICENSE",
23
+ "!build/**/*.spec.js",
24
+ "!build/**/*.spec.js.map",
25
+ "!build/**/*.spec.d.ts",
26
+ "!build/**/_benchmarks/**",
27
+ "!build/**/_integration-tests/**",
28
+ "!build/**/_spec-support/**"
23
29
  ],
24
30
  "engines": {
25
31
  "node": ">=20"
@@ -1,105 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FAM_IntegrationTest_Setup_Util = void 0;
4
- const tslib_1 = require("tslib");
5
- const net = tslib_1.__importStar(require("net"));
6
- const mongoose_1 = tslib_1.__importDefault(require("mongoose"));
7
- const nts_dynamo_1 = require("@futdevpro/nts-dynamo");
8
- const fam_db_models_const_1 = require("../../_collections/fam-db-models.const");
9
- /** Integrációs teszt issuer — a MongoDB-dokumentumok azonosítására. */
10
- const INTTEST_ISSUER = 'integration-test';
11
- /** Teszt-adat prefix — a teszt-dokumentumok megkülönböztetésére (NEM ütközik production-nal). */
12
- const INTTEST_PREFIX = 'inttest-';
13
- /** A dedikált spec-DB neve (a runner/local Mongo-n; NEM a `fdp-agent-memory` prod-DB). */
14
- const INTTEST_DB_NAME = 'fam_inttest';
15
- /** MongoDB elérhetőség-ellenőrzés timeout (ms). */
16
- const MONGO_CHECK_TIMEOUT_MS = 1500;
17
- /**
18
- * `FAM_IntegrationTest_Setup_Util` — az integrációs tesztek közös setup-utility-je (a
19
- * `ccap-revisioned` `IntegrationTest_Setup_Util` mintára). MongoDB-elérhetőség (TCP-probe a
20
- * `MONGO_URL`-re), egyedi `inttest-` ID-generálás, ÉS egy **lightweight DB-setup** (mongoose-connect +
21
- * `DyNTS_GlobalService.setServices`) — **NEM a teljes `new App()` boot** (az HTTP-listen + LVS-hydrate
22
- * postProcess-t indít, ami a spec-eket hangolja); csak a DataService-ek DB-rétegét állítja fel.
23
- */
24
- class FAM_IntegrationTest_Setup_Util {
25
- /** Teszt issuer azonosító — minden integrációs-teszt DataService-híváshoz. */
26
- static getIssuer() {
27
- return INTTEST_ISSUER;
28
- }
29
- /**
30
- * Egyedi, időbélyeges ID/tartalom a teszt-adatokhoz. Pattern: `inttest-{timestamp}-{random}` —
31
- * párhuzamos futtatásban is egyedi, és a prefix jelzi a teszt-adatot.
32
- */
33
- static generateUniqueId() {
34
- const timestamp = Date.now();
35
- const random = Math.random().toString(36).substring(2, 8);
36
- return `${INTTEST_PREFIX}${timestamp}-${random}`;
37
- }
38
- /**
39
- * MongoDB elérhetőség TCP-socket-tel (a `MONGO_URL` host:port-ja; default
40
- * `mongodb://127.0.0.1:27017`). Ha nem elérhető → `false`, és a tesztek `pending()`-re állnak
41
- * (CI-ben Mongo nélkül is zöld a suite — pending, NEM failure).
42
- */
43
- static checkMongoAvailability() {
44
- return new Promise((resolve) => {
45
- const mongoUrl = process.env.MONGO_URL ?? 'mongodb://127.0.0.1:27017';
46
- const urlNoProtocol = mongoUrl.replace(/^mongodb(\+srv)?:\/\//, '');
47
- const hostPort = urlNoProtocol.split('/')[0];
48
- const parts = hostPort.split(':');
49
- const host = parts[0];
50
- const port = parseInt(parts[1] ?? '27017', 10);
51
- const socket = net.createConnection({ host: host, port: port });
52
- let finished = false;
53
- const finish = (available) => {
54
- if (finished) {
55
- return;
56
- }
57
- finished = true;
58
- try {
59
- socket.destroy();
60
- }
61
- catch { /* ignore */ }
62
- resolve(available);
63
- };
64
- socket.on('connect', () => { finish(true); });
65
- socket.on('error', () => { finish(false); });
66
- setTimeout(() => { finish(false); }, MONGO_CHECK_TIMEOUT_MS);
67
- });
68
- }
69
- /**
70
- * Lightweight DB-setup (`beforeAll`-ban, ha a Mongo elérhető): mongoose-connect a dedikált
71
- * `fam_inttest` DB-re + `DyNTS_GlobalService.setServices` a `FAM_DB_MODELS`-szel (SSOT). Így a
72
- * `new FAM_*_DataService(...)` (eager getDBService) feloldódik — App-boot, HTTP-listen és
73
- * embedding/LVS-postProcess NÉLKÜL (azok a teljes boot velejárói, itt nem kellenek).
74
- */
75
- static async setupDb() {
76
- const mongoUrl = process.env.MONGO_URL ?? 'mongodb://127.0.0.1:27017';
77
- const base = mongoUrl.replace(/^(mongodb(\+srv)?:\/\/[^/]+).*$/, '$1');
78
- await mongoose_1.default.connect(`${base}/${INTTEST_DB_NAME}`);
79
- await nts_dynamo_1.DyNTS_GlobalService.setServices({ dbModels: fam_db_models_const_1.FAM_DB_MODELS });
80
- }
81
- /** DB-teardown (`afterAll`-ban): a mongoose-kapcsolat zárása (best-effort). */
82
- static async teardownDb() {
83
- try {
84
- if (mongoose_1.default.connection.readyState !== 0) {
85
- await mongoose_1.default.disconnect();
86
- }
87
- }
88
- catch {
89
- // best-effort — a process-vég úgyis bontja.
90
- }
91
- }
92
- /** Egy collection ürítése teszt KÖZÖTT (a teszt-izolációhoz). No-op ha a kapcsolat nem áll. */
93
- static async clearCollection(collectionName) {
94
- if (mongoose_1.default.connection.readyState !== 1) {
95
- return;
96
- }
97
- try {
98
- await mongoose_1.default.connection.collection(collectionName).deleteMany({});
99
- }
100
- catch {
101
- // a collection lehet hogy még nem létezik — nem hiba.
102
- }
103
- }
104
- }
105
- exports.FAM_IntegrationTest_Setup_Util = FAM_IntegrationTest_Setup_Util;