@futdevpro/fdp-agent-memory 0.1.0 → 1.1.12

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 (100) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +7 -7
  3. package/build/package.json +6 -5
  4. package/build/src/_cli/_collections/fam-arg.util.js +48 -0
  5. package/build/src/_cli/_collections/fam-cli.const.js +40 -0
  6. package/build/src/_cli/_collections/fam-output.util.js +86 -0
  7. package/build/src/_cli/_collections/fam-project-discovery.util.js +98 -0
  8. package/build/src/_cli/_commands/capture.command.js +73 -0
  9. package/build/src/_cli/_commands/config.command.js +93 -0
  10. package/build/src/_cli/_commands/doctor.command.js +124 -0
  11. package/build/src/_cli/_commands/errors.command.js +66 -0
  12. package/build/src/_cli/_commands/export.command.js +65 -0
  13. package/build/src/_cli/_commands/find-duplicates.command.js +97 -0
  14. package/build/src/_cli/_commands/import.command.js +136 -0
  15. package/build/src/_cli/_commands/init.command.js +147 -0
  16. package/build/src/_cli/_commands/read.command.js +109 -0
  17. package/build/src/_cli/_commands/scan-projects.command.js +138 -0
  18. package/build/src/_cli/_commands/scan.command.js +98 -0
  19. package/build/src/_cli/_commands/seed.command.js +40 -0
  20. package/build/src/_cli/_commands/serve.command.js +350 -0
  21. package/build/src/_cli/_commands/start.command.js +134 -0
  22. package/build/src/_cli/_commands/stats.command.js +54 -0
  23. package/build/src/_cli/_commands/write.command.js +103 -0
  24. package/build/src/_cli/_models/interfaces/fam-cli-global-options.interface.js +2 -0
  25. package/build/src/_cli/_models/interfaces/fam-cli-output.interface.js +9 -0
  26. package/build/src/_cli/_models/interfaces/fam-client-result.interface.js +2 -0
  27. package/build/src/_cli/_services/fam-client.service.js +140 -0
  28. package/build/src/_cli/register-commands.js +86 -0
  29. package/build/src/_collections/config-catalog.const.js +67 -1
  30. package/build/src/_collections/fam-console.util.js +367 -0
  31. package/build/src/_collections/fam-entry-bootstrap.util.js +158 -4
  32. package/build/src/_collections/fam-error-factory.util.js +0 -9
  33. package/build/src/_collections/fam-mcp-bridge.util.js +49 -0
  34. package/build/src/_collections/fam-reference-code.util.js +105 -0
  35. package/build/src/_collections/fam-version.const.js +10 -0
  36. package/build/src/_models/data-models/fam-entry-base-properties.const.js +1 -0
  37. package/build/src/_models/data-models/fam-entry.data-model.js +6 -0
  38. package/build/src/_models/data-models/fam-ingest-run.data-model.js +3 -1
  39. package/build/src/_models/data-models/fam-reference.data-model.js +7 -0
  40. package/build/src/_modules/capture/_collections/fam-capture.const.js +11 -0
  41. package/build/src/_modules/capture/_services/fam-auto-capture.control-service.js +87 -0
  42. package/build/src/_modules/capture/index.js +8 -0
  43. package/build/src/_modules/embedding/_collections/fam-embedding-prefix.util.js +77 -0
  44. package/build/src/_modules/embedding/_services/fam-duplicate-scan.control-service.js +202 -0
  45. package/build/src/_modules/embedding/_services/fam-embedding-pipeline.control-service.js +33 -9
  46. package/build/src/_modules/embedding/_services/fam-embedding.control-service.js +21 -2
  47. package/build/src/_modules/embedding/_services/fam-entry.data-service.js +135 -0
  48. package/build/src/_modules/embedding/_services/fam-vector-search.control-service.js +42 -32
  49. package/build/src/_modules/embedding/index.js +4 -1
  50. package/build/src/_modules/export/_collections/fam-export.const.js +22 -0
  51. package/build/src/_modules/export/_services/fam-export.control-service.js +64 -0
  52. package/build/src/_modules/export/index.js +8 -0
  53. package/build/src/_modules/ingest/_collections/fam-famignore.util.js +83 -0
  54. package/build/src/_modules/ingest/_collections/fam-file-routing.util.js +59 -48
  55. package/build/src/_modules/ingest/_collections/fam-project-identity.util.js +134 -0
  56. package/build/src/_modules/ingest/_collections/fam-scan-progress.util.js +57 -0
  57. package/build/src/_modules/ingest/_collections/fam-scan-summary.util.js +60 -0
  58. package/build/src/_modules/ingest/_collections/fam-scan-weight.util.js +53 -0
  59. package/build/src/_modules/ingest/_collections/fam-secret-exclude.util.js +37 -14
  60. package/build/src/_modules/ingest/_collections/fam-sliding-chunker.util.js +34 -0
  61. package/build/src/_modules/ingest/_collections/fam-ts-chunker.util.js +200 -14
  62. package/build/src/_modules/ingest/_services/fam-delta-compare.util.js +4 -1
  63. package/build/src/_modules/ingest/_services/fam-ingest-run.data-service.js +7 -4
  64. package/build/src/_modules/ingest/_services/fam-ingest.control-service.js +346 -17
  65. package/build/src/_modules/ingest/_services/fam-scan.control-service.js +25 -2
  66. package/build/src/_modules/ingest/index.js +3 -1
  67. package/build/src/_modules/mcp/_collections/fam-active-rules.util.js +56 -0
  68. package/build/src/_modules/mcp/_collections/fam-core-tools.const.js +47 -6
  69. package/build/src/_modules/mcp/_services/fam-capabilities-tool.service.js +4 -4
  70. package/build/src/_modules/mcp/_services/fam-capability-registry.service.js +224 -18
  71. package/build/src/_modules/mcp/_services/fam-mcp-adapter.service.js +4 -4
  72. package/build/src/_modules/mcp/_services/fam-mcp-server.service.js +4 -4
  73. package/build/src/_modules/mcp/_services/fam-read-tool.service.js +53 -1
  74. package/build/src/_modules/mcp/_services/fam-write-tool.service.js +104 -8
  75. package/build/src/_modules/mcp/index.js +4 -4
  76. package/build/src/_modules/migration/_collections/fam-claude-mem-normalize.util.js +66 -3
  77. package/build/src/_modules/migration/_collections/fam-prompt-aggregate.util.js +143 -0
  78. package/build/src/_modules/migration/_collections/fam-target-mapping.util.js +19 -0
  79. package/build/src/_modules/migration/_enums/fam-claude-mem-source.type-enum.js +6 -0
  80. package/build/src/_modules/migration/_models/interfaces/fam-claude-mem.interface.js +5 -0
  81. package/build/src/_modules/migration/_services/fam-agent-memory-reader.service.js +125 -0
  82. package/build/src/_modules/migration/_services/fam-claude-mem-import.control-service.js +101 -18
  83. package/build/src/_modules/migration/_services/fam-import-dedup.data-service.js +53 -0
  84. package/build/src/_modules/migration/index.js +3 -1
  85. package/build/src/_modules/retrieval/_services/fam-retrieval-candidate.data-service.js +78 -4
  86. package/build/src/_modules/retrieval/_services/fam-retrieval.control-service.js +293 -50
  87. package/build/src/_modules/scope-reference/_collections/fam-scope-normalize.util.js +6 -3
  88. package/build/src/_modules/scope-reference/_services/fam-reference.data-service.js +18 -0
  89. package/build/src/_modules/scope-reference/_services/fam-scope-resolver.control-service.js +79 -20
  90. package/build/src/_routes/server/api/api.controller.js +34 -2
  91. package/build/src/_routes/server/client-app/client-app.control-service.js +1 -1
  92. package/build/src/_routes/server/server-status/server-status.controller.js +2 -1
  93. package/build/src/app.server.js +13 -1
  94. package/build/src/environments/environment.js +1 -1
  95. package/build/src/index.js +1 -1
  96. package/client-dist/{chunk-GHKRM4SM.js → chunk-I77GXVAQ.js} +1 -1
  97. package/client-dist/{chunk-LMTL7GA3.js → chunk-YXHWCJ5O.js} +1 -1
  98. package/client-dist/index.html +1 -1
  99. package/client-dist/{main-2KWB3QYK.js → main-PJPEDVJT.js} +1 -1
  100. package/package.json +6 -5
@@ -5,6 +5,7 @@ const fsm_dynamo_1 = require("@futdevpro/fsm-dynamo");
5
5
  const error_codes_const_1 = require("../../../_collections/error-codes.const");
6
6
  const fam_error_factory_util_1 = require("../../../_collections/fam-error-factory.util");
7
7
  const config_control_service_1 = require("../../../_routes/server/config/config.control-service");
8
+ const fam_embedding_prefix_util_1 = require("../_collections/fam-embedding-prefix.util");
8
9
  const fam_embedding_cost_control_service_1 = require("./fam-embedding-cost.control-service");
9
10
  const fam_lmstudio_embedding_provider_1 = require("./fam-lmstudio-embedding.provider");
10
11
  const fam_mock_embedding_provider_1 = require("./fam-mock-embedding.provider");
@@ -92,10 +93,19 @@ class FAM_Embedding_ControlService {
92
93
  fsm_dynamo_1.DyFM_Log.warn(`[FAM embedding cost-warn] ${decision.reason}`);
93
94
  }
94
95
  const provider = this.buildProvider(resolved, issuer);
96
+ // FEAT-004: task-prefix (query/document aszimmetria) — a provider-dispatch ELŐTT, a feloldott
97
+ // modell + callType alapján. A `mock` provider KIVÉTEL (`off`): a determinisztikus hash-en az
98
+ // aszimmetrikus prefix elrontaná a query↔doc egyezést (a teszt-double match-ét).
99
+ const prefixMode = resolved.provider === 'mock'
100
+ ? 'off'
101
+ : await this.resolveTaskPrefixMode({ table: set.table, scopePath: set.scopePath });
102
+ const texts = fam_embedding_prefix_util_1.FAM_EmbeddingPrefix_Util.apply({
103
+ texts: set.texts, modelId: resolved.modelId, callType: callType, mode: prefixMode,
104
+ });
95
105
  // Batch-elt dispatch (token-/rate-limit védelem; dsgn-007 embedding.batchSize).
96
106
  const allVectors = [];
97
- for (let i = 0; i < set.texts.length; i += resolved.batchSize) {
98
- const batch = set.texts.slice(i, i + resolved.batchSize);
107
+ for (let i = 0; i < texts.length; i += resolved.batchSize) {
108
+ const batch = texts.slice(i, i + resolved.batchSize);
99
109
  try {
100
110
  const vectors = await provider.embedTexts({ texts: batch, model: resolved.modelId, issuer: issuer });
101
111
  this.assertDimensions(vectors, resolved, issuer);
@@ -232,6 +242,15 @@ class FAM_Embedding_ControlService {
232
242
  }
233
243
  return process.env.OPENAI_EMBEDDING_MODEL || 'text-embedding-3-large';
234
244
  }
245
+ /**
246
+ * A task-prefix mód feloldása a config-precedencián (`embedding.taskPrefixMode`, FEAT-004). Ismeretlen /
247
+ * nem-enum érték → `auto` (a katalógus-default). A `mock`-provider-skip-et a hívó (`embedTexts`) kezeli.
248
+ */
249
+ async resolveTaskPrefixMode(context) {
250
+ const value = (await config_control_service_1.FAM_Config_ControlService.getInstance()
251
+ .resolve('embedding.taskPrefixMode', { table: context.table, scopePath: context.scopePath })).value;
252
+ return value === 'off' || value === 'on' || value === 'auto' ? value : 'auto';
253
+ }
235
254
  /** Az LM Studio base URL az ENV-ből (titok-szerű, nem DB; dsgn-006 §8). */
236
255
  lmStudioBaseUrl() {
237
256
  return process.env.LMSTUDIO_BASE_URL;
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FAM_Entry_DataService = void 0;
4
+ const tslib_1 = require("tslib");
4
5
  const nts_dynamo_1 = require("@futdevpro/nts-dynamo");
6
+ const mongoose_1 = tslib_1.__importDefault(require("mongoose"));
5
7
  const fam_entry_data_model_1 = require("../../../_models/data-models/fam-entry.data-model");
6
8
  /**
7
9
  * `FAM_Entry_DataService` (SP-2.2) — egy GENERIKUS, tár-paraméteres entry-CRUD a vektor-rétegnek.
@@ -39,6 +41,68 @@ class FAM_Entry_DataService extends nts_dynamo_1.DyNTS_DataService {
39
41
  async findHydratableList(filter) {
40
42
  return this.findDataList(filter);
41
43
  }
44
+ /**
45
+ * PERF (FAM-REV / BFR-AM-013): a boot-hidratálás **lean + `{_id, contentVector}`-projekciós** variánsa.
46
+ * A pool-feltöltés (`hydrateTable`) CSAK az `_id`-t és a `contentVector`-t olvassa — a `content` szöveg,
47
+ * a metadata, a `scopePath`/`tags`/`kind` stb. a hidratáláshoz SOHA nem kell. Ezért: (1) `{_id:1,
48
+ * contentVector:1}`-projekció → a `content` (~1 KB/entry) NEM utazik; (2) `.lean()` → plain object,
49
+ * NINCS Mongoose-doc-hidratálás (ez a boot-OOM-kockázat fő forrása 35k doc-nál — FAM-REV-062). A
50
+ * `_deleted:null` az aktív-szűrő (a `findDataList` DB-szintű soft-delete-kizárását replikálja: a
51
+ * soft-delete `_deleted`-et `Date`-re állítja, az aktívon hiányzik/`null`). Model-hiány (boot-bootstrap
52
+ * előtt) → `findDataList`-fallback (korrekt, csak teljes-doc). Ugyanaz a minta, mint a candidate-prefilter
53
+ * (`FAM_RetrievalCandidate_DataService.findActiveCandidates`).
54
+ */
55
+ async findHydratableVectorsLean(filter) {
56
+ const dataName = this.dataParams.dataName;
57
+ const model = mongoose_1.default.models[dataName];
58
+ if (!model) {
59
+ return this.findDataList(filter);
60
+ }
61
+ const leanFilter = { ...filter, _deleted: null };
62
+ const docs = await model.find(leanFilter, { _id: 1, contentVector: 1 }).lean().exec();
63
+ // KRITIKUS (_id-konzisztencia): a `.lean()` NYERS ObjectId `_id`-t ad, de a pool-kulcsoknak a
64
+ // `findDataList`-tel AZONOS string `_id`-nek kell lenniük (a candidate-prefilter candidateIds-ai
65
+ // ehhez matchelnek; ObjectId-kulcs → 0 hit). Stringify, hogy a hidratált pool-kulcsok stringek legyenek.
66
+ for (const doc of docs) {
67
+ doc._id = String(doc._id);
68
+ }
69
+ return docs;
70
+ }
71
+ /**
72
+ * PERF (boot-csúcs): a `findHydratableVectorsLean` STREAMING (Mongoose-cursor) variánsa — a teljes
73
+ * (~23k-doc, ~486 MB `contentVector`-os) listát SOHA nem tartja egyben a memóriában, hanem doc-onként
74
+ * yield-eli az `onVector` callback-nek (a pool-építés közben a doc GC-zhető). Így a boot tranziens-csúcsa
75
+ * a teljes-lista-méretről (~486 MB) ~egy-doc-méretre csökken → kisebb boot-OOM-kockázat + a steady-state
76
+ * RSS alacsonyabb high-water-markra áll be (a `normalizedOnly` megtakarítás láthatóvá válik). Az `_id`
77
+ * stringify itt is kötelező (pool-kulcs-konzisztencia). Visszaadja a yield-elt (érvényes) vektorok számát.
78
+ * Model-hiány (boot-bootstrap előtt) → `findHydratableVectorsLean`-fallback (teljes-lista, korrekt).
79
+ */
80
+ async streamHydratableVectors(filter, onVector) {
81
+ const dataName = this.dataParams.dataName;
82
+ const model = mongoose_1.default.models[dataName];
83
+ if (!model) {
84
+ const docs = await this.findHydratableVectorsLean(filter);
85
+ let fallbackCount = 0;
86
+ for (const doc of docs) {
87
+ if (doc._id && doc.contentVector?.length) {
88
+ onVector(String(doc._id), doc.contentVector);
89
+ fallbackCount++;
90
+ }
91
+ }
92
+ return fallbackCount;
93
+ }
94
+ const leanFilter = { ...filter, _deleted: null };
95
+ const cursor = model.find(leanFilter, { _id: 1, contentVector: 1 }).lean().cursor();
96
+ let count = 0;
97
+ for await (const raw of cursor) {
98
+ const doc = raw;
99
+ if (doc._id && doc.contentVector?.length) {
100
+ onVector(String(doc._id), doc.contentVector);
101
+ count++;
102
+ }
103
+ }
104
+ return count;
105
+ }
42
106
  /**
43
107
  * Egy entry vektor-mezőinek ATOMIKUS frissítése (`$set`) — a Mixed silent-drop ellen. A
44
108
  * `contentVector` + `embeddingModel` + `embeddingStatus` + opc. `contentHash` egy lépésben.
@@ -60,5 +124,76 @@ class FAM_Entry_DataService extends nts_dynamo_1.DyNTS_DataService {
60
124
  const update = { $set: { embeddingStatus: status } };
61
125
  await this.updateData({ filterBy: { _id: id }, update: update });
62
126
  }
127
+ /**
128
+ * A felismert azonosító-kódok (`referenceCodes`) atomikus frissítése (`$set`) — FEAT-003 inverted-index.
129
+ * Az embed-sikertől FÜGGETLEN write (a write-path az embed ELŐTT hívja), így a kód-citáció a még nem
130
+ * embeddelt / embed-bukott elemen is megjelenik a determinisztikus read-expanzióban.
131
+ */
132
+ async setReferenceCodes(id, referenceCodes) {
133
+ const update = { $set: { referenceCodes: referenceCodes } };
134
+ await this.updateData({ filterBy: { _id: id }, update: update });
135
+ }
136
+ /**
137
+ * A scan-provenance `source.root` backfill-je (FAM-REV-048 kiegészítés) — a re-scan `equal`
138
+ * (változatlan-content) ágán hívva, hogy a metaadat-frissítés a content-skip ELLENÉRE megtörténjen.
139
+ * **FELTÉTELES** `$set`: csak akkor ír, ha a `source.root` hiányzik VAGY eltér (a Mongo-feltétel no-op,
140
+ * ha már stimmel), így a teljes korpusz root-tudatossá válik (törölt-fájl reconciliation-lefedettség)
141
+ * re-embed nélkül. Dot-path kulcs `$set`/filter — a Dynamo-típus loose-object útján (mint `setVector`).
142
+ */
143
+ async backfillSourceRoot(id, root) {
144
+ const setBlock = { ['source.root']: root };
145
+ const update = { $set: setBlock };
146
+ const filterBy = { _id: id };
147
+ filterBy['source.root'] = { $ne: root };
148
+ await this.updateData({ filterBy: filterBy, update: update });
149
+ }
150
+ /**
151
+ * A `sourceFilePath` (display-relatív út) FELTÉTELES backfill-je (FAM-REV-050) — a re-scan `equal` ágán
152
+ * hívva, hogy a default (basePath-nélküli) relatív út KONZISZTENS legyen, miután a fájl-identitás az
153
+ * `absolutePath`-ra állt át (egy korábban más scan-gyökérről — pl. `scan-file` basename — ingestelt chunk
154
+ * relatív útja a friss scan-gyökérhez igazodjon). Csak akkor ír, ha eltér (`$ne` — no-op, ha már stimmel).
155
+ */
156
+ async backfillSourceFilePath(id, relativePath) {
157
+ const update = { $set: { sourceFilePath: relativePath } };
158
+ const filterBy = { _id: id };
159
+ filterBy.sourceFilePath = { $ne: relativePath };
160
+ await this.updateData({ filterBy: filterBy, update: update });
161
+ }
162
+ /**
163
+ * A `referenceCodes` FELTÉTELES backfill-je (FEAT-003 + 2026-06-20 audit) — a re-scan `equal` ágán
164
+ * hívva, hogy a kód-index a content-skip ELLENÉRE frissüljön (pl. a Tailwind-denylist-bővítés a régi
165
+ * `equal`-chunkokra is érvényesüljön). Csak akkor ír, ha a tárolt tömb ELTÉR az újonnan kinyertől
166
+ * (`referenceCodes: {$ne}` — a Mongo no-op, ha egyezik); a `extract` determinisztikus sorrendje miatt
167
+ * a változatlan content ugyanazt a tömböt adja → nincs fölös write.
168
+ */
169
+ async backfillReferenceCodes(id, codes) {
170
+ const update = { $set: { referenceCodes: codes } };
171
+ const filterBy = { _id: id };
172
+ filterBy['referenceCodes'] = { $ne: codes };
173
+ await this.updateData({ filterBy: filterBy, update: update });
174
+ }
175
+ /**
176
+ * A `position` (chunk char/sor-offset) FELTÉTELES backfill-je (FAM-REV-061) — a re-scan `equal` ágán hívva,
177
+ * hogy a chunker pozíció-fix (a sliding-ablakok per-ablak abszolút charStart-ja) a régi `equal`-chunkokra is
178
+ * érvényesüljön, RE-EMBED NÉLKÜL (a pozíció a vektort nem érinti). Csak akkor ír, ha a `position.charStart`
179
+ * ELTÉR (a bug szimptómája: a nagy-blokk sub-chunkjai mind a blokk-kezdetre ütköztek) — a Mongo no-op, ha
180
+ * már stimmel. A friss `position` mind a négy mezőt felülírja (char/sor egyszerre számolt → konzisztens).
181
+ */
182
+ async backfillPosition(id, position) {
183
+ const setBlock = {
184
+ ['position.charStart']: position.charStart,
185
+ ['position.charEnd']: position.charEnd,
186
+ };
187
+ if (position.lineStart !== undefined) {
188
+ setBlock['position.lineStart'] = position.lineStart;
189
+ }
190
+ if (position.lineEnd !== undefined) {
191
+ setBlock['position.lineEnd'] = position.lineEnd;
192
+ }
193
+ const update = { $set: setBlock };
194
+ const filterBy = { _id: id };
195
+ filterBy['position.charStart'] = { $ne: position.charStart };
196
+ await this.updateData({ filterBy: filterBy, update: update });
197
+ }
63
198
  }
64
199
  exports.FAM_Entry_DataService = FAM_Entry_DataService;
@@ -74,9 +74,17 @@ class FAM_VectorSearch_ControlService {
74
74
  this.poolDims.set(table, null);
75
75
  const dataService = new fam_entry_data_service_1.FAM_Entry_DataService({ dataParams: registryEntry.dataParams, issuer: this.issuer });
76
76
  const filter = { embeddingStatus: 'completed' };
77
- let entries;
77
+ let loaded = 0;
78
78
  try {
79
- entries = await dataService.findHydratableList(filter);
79
+ // PERF: lean + {_id, contentVector}-projekció + STREAMING (cursor) — a hidratálás CSAK ezt a két
80
+ // mezőt olvassa (nincs content-szöveg, nincs Mongoose-doc-hidratálás), ÉS doc-onként tölti a poolt
81
+ // (a teljes ~486 MB-os lista SOHA nincs egyben memóriában) → kisebb boot-csúcs + boot-OOM-kockázat.
82
+ await dataService.streamHydratableVectors(filter, (id, contentVector) => {
83
+ if (this.acceptVectorForPool(table, contentVector)) {
84
+ pool.addVector(id, contentVector);
85
+ loaded++;
86
+ }
87
+ });
80
88
  }
81
89
  catch (error) {
82
90
  throw fam_error_factory_util_1.FAM_Error_Util.create({
@@ -87,16 +95,6 @@ class FAM_VectorSearch_ControlService {
87
95
  context: { operation: 'hydrate', table: table },
88
96
  });
89
97
  }
90
- let loaded = 0;
91
- for (const entry of entries) {
92
- if (!entry._id || !entry.contentVector?.length) {
93
- continue;
94
- }
95
- if (this.acceptVectorForPool(table, entry.contentVector)) {
96
- pool.addVector(entry._id, entry.contentVector);
97
- loaded++;
98
- }
99
- }
100
98
  this.hydrated.set(table, true);
101
99
  fsm_dynamo_1.DyFM_Log.log(`[FAM hydrate] '${table}' tár: ${loaded} vektor betöltve az in-memory pool-ba.`);
102
100
  }
@@ -143,7 +141,7 @@ class FAM_VectorSearch_ControlService {
143
141
  });
144
142
  }
145
143
  const pool = this.getOrCreatePool(input.table);
146
- const poolSize = pool.getAll().size;
144
+ const poolSize = pool.size();
147
145
  if (!poolSize) {
148
146
  // Bootstrap-skip jelzés (dsgn-006 §4.1): üres pool (embedding nincs konfigurálva / nincs
149
147
  // completed entry). NEM néma — a capability + UI piros banner ezt jelzi (dsgn-008/dsgn-011).
@@ -166,7 +164,15 @@ class FAM_VectorSearch_ControlService {
166
164
  }
167
165
  /** Egy tár pool-jának mérete (a `test_vector_search` / `get_storage_usage` capability — dsgn-003). */
168
166
  getPoolSize(table) {
169
- return this.getOrCreatePool(table).getAll().size;
167
+ return this.getOrCreatePool(table).size();
168
+ }
169
+ /**
170
+ * Egy tár pool-jának tényleges (megfigyelt) embedding-dimenziója (FAM-REV-044) — az első elfogadott
171
+ * vektornál rögzül (`acceptVectorForPool`). `null`, ha a pool még üres / nincs dimenzió. A
172
+ * `get_embedding_config` ezzel jelenti a runtime-valóságot a (gyakran null) deklarált dim mellett.
173
+ */
174
+ getPoolDim(table) {
175
+ return this.poolDims.get(table) ?? null;
170
176
  }
171
177
  /** Hidratált-e már egy tár (a bootstrap-skip diagnosztikához). */
172
178
  isHydrated(table) {
@@ -181,20 +187,13 @@ class FAM_VectorSearch_ControlService {
181
187
  * szűkített halmazra korlátozódik (a jelöltszám bizonyíthatóan kisebb a teljes tárnál — acceptance #4).
182
188
  */
183
189
  searchInCandidates(pool, queryVector, candidateIds, topK) {
184
- const all = pool.getAll();
185
- const scoped = new local_vector_search_1.LVS_VectorPool_ControlService();
186
- let scopedCount = 0;
187
- for (const id of candidateIds) {
188
- const vector = all.get(id);
189
- if (vector) {
190
- scoped.addVector(id, vector);
191
- scopedCount++;
192
- }
193
- }
194
- if (!scopedCount) {
195
- return [];
196
- }
197
- const results = scoped.search(queryVector, topK, local_vector_search_1.LVS_Search_Mode.cosineSimilarity);
190
+ // PERF (nts-dynamo ^1.15.71): a pool NATÍVAN szűr a candidate-ID halmazra — a MÁR előre-normalizált
191
+ // pool-on iterál, a nem-jelölteket `Set.has` O(1)-gyel kihagyja. Így NINCS per-query szűkített-pool-
192
+ // építés + a vektorok ÚJRA-normalizálása (a régi út 24k-jelöltnél ~24k l2Normalize-alloc + GC-spike-ot
193
+ // okozott; bench: unscoped 985ms → 90ms = 11×, scoped 37ms → 5ms = 7×, top-egyezés identikus). A
194
+ // pool candidateIds halmaz pontosan ugyanaz, mint a régi (a pool csak a hidratált entry-ket tartja).
195
+ const candidateSet = new Set(candidateIds);
196
+ const results = pool.search(queryVector, topK, local_vector_search_1.LVS_Search_Mode.cosineSimilarity, candidateSet);
198
197
  return results.map((hit) => ({ id: hit.id, score: hit.score }));
199
198
  }
200
199
  /** A query-vektor feloldása: kész `queryVector`, vagy a `queryText` embeddelése (provider-agnosztikus). */
@@ -216,18 +215,29 @@ class FAM_VectorSearch_ControlService {
216
215
  getOrCreatePool(table) {
217
216
  let pool = this.pools.get(table);
218
217
  if (!pool) {
219
- pool = new local_vector_search_1.LVS_VectorPool_ControlService();
218
+ // PERF (nts-dynamo ^1.15.72): a FAM KIZÁRÓLAG cosine-keresést használ + SOSEM olvas nyers vektor-
219
+ // értéket (csak `size()` + `search(cosine)`), ezért a nyers `vectorPool` holt súly. `normalizedOnly`
220
+ // → a pool csak a normalizált másolatot tartja → ~50% pool-memória (35k vektor @2560-dim: ~700MB).
221
+ pool = new local_vector_search_1.LVS_VectorPool_ControlService({ normalizedOnly: true });
220
222
  this.pools.set(table, pool);
221
223
  this.poolDims.set(table, null);
222
224
  }
223
225
  return pool;
224
226
  }
225
227
  /**
226
- * Dimenzió-homogenitás kapu (dsgn-006 §6.2 / acceptance #6): a pool első vektora rögzíti a
227
- * dimenziót; az ettől eltérő dimenziójú vektort NEM vesszük fel (nem kever eltérő dimenziót egy
228
- * tár pool-jában a dimenzió-váltás explicit re-embedet igényel). Eltérés warn (nem néma).
228
+ * Pool-felvételi kapu: (1) finiteség (NaN/±Inf-szűrés) + (2) dimenzió-homogenitás (dsgn-006 §6.2 /
229
+ * acceptance #6). A pool első vektora rögzíti a dimenziót; az ettől eltérő dimenziójú vektort NEM
230
+ * vesszük fel (a dimenzió-váltás explicit re-embedet igényel). Mindkét elutasítás warn (nem néma).
229
231
  */
230
232
  acceptVectorForPool(table, vector) {
233
+ // Finiteség-kapu (consumer-szintű BFR-AM-013 mitigáció): egy nem-véges (NaN/±Inf) elemű vektor
234
+ // némán megmérgezné a koszinusz-rangsort (a NaN-összehasonlítások kiejtik a valódi találatokat),
235
+ // ezért a pool-ba NEM kerül be. Az engine-szintű dot-product-guard továbbra is bedrock-igény.
236
+ if (!vector.every((value) => Number.isFinite(value))) {
237
+ fsm_dynamo_1.DyFM_Log.warn(`[FAM vector-pool] '${table}' tár nem-véges (NaN/±Inf) vektor-elem → kihagyva `
238
+ + `(hibás embedding / csonka write; BFR-AM-013).`);
239
+ return false;
240
+ }
231
241
  const current = this.poolDims.get(table);
232
242
  if (current === null || current === undefined) {
233
243
  this.poolDims.set(table, vector.length);
@@ -8,7 +8,7 @@
8
8
  * (BFR-AM-002/008); a bedrock-csere (MP-15) a `FAM_EmbeddingProvider` interfész mögött non-breaking.
9
9
  */
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
- exports.FAM_EmbeddingBootstrap_ControlService = exports.FAM_EmbeddingCost_ControlService = exports.FAM_EmbeddingPipeline_ControlService = exports.FAM_EmbeddingPreset_ControlService = exports.FAM_StoreRegistry_Util = exports.FAM_STORE_REGISTRY = exports.FAM_Entry_DataService = exports.FAM_VectorSearch_ControlService = exports.FAM_MOCK_EMBEDDING_DIMS = exports.FAM_Mock_EmbeddingProvider = exports.FAM_LMStudio_EmbeddingProvider = exports.FAM_OpenAI_EmbeddingProvider = exports.FAM_Embedding_ControlService = void 0;
11
+ exports.FAM_EmbeddingBootstrap_ControlService = exports.FAM_DuplicateScan_ControlService = exports.FAM_EmbeddingCost_ControlService = exports.FAM_EmbeddingPipeline_ControlService = exports.FAM_EmbeddingPreset_ControlService = exports.FAM_StoreRegistry_Util = exports.FAM_STORE_REGISTRY = exports.FAM_Entry_DataService = exports.FAM_VectorSearch_ControlService = exports.FAM_MOCK_EMBEDDING_DIMS = exports.FAM_Mock_EmbeddingProvider = exports.FAM_LMStudio_EmbeddingProvider = exports.FAM_OpenAI_EmbeddingProvider = exports.FAM_Embedding_ControlService = void 0;
12
12
  // SP-2.1 — provider-dispatch kapu + providerek
13
13
  var fam_embedding_control_service_1 = require("./_services/fam-embedding.control-service");
14
14
  Object.defineProperty(exports, "FAM_Embedding_ControlService", { enumerable: true, get: function () { return fam_embedding_control_service_1.FAM_Embedding_ControlService; } });
@@ -35,6 +35,9 @@ var fam_embedding_pipeline_control_service_1 = require("./_services/fam-embeddin
35
35
  Object.defineProperty(exports, "FAM_EmbeddingPipeline_ControlService", { enumerable: true, get: function () { return fam_embedding_pipeline_control_service_1.FAM_EmbeddingPipeline_ControlService; } });
36
36
  var fam_embedding_cost_control_service_1 = require("./_services/fam-embedding-cost.control-service");
37
37
  Object.defineProperty(exports, "FAM_EmbeddingCost_ControlService", { enumerable: true, get: function () { return fam_embedding_cost_control_service_1.FAM_EmbeddingCost_ControlService; } });
38
+ // near-duplikátum FELDERÍTŐ (read-only; user-direktíva 2026-06-21)
39
+ var fam_duplicate_scan_control_service_1 = require("./_services/fam-duplicate-scan.control-service");
40
+ Object.defineProperty(exports, "FAM_DuplicateScan_ControlService", { enumerable: true, get: function () { return fam_duplicate_scan_control_service_1.FAM_DuplicateScan_ControlService; } });
38
41
  // boot-hook (App.postProcess)
39
42
  var fam_embedding_bootstrap_control_service_1 = require("./_services/fam-embedding-bootstrap.control-service");
40
43
  Object.defineProperty(exports, "FAM_EmbeddingBootstrap_ControlService", { enumerable: true, get: function () { return fam_embedding_bootstrap_control_service_1.FAM_EmbeddingBootstrap_ControlService; } });
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FAM_AUTHORED_SOURCE_TYPES = void 0;
4
+ exports.isAuthoredSource = isAuthoredSource;
5
+ /**
6
+ * FEAT-007 — a **non-file-derived** (kézzel / agent-írt) bejegyzés-források halmaza. Ezek a `source.type`-ok
7
+ * NEM regenerálhatók re-scan-nel: a `manual` (felhasználó kézzel írta) és az `agent` (agent-write) tartalom a
8
+ * **pótolhatatlan, kurált érték** — szemben a `scan` / `scan-summary` (fájlból generált, re-scan visszahozza)
9
+ * és az `import` (külső forrásból re-importálható) bejegyzésekkel. A backup/export ezekre fókuszál.
10
+ */
11
+ exports.FAM_AUTHORED_SOURCE_TYPES = ['manual', 'agent'];
12
+ /**
13
+ * Egy bejegyzés **authored** (non-file-derived, pótolhatatlan)-e a `source.type` alapján. A hiányzó/ismeretlen
14
+ * `source` defenzíven NEM authored (a fájl-derived túlsúly miatt biztonságos default — a backup nem hígul fel
15
+ * scan-elt elemekkel; a tényleges authored elemeknek mindig van `manual`/`agent` source-uk a write-úton).
16
+ */
17
+ function isAuthoredSource(source) {
18
+ if (!source) {
19
+ return false;
20
+ }
21
+ return exports.FAM_AUTHORED_SOURCE_TYPES.includes(source.type);
22
+ }
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FAM_Export_ControlService = void 0;
4
+ const embedding_1 = require("../../embedding");
5
+ const fam_export_const_1 = require("../_collections/fam-export.const");
6
+ /**
7
+ * `FAM_Export_ControlService` (FEAT-007) — a **non-file-derived, pótolhatatlan** (kézzel/agent-írt) bejegyzések
8
+ * külön exportja/biztonsági mentése. A `scan`/`scan-summary` (fájlból re-scannelhető) és `import` (re-importálható)
9
+ * elemekkel szemben a `manual`/`agent` source-ú `memory`/`knowledge`/… bejegyzés re-scan-nel NEM regenerálható —
10
+ * ezt a kurált értéket menti ki a hívó (CLI `fam export` / `export_authored` capability) egy JSON-fájlba.
11
+ *
12
+ * **Mongo-szintű szűrés:** per-tár `findDataList({ 'source.type': $in [manual, agent] })` — az authored-halmaz
13
+ * kicsi (tipikusan tucatnyi), így a teljes-doc olvasás nem perf-gond; a `contentVector` az export-mappolásnál
14
+ * KIMARAD (a kurált CONTENT a pótolhatatlan, a vektor re-embeddel regenerálható). A `isAuthoredSource` helper a
15
+ * provenance-SSOT (a Mongo-filter mellett defenzív in-memory ellenőrzés).
16
+ *
17
+ * **FIGYELEM (memory: dynts_dataservice_eager_resolve):** per-tár lazy `new FAM_Entry_DataService` (nincs élő mező).
18
+ */
19
+ class FAM_Export_ControlService {
20
+ issuer = 'FAM_Export_ControlService';
21
+ /**
22
+ * Az ÖSSZES tár authored (non-file-derived) bejegyzésének exportja. Tárakon iterál (`FAM_STORE_REGISTRY`),
23
+ * per-tár `source.type ∈ {manual, agent}`-re szűr, és a kurált mezőket adja vissza (a `contentVector` nélkül).
24
+ */
25
+ async exportAuthored() {
26
+ const byTable = {};
27
+ const entries = [];
28
+ for (const registryEntry of embedding_1.FAM_STORE_REGISTRY) {
29
+ const storeEntries = await this.loadAuthored(registryEntry);
30
+ byTable[registryEntry.table] = storeEntries.length;
31
+ for (const entry of storeEntries) {
32
+ entries.push(this.toExportEntry(entry, registryEntry.table));
33
+ }
34
+ }
35
+ return { formatVersion: 1, total: entries.length, byTable: byTable, entries: entries };
36
+ }
37
+ /** Egy tár authored bejegyzései (Mongo-filter `source.type $in [manual, agent]` + defenzív helper-szűrés). */
38
+ async loadAuthored(registryEntry) {
39
+ const dataService = new embedding_1.FAM_Entry_DataService({ dataParams: registryEntry.dataParams, issuer: this.issuer });
40
+ const filter = {};
41
+ filter['source.type'] = { $in: fam_export_const_1.FAM_AUTHORED_SOURCE_TYPES };
42
+ const storeEntries = await dataService.findDataList(filter);
43
+ // A helper a provenance-SSOT: a hiányzó/ismeretlen source defenzíven kizárandó (a Mongo-filter már szűrt).
44
+ return storeEntries.filter((entry) => (0, fam_export_const_1.isAuthoredSource)(entry.source));
45
+ }
46
+ /** Egy `FAM_Entry` → exportált authored-bejegyzés (a `contentVector` KIHAGYVA — re-embeddel regenerálható). */
47
+ toExportEntry(entry, table) {
48
+ return {
49
+ id: String(entry._id),
50
+ table: table,
51
+ content: entry.content ?? '',
52
+ source: entry.source,
53
+ kind: entry.kind,
54
+ tags: entry.tags,
55
+ weight: entry.weight,
56
+ scopePath: entry.scopePath,
57
+ referenceCodes: entry.referenceCodes,
58
+ headingPath: entry.headingPath,
59
+ createdMs: entry.__created ? new Date(entry.__created).getTime() : undefined,
60
+ lastModifiedMs: entry.__lastModified ? new Date(entry.__lastModified).getTime() : undefined,
61
+ };
62
+ }
63
+ }
64
+ exports.FAM_Export_ControlService = FAM_Export_ControlService;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isAuthoredSource = exports.FAM_AUTHORED_SOURCE_TYPES = exports.FAM_Export_ControlService = void 0;
4
+ var fam_export_control_service_1 = require("./_services/fam-export.control-service");
5
+ Object.defineProperty(exports, "FAM_Export_ControlService", { enumerable: true, get: function () { return fam_export_control_service_1.FAM_Export_ControlService; } });
6
+ var fam_export_const_1 = require("./_collections/fam-export.const");
7
+ Object.defineProperty(exports, "FAM_AUTHORED_SOURCE_TYPES", { enumerable: true, get: function () { return fam_export_const_1.FAM_AUTHORED_SOURCE_TYPES; } });
8
+ Object.defineProperty(exports, "isAuthoredSource", { enumerable: true, get: function () { return fam_export_const_1.isAuthoredSource; } });
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FAM_FamIgnore_Util = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const fs = tslib_1.__importStar(require("fs"));
6
+ const path = tslib_1.__importStar(require("path"));
7
+ /**
8
+ * `FAM_FamIgnore_Util` (descriptive user feedback / nagy-scan ergonómia) — a **`.fdpfamignore`** in-repo,
9
+ * gitignore-stílusú ignore-fájl támogatása. A projekt (vagy a workspace-gyökér) MAGÁBAN hordozza, mit hagyjon
10
+ * ki a FAM-scan — a config `scan.ignorePatterns` MELLETT (HOZZÁADÓDIK, nem felülír). A scan a `resolvedRoot`-ban
11
+ * lévő `.fdpfamignore`-t olvassa (scan-folder/scan-project; a scan-file egyetlen explicit fájlt céloz).
12
+ *
13
+ * **Szintaxis (gitignore-szerű, a `FAM_GlobMatch_Util`-ra fordítva):** soronként egy minta; `#` komment + üres
14
+ * sor kihagyva. `node_modules` (csupasz név) → bárhol illeszkedik; `/build` (vezető `/`) → a gyökérhez horgonyzott;
15
+ * `dist/` (záró `/`) → könyvtár; `src/gen` (beágyazott `/`) → gyökér-relatív; `*.tgz` → bárhol. **A `!` negáció
16
+ * v1-ben NEM támogatott** (a flat „matchesAny → skip" modellben — BACKLOG), az ilyen sorok kihagyva.
17
+ *
18
+ * **v1 hatókör:** a scan-GYÖKÉR `.fdpfamignore`-ja (a teljes scanre). A NESTED (per-alkönyvtár) `.fdpfamignore`
19
+ * a két-fázisú felderítés (walk → classify) miatt külön kör — BACKLOG (a `baseDir` paraméter már készen áll rá).
20
+ */
21
+ class FAM_FamIgnore_Util {
22
+ /** Az ignore-fájl kanonikus neve. */
23
+ static FILENAME = '.fdpfamignore';
24
+ /**
25
+ * Egy `.fdpfamignore` tartalom → nyers minta-sorok (trim; `#`-komment + üres + `!`-negáció kihagyva).
26
+ * Tisztán pure — a fordítást (`toGlobPatterns`) külön végzi, hogy a parse önállóan tesztelhető legyen.
27
+ */
28
+ static parse(content) {
29
+ const result = [];
30
+ for (const rawLine of content.split(/\r?\n/)) {
31
+ const line = rawLine.trim();
32
+ if (!line || line.startsWith('#') || line.startsWith('!')) {
33
+ continue;
34
+ }
35
+ result.push(line);
36
+ }
37
+ return result;
38
+ }
39
+ /**
40
+ * Nyers gitignore-szerű minták → a `FAM_GlobMatch_Util` mintái (az ENTRY + a TARTALMA: `<base>` + `<base>/**`),
41
+ * a `baseDir` (root-relatív könyvtár, ahol az ignore-fájl van) elé fűzve. Horgonyzás: vezető `/` VAGY beágyazott
42
+ * `/` → a `baseDir`-hez horgonyzott; csupasz név → globális (bárhol-illeszkedő) prefix. A záró `/` (könyvtár-jel)
43
+ * levágva (a FAM a fájlt és a könyvtárat egyformán skip-eli).
44
+ */
45
+ static toGlobPatterns(lines, baseDir = '') {
46
+ const prefix = baseDir ? `${baseDir.replace(/\\/g, '/').replace(/\/+$/, '')}/` : '';
47
+ const patterns = [];
48
+ for (const rawLine of lines) {
49
+ let line = rawLine.trim();
50
+ const anchored = line.startsWith('/');
51
+ if (anchored) {
52
+ line = line.slice(1);
53
+ }
54
+ if (line.endsWith('/')) {
55
+ line = line.slice(0, -1);
56
+ }
57
+ if (!line) {
58
+ continue;
59
+ }
60
+ const base = (anchored || line.includes('/')) ? `${prefix}${line}` : `${prefix}**/${line}`;
61
+ patterns.push(base, `${base}/**`);
62
+ }
63
+ return patterns;
64
+ }
65
+ /**
66
+ * A `.fdpfamignore` beolvasása + fordítása egy könyvtárból (a `dirAbsolutePath`-ban). Hiányzó / olvashatatlan
67
+ * fájl → `[]` (opt-in: csak ha létezik). A `baseDir` a root-relatív horgony (a gyökér-szintű hívásnál `''`).
68
+ */
69
+ static loadFromDir(dirAbsolutePath, baseDir = '') {
70
+ try {
71
+ const filePath = path.join(dirAbsolutePath, FAM_FamIgnore_Util.FILENAME);
72
+ if (!fs.existsSync(filePath)) {
73
+ return [];
74
+ }
75
+ return FAM_FamIgnore_Util.toGlobPatterns(FAM_FamIgnore_Util.parse(fs.readFileSync(filePath, 'utf-8')), baseDir);
76
+ }
77
+ catch {
78
+ // Az ignore-fájl olvasás-hibája SOHA nem buktatja a scant (opt-in, best-effort).
79
+ return [];
80
+ }
81
+ }
82
+ }
83
+ exports.FAM_FamIgnore_Util = FAM_FamIgnore_Util;