@futdevpro/fdp-agent-memory 0.1.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 (117) hide show
  1. package/README.md +345 -0
  2. package/build/package.json +96 -0
  3. package/build/src/_assets/mcp-client-config/README.md +29 -0
  4. package/build/src/_assets/mcp-client-config/claude_desktop_config.json +15 -0
  5. package/build/src/_assets/mcp-client-config/mcp.json +15 -0
  6. package/build/src/_collections/config-catalog.const.js +180 -0
  7. package/build/src/_collections/config-error-codes.const.js +30 -0
  8. package/build/src/_collections/config-presets.const.js +25 -0
  9. package/build/src/_collections/error-banners.const.js +100 -0
  10. package/build/src/_collections/error-codes.const.js +150 -0
  11. package/build/src/_collections/fam-db-models.const.js +37 -0
  12. package/build/src/_collections/fam-entry-bootstrap.util.js +80 -0
  13. package/build/src/_collections/fam-error-context.util.js +90 -0
  14. package/build/src/_collections/fam-error-factory.util.js +64 -0
  15. package/build/src/_enums/fam-config-level.type-enum.js +15 -0
  16. package/build/src/_enums/fam-table.type-enum.js +20 -0
  17. package/build/src/_integration-tests/_helpers/fam-integration-test-setup.util.js +105 -0
  18. package/build/src/_models/data-models/fam-codebase.data-model.js +51 -0
  19. package/build/src/_models/data-models/fam-coding-patterns.data-model.js +58 -0
  20. package/build/src/_models/data-models/fam-config.data-model.js +68 -0
  21. package/build/src/_models/data-models/fam-documents.data-model.js +53 -0
  22. package/build/src/_models/data-models/fam-entry-base-properties.const.js +43 -0
  23. package/build/src/_models/data-models/fam-entry.data-model.js +81 -0
  24. package/build/src/_models/data-models/fam-error.data-model.js +88 -0
  25. package/build/src/_models/data-models/fam-ingest-run.data-model.js +74 -0
  26. package/build/src/_models/data-models/fam-knowledge.data-model.js +48 -0
  27. package/build/src/_models/data-models/fam-memory.data-model.js +55 -0
  28. package/build/src/_models/data-models/fam-reference.data-model.js +67 -0
  29. package/build/src/_models/data-models/fam-rules.data-model.js +51 -0
  30. package/build/src/_models/data-models/fam-scope.data-model.js +52 -0
  31. package/build/src/_models/interfaces/fam-common.interface.js +23 -0
  32. package/build/src/_models/interfaces/fam-config.interface.js +2 -0
  33. package/build/src/_models/interfaces/fam-error.interface.js +2 -0
  34. package/build/src/_modules/embedding/_collections/fam-embedding-pricing.const.js +22 -0
  35. package/build/src/_modules/embedding/_collections/fam-store-registry.const.js +63 -0
  36. package/build/src/_modules/embedding/_models/interfaces/fam-embedding-cost.interface.js +10 -0
  37. package/build/src/_modules/embedding/_models/interfaces/fam-embedding-provider.interface.js +2 -0
  38. package/build/src/_modules/embedding/_models/interfaces/fam-resolved-provider.interface.js +2 -0
  39. package/build/src/_modules/embedding/_services/fam-embedding-bootstrap.control-service.js +52 -0
  40. package/build/src/_modules/embedding/_services/fam-embedding-cost.control-service.js +175 -0
  41. package/build/src/_modules/embedding/_services/fam-embedding-pipeline.control-service.js +202 -0
  42. package/build/src/_modules/embedding/_services/fam-embedding-preset.control-service.js +66 -0
  43. package/build/src/_modules/embedding/_services/fam-embedding.control-service.js +253 -0
  44. package/build/src/_modules/embedding/_services/fam-entry.data-service.js +64 -0
  45. package/build/src/_modules/embedding/_services/fam-lmstudio-embedding.provider.js +112 -0
  46. package/build/src/_modules/embedding/_services/fam-mock-embedding.provider.js +64 -0
  47. package/build/src/_modules/embedding/_services/fam-openai-embedding.provider.js +64 -0
  48. package/build/src/_modules/embedding/_services/fam-vector-search.control-service.js +244 -0
  49. package/build/src/_modules/embedding/index.js +40 -0
  50. package/build/src/_modules/ingest/_collections/fam-content-hash.util.js +35 -0
  51. package/build/src/_modules/ingest/_collections/fam-file-routing.util.js +95 -0
  52. package/build/src/_modules/ingest/_collections/fam-glob-match.util.js +84 -0
  53. package/build/src/_modules/ingest/_collections/fam-md-chunker.util.js +164 -0
  54. package/build/src/_modules/ingest/_collections/fam-scan-path.util.js +91 -0
  55. package/build/src/_modules/ingest/_collections/fam-secret-exclude.util.js +54 -0
  56. package/build/src/_modules/ingest/_collections/fam-sliding-chunker.util.js +76 -0
  57. package/build/src/_modules/ingest/_collections/fam-ts-chunker.util.js +316 -0
  58. package/build/src/_modules/ingest/_models/interfaces/fam-ingest.interface.js +2 -0
  59. package/build/src/_modules/ingest/_services/fam-chunker.control-service.js +114 -0
  60. package/build/src/_modules/ingest/_services/fam-delta-compare.util.js +74 -0
  61. package/build/src/_modules/ingest/_services/fam-ingest-run.data-service.js +85 -0
  62. package/build/src/_modules/ingest/_services/fam-ingest.control-service.js +384 -0
  63. package/build/src/_modules/ingest/_services/fam-scan.control-service.js +211 -0
  64. package/build/src/_modules/ingest/index.js +46 -0
  65. package/build/src/_modules/mcp/_collections/fam-core-tools.const.js +186 -0
  66. package/build/src/_modules/mcp/_models/interfaces/fam-mcp.interface.js +31 -0
  67. package/build/src/_modules/mcp/_services/fam-capabilities-tool.service.js +111 -0
  68. package/build/src/_modules/mcp/_services/fam-capability-registry.service.js +1180 -0
  69. package/build/src/_modules/mcp/_services/fam-mcp-adapter.service.js +123 -0
  70. package/build/src/_modules/mcp/_services/fam-mcp-server.service.js +69 -0
  71. package/build/src/_modules/mcp/_services/fam-read-tool.service.js +99 -0
  72. package/build/src/_modules/mcp/_services/fam-write-tool.service.js +460 -0
  73. package/build/src/_modules/mcp/index.js +35 -0
  74. package/build/src/_modules/migration/_collections/fam-claude-mem-normalize.util.js +166 -0
  75. package/build/src/_modules/migration/_collections/fam-import-content-hash.util.js +38 -0
  76. package/build/src/_modules/migration/_collections/fam-target-mapping.util.js +90 -0
  77. package/build/src/_modules/migration/_enums/fam-claude-mem-source.type-enum.js +20 -0
  78. package/build/src/_modules/migration/_models/interfaces/fam-claude-mem.interface.js +26 -0
  79. package/build/src/_modules/migration/_services/fam-claude-mem-export-reader.service.js +134 -0
  80. package/build/src/_modules/migration/_services/fam-claude-mem-import.control-service.js +533 -0
  81. package/build/src/_modules/migration/_services/fam-claude-mem-sqlite-reader.service.js +144 -0
  82. package/build/src/_modules/migration/_services/fam-claude-mem-worker-reader.service.js +115 -0
  83. package/build/src/_modules/migration/_services/fam-import-dedup.data-service.js +102 -0
  84. package/build/src/_modules/migration/index.js +38 -0
  85. package/build/src/_modules/retrieval/_models/interfaces/fam-retrieval.interface.js +2 -0
  86. package/build/src/_modules/retrieval/_services/fam-retrieval-candidate.data-service.js +67 -0
  87. package/build/src/_modules/retrieval/_services/fam-retrieval-suggestions.util.js +182 -0
  88. package/build/src/_modules/retrieval/_services/fam-retrieval.control-service.js +282 -0
  89. package/build/src/_modules/retrieval/index.js +22 -0
  90. package/build/src/_modules/scope-reference/_collections/fam-fuzzy-match.util.js +86 -0
  91. package/build/src/_modules/scope-reference/_collections/fam-scope-normalize.util.js +47 -0
  92. package/build/src/_modules/scope-reference/_models/interfaces/fam-reference-resolution.interface.js +2 -0
  93. package/build/src/_modules/scope-reference/_models/interfaces/fam-resolution-trace.interface.js +2 -0
  94. package/build/src/_modules/scope-reference/_services/fam-reference.data-service.js +179 -0
  95. package/build/src/_modules/scope-reference/_services/fam-scope-resolver.control-service.js +473 -0
  96. package/build/src/_modules/scope-reference/_services/fam-scope.data-service.js +215 -0
  97. package/build/src/_modules/scope-reference/index.js +26 -0
  98. package/build/src/_routes/server/api/api.controller.js +400 -0
  99. package/build/src/_routes/server/client-app/client-app.control-service.js +132 -0
  100. package/build/src/_routes/server/client-app/client-app.controller.js +35 -0
  101. package/build/src/_routes/server/config/config.control-service.js +476 -0
  102. package/build/src/_routes/server/config/config.data-service.js +49 -0
  103. package/build/src/_routes/server/errors/errors.control-service.js +123 -0
  104. package/build/src/_routes/server/errors/errors.controller.js +65 -0
  105. package/build/src/_routes/server/errors/errors.data-service.js +80 -0
  106. package/build/src/_routes/server/server-status/server-status.control-service.js +19 -0
  107. package/build/src/_routes/server/server-status/server-status.controller.js +39 -0
  108. package/build/src/app.server.js +122 -0
  109. package/build/src/environments/environment.js +20 -0
  110. package/build/src/index.js +18 -0
  111. package/client-dist/chunk-GHKRM4SM.js +1 -0
  112. package/client-dist/chunk-LMTL7GA3.js +575 -0
  113. package/client-dist/index.html +17 -0
  114. package/client-dist/main-2KWB3QYK.js +2 -0
  115. package/client-dist/polyfills-HGDOEU5L.js +2 -0
  116. package/client-dist/styles-3J7JD5YE.css +1 -0
  117. package/package.json +96 -0
@@ -0,0 +1,244 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FAM_VectorSearch_ControlService = void 0;
4
+ const fsm_dynamo_1 = require("@futdevpro/fsm-dynamo");
5
+ const local_vector_search_1 = require("@futdevpro/nts-dynamo/local-vector-search");
6
+ const error_codes_const_1 = require("../../../_collections/error-codes.const");
7
+ const fam_error_factory_util_1 = require("../../../_collections/fam-error-factory.util");
8
+ const fam_store_registry_const_1 = require("../_collections/fam-store-registry.const");
9
+ const fam_embedding_control_service_1 = require("./fam-embedding.control-service");
10
+ const fam_entry_data_service_1 = require("./fam-entry.data-service");
11
+ /**
12
+ * `FAM_VectorSearch_ControlService` (SP-2.2, dsgn-006 §4) — **Memory Vector Search** (BFR-AM-001
13
+ * workaround). Singleton. A vektorok a SAJÁT MongoDB-ben élnek (`FAM_Entry.contentVector`); boot-kor
14
+ * a Dynamo `LVS_VectorPool_ControlService` **in-memory** pool-jába hidratálódnak (tár-particionált).
15
+ * A `read` (MP-5) ezen a pool-on futtat koszinusz-keresést. **NEM** MongoDB Atlas Vector Search.
16
+ *
17
+ * **Pool-választás:** a magas-szintű `DyNTS_LVS_VectorDataService` minden `vectorSearch`-nél a DB-ből
18
+ * újraépítené a pool-t + string-input-ot vektorizálna (OpenAI). A FAM ehelyett közvetlenül a
19
+ * `LVS_VectorPool_ControlService`-t (a Dynamo LVS in-memory engine-je) használja per-tár, perzisztens
20
+ * Mongo-hidratálással — így VALÓDI per-hit koszinusz-score-t ad (BFR-AM-007) és a query-vektort a
21
+ * provider-agnosztikus `FAM_Embedding_ControlService` adja (NEM hardwired OpenAI).
22
+ *
23
+ * **Dimenzió-konzisztencia (acceptance #6):** egy tár pool-ja nem kever eltérő dimenziójú vektort —
24
+ * a hidratálás csak a `dims`-egyező completed entry-ket veszi fel (a dimenzió-váltás re-embedet igényel).
25
+ */
26
+ class FAM_VectorSearch_ControlService {
27
+ static _instance;
28
+ issuer = 'FAM_VectorSearch_ControlService';
29
+ /** Tár-particionált in-memory pool-ok (a Dynamo LVS engine-jén; BFR-AM-001). */
30
+ pools = new Map();
31
+ /** A pool dimenziója táranként (homogenitás-ellenőrzés; null = még üres pool). */
32
+ poolDims = new Map();
33
+ /** Hidratált-e már az adott tár pool-ja (a `search` bootstrap-skip jelzéséhez). */
34
+ hydrated = new Map();
35
+ static getInstance() {
36
+ if (!FAM_VectorSearch_ControlService._instance) {
37
+ FAM_VectorSearch_ControlService._instance = new FAM_VectorSearch_ControlService();
38
+ }
39
+ return FAM_VectorSearch_ControlService._instance;
40
+ }
41
+ // =========================================================================
42
+ // BOOT-HIDRATÁLÁS (dsgn-006 §4.1)
43
+ // =========================================================================
44
+ /**
45
+ * A teljes vektor-pool hidratálása minden fő tárra (dsgn-006 §4.1). Szerver-boot-kor hívandó (a
46
+ * bootstrap köti be). Tár-particionált: minden tárra külön pool. A `completed && !_deleted` entry-k
47
+ * `contentVector`-jét tölti be (`key = entry._id`). Hiba esetén `FAM-VEC-HYDRATE-001` (nem néma),
48
+ * de a többi tár hidratálása FOLYTATÓDIK (egy tár bukása nem dönti le az egészet).
49
+ */
50
+ async hydrateAll() {
51
+ for (const entry of fam_store_registry_const_1.FAM_STORE_REGISTRY) {
52
+ try {
53
+ await this.hydrateTable(entry.table);
54
+ }
55
+ catch (error) {
56
+ // No-silent-failure: a hidratálás-hiba persistált (a global handler kapja), de a többi
57
+ // tár hidratálása folytatódik (a `hydrateTable` dob, itt logoljuk + tovább).
58
+ fsm_dynamo_1.DyFM_Log.warn(`[FAM hydrate] '${entry.table}' tár hidratálása sikertelen: ${error?.message}`);
59
+ }
60
+ }
61
+ }
62
+ /**
63
+ * Egy tár pool-jának hidratálása (dsgn-006 §4.1). A `completed`-státuszú, nem-üres-vektorú entry-ket
64
+ * tölti be; az `error`-státuszúak KIMARADNAK (nincs használható vektor — acceptance, dsgn-006 §3.1).
65
+ * A pool dimenzióját az első felvett vektor rögzíti; eltérő dimenziójú vektor → kihagyva + warn.
66
+ */
67
+ async hydrateTable(table) {
68
+ const registryEntry = fam_store_registry_const_1.FAM_StoreRegistry_Util.getEntry(table);
69
+ if (!registryEntry) {
70
+ return;
71
+ }
72
+ const pool = this.getOrCreatePool(table);
73
+ pool.clearPool();
74
+ this.poolDims.set(table, null);
75
+ const dataService = new fam_entry_data_service_1.FAM_Entry_DataService({ dataParams: registryEntry.dataParams, issuer: this.issuer });
76
+ const filter = { embeddingStatus: 'completed' };
77
+ let entries;
78
+ try {
79
+ entries = await dataService.findHydratableList(filter);
80
+ }
81
+ catch (error) {
82
+ throw fam_error_factory_util_1.FAM_Error_Util.create({
83
+ errorCode: error_codes_const_1.FAM_ERROR_CODES.vecHydrate,
84
+ message: `A(z) '${table}' tár vektor-hidratálása sikertelen a Mongo-olvasásnál.`,
85
+ issuer: this.issuer,
86
+ cause: error,
87
+ context: { operation: 'hydrate', table: table },
88
+ });
89
+ }
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
+ this.hydrated.set(table, true);
101
+ fsm_dynamo_1.DyFM_Log.log(`[FAM hydrate] '${table}' tár: ${loaded} vektor betöltve az in-memory pool-ba.`);
102
+ }
103
+ // =========================================================================
104
+ // PERSIST-ON-WRITE upsert (dsgn-006 §3) — a write/scan path hívja mentés után
105
+ // =========================================================================
106
+ /**
107
+ * Egy entry vektorának pool-ba illesztése mentés UTÁN (dsgn-006 §3). A persistence (Mongo
108
+ * `contentVector`) a write-path felelőssége; ITT a már-persistált vektor az in-memory pool-ba kerül
109
+ * (`addVector` = upsert; a pool felülírja a meglévőt azonos kulcson). Dimenzió-eltérés → kihagyva + warn.
110
+ */
111
+ upsertVector(set) {
112
+ if (!set.vector.length) {
113
+ return;
114
+ }
115
+ const pool = this.getOrCreatePool(set.table);
116
+ if (this.acceptVectorForPool(set.table, set.vector)) {
117
+ pool.addVector(set.id, set.vector);
118
+ }
119
+ }
120
+ /** Egy entry vektorának eltávolítása a pool-ból (soft-delete / error-státusz után). */
121
+ removeVector(table, id) {
122
+ this.getOrCreatePool(table).removeVector(id);
123
+ }
124
+ // =========================================================================
125
+ // SEARCH (dsgn-006 §4.2/4.3) — koszinusz a (szűkített) pool-on
126
+ // =========================================================================
127
+ /**
128
+ * Vektor-keresés egy tár pool-jában (dsgn-006 §4.2). MVP1: `cosineSimilarity` (l2-normalizált
129
+ * vektorokon). A `queryVector` VAGY a `queryText` (ez esetben a provider-agnosztikus
130
+ * `FAM_Embedding_ControlService` embeddeli a `table`-re feloldva). A `candidateIds` (prefilter→vektor,
131
+ * dsgn-006 §4.3) szűkíti a jelölt-halmazt MIELŐTT a koszinusz fut — kisebb számítás. A score VALÓDI
132
+ * koszinusz (BFR-AM-007). Üres pool / hiányzó embedding → `FAM-VEC-SEARCH-001` (bootstrap-skip jelzés).
133
+ */
134
+ async search(input) {
135
+ const queryVector = await this.resolveQueryVector(input);
136
+ if (!queryVector.length) {
137
+ throw fam_error_factory_util_1.FAM_Error_Util.create({
138
+ errorCode: error_codes_const_1.FAM_ERROR_CODES.vecSearch,
139
+ message: `Üres query-vektor a(z) '${input.table}' tár keresésénél `
140
+ + `(sem queryVector, sem embeddelhető queryText).`,
141
+ issuer: this.issuer,
142
+ context: { operation: 'search', table: input.table, query: input.queryText },
143
+ });
144
+ }
145
+ const pool = this.getOrCreatePool(input.table);
146
+ const poolSize = pool.getAll().size;
147
+ if (!poolSize) {
148
+ // Bootstrap-skip jelzés (dsgn-006 §4.1): üres pool (embedding nincs konfigurálva / nincs
149
+ // completed entry). NEM néma — a capability + UI piros banner ezt jelzi (dsgn-008/dsgn-011).
150
+ throw fam_error_factory_util_1.FAM_Error_Util.create({
151
+ errorCode: error_codes_const_1.FAM_ERROR_CODES.vecSearch,
152
+ message: `A(z) '${input.table}' tár vektor-pool-ja ÜRES (bootstrap-skip): nincs hidratált, `
153
+ + `completed-embeddingű vektor. Ellenőrizd az embedding-konfigurációt (test_embedding_provider).`,
154
+ issuer: this.issuer,
155
+ context: { operation: 'search', table: input.table },
156
+ });
157
+ }
158
+ // Prefilter→vektor (dsgn-006 §4.3): ha van candidate-szűkítés, a koszinusz CSAK a szűkített
159
+ // halmazon fut (a teljes pool helyett egy ideiglenes szűkített pool-on) — kisebb számítás.
160
+ if (input.candidateIds && input.candidateIds.length) {
161
+ return this.searchInCandidates(pool, queryVector, input.candidateIds, input.topK);
162
+ }
163
+ // A Dynamo LVS pool `search` per-hit VALÓDI score-t ad (cosine; BFR-AM-007 a maga-számolt score).
164
+ const results = pool.search(queryVector, input.topK, local_vector_search_1.LVS_Search_Mode.cosineSimilarity);
165
+ return results.map((hit) => ({ id: hit.id, score: hit.score }));
166
+ }
167
+ /** Egy tár pool-jának mérete (a `test_vector_search` / `get_storage_usage` capability — dsgn-003). */
168
+ getPoolSize(table) {
169
+ return this.getOrCreatePool(table).getAll().size;
170
+ }
171
+ /** Hidratált-e már egy tár (a bootstrap-skip diagnosztikához). */
172
+ isHydrated(table) {
173
+ return this.hydrated.get(table) ?? false;
174
+ }
175
+ // =========================================================================
176
+ // helpers
177
+ // =========================================================================
178
+ /**
179
+ * A koszinusz egy SZŰKÍTETT jelölt-halmazon (prefilter→vektor, dsgn-006 §4.3). Egy ideiglenes
180
+ * pool-ba másoljuk a candidate-ID-k vektorait, és azon futtatjuk a koszinuszt — így a számítás a
181
+ * szűkített halmazra korlátozódik (a jelöltszám bizonyíthatóan kisebb a teljes tárnál — acceptance #4).
182
+ */
183
+ 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);
198
+ return results.map((hit) => ({ id: hit.id, score: hit.score }));
199
+ }
200
+ /** A query-vektor feloldása: kész `queryVector`, vagy a `queryText` embeddelése (provider-agnosztikus). */
201
+ async resolveQueryVector(input) {
202
+ if (input.queryVector && input.queryVector.length) {
203
+ return input.queryVector;
204
+ }
205
+ if (input.queryText && input.queryText.trim().length) {
206
+ return fam_embedding_control_service_1.FAM_Embedding_ControlService.getInstance().embedOne({
207
+ text: input.queryText,
208
+ table: input.table,
209
+ callType: 'embed-query',
210
+ issuer: this.issuer,
211
+ });
212
+ }
213
+ return [];
214
+ }
215
+ /** A tár pool-ja (lazy létrehozás). */
216
+ getOrCreatePool(table) {
217
+ let pool = this.pools.get(table);
218
+ if (!pool) {
219
+ pool = new local_vector_search_1.LVS_VectorPool_ControlService();
220
+ this.pools.set(table, pool);
221
+ this.poolDims.set(table, null);
222
+ }
223
+ return pool;
224
+ }
225
+ /**
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).
229
+ */
230
+ acceptVectorForPool(table, vector) {
231
+ const current = this.poolDims.get(table);
232
+ if (current === null || current === undefined) {
233
+ this.poolDims.set(table, vector.length);
234
+ return true;
235
+ }
236
+ if (current !== vector.length) {
237
+ fsm_dynamo_1.DyFM_Log.warn(`[FAM vector-pool] '${table}' tár dimenzió-eltérés: pool=${current}, vektor=${vector.length} `
238
+ + `→ kihagyva (re-embed szükséges; dsgn-006 §6.2).`);
239
+ return false;
240
+ }
241
+ return true;
242
+ }
243
+ }
244
+ exports.FAM_VectorSearch_ControlService = FAM_VectorSearch_ControlService;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ /**
3
+ * `embedding` modul barrel (MP-2, dsgn-006) — az embedding + Memory Vector Search engine publikus
4
+ * felülete. A fogyasztó-MP-k (MP-4 scan/auto-embed, MP-5 read/retrieval, MP-6 capabilities) innen
5
+ * importálnak. A bootstrap (`App.postProcess`) a `FAM_EmbeddingBootstrap_ControlService`-t hívja.
6
+ *
7
+ * Boundary (dsgn-006 §1): a provider-impl-ek (LM Studio HTTP / mock) projekt-lokális workaround-ok
8
+ * (BFR-AM-002/008); a bedrock-csere (MP-15) a `FAM_EmbeddingProvider` interfész mögött non-breaking.
9
+ */
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;
12
+ // SP-2.1 — provider-dispatch kapu + providerek
13
+ var fam_embedding_control_service_1 = require("./_services/fam-embedding.control-service");
14
+ Object.defineProperty(exports, "FAM_Embedding_ControlService", { enumerable: true, get: function () { return fam_embedding_control_service_1.FAM_Embedding_ControlService; } });
15
+ var fam_openai_embedding_provider_1 = require("./_services/fam-openai-embedding.provider");
16
+ Object.defineProperty(exports, "FAM_OpenAI_EmbeddingProvider", { enumerable: true, get: function () { return fam_openai_embedding_provider_1.FAM_OpenAI_EmbeddingProvider; } });
17
+ var fam_lmstudio_embedding_provider_1 = require("./_services/fam-lmstudio-embedding.provider");
18
+ Object.defineProperty(exports, "FAM_LMStudio_EmbeddingProvider", { enumerable: true, get: function () { return fam_lmstudio_embedding_provider_1.FAM_LMStudio_EmbeddingProvider; } });
19
+ var fam_mock_embedding_provider_1 = require("./_services/fam-mock-embedding.provider");
20
+ Object.defineProperty(exports, "FAM_Mock_EmbeddingProvider", { enumerable: true, get: function () { return fam_mock_embedding_provider_1.FAM_Mock_EmbeddingProvider; } });
21
+ Object.defineProperty(exports, "FAM_MOCK_EMBEDDING_DIMS", { enumerable: true, get: function () { return fam_mock_embedding_provider_1.FAM_MOCK_EMBEDDING_DIMS; } });
22
+ // SP-2.2 — Memory Vector Search (Mongo-persist + LVS hidratálás)
23
+ var fam_vector_search_control_service_1 = require("./_services/fam-vector-search.control-service");
24
+ Object.defineProperty(exports, "FAM_VectorSearch_ControlService", { enumerable: true, get: function () { return fam_vector_search_control_service_1.FAM_VectorSearch_ControlService; } });
25
+ var fam_entry_data_service_1 = require("./_services/fam-entry.data-service");
26
+ Object.defineProperty(exports, "FAM_Entry_DataService", { enumerable: true, get: function () { return fam_entry_data_service_1.FAM_Entry_DataService; } });
27
+ var fam_store_registry_const_1 = require("./_collections/fam-store-registry.const");
28
+ Object.defineProperty(exports, "FAM_STORE_REGISTRY", { enumerable: true, get: function () { return fam_store_registry_const_1.FAM_STORE_REGISTRY; } });
29
+ Object.defineProperty(exports, "FAM_StoreRegistry_Util", { enumerable: true, get: function () { return fam_store_registry_const_1.FAM_StoreRegistry_Util; } });
30
+ // SP-2.3 — presetek + per-tár/scope override
31
+ var fam_embedding_preset_control_service_1 = require("./_services/fam-embedding-preset.control-service");
32
+ Object.defineProperty(exports, "FAM_EmbeddingPreset_ControlService", { enumerable: true, get: function () { return fam_embedding_preset_control_service_1.FAM_EmbeddingPreset_ControlService; } });
33
+ // SP-2.4 — re-embed + auto-embed pipeline + cost-cap
34
+ var fam_embedding_pipeline_control_service_1 = require("./_services/fam-embedding-pipeline.control-service");
35
+ Object.defineProperty(exports, "FAM_EmbeddingPipeline_ControlService", { enumerable: true, get: function () { return fam_embedding_pipeline_control_service_1.FAM_EmbeddingPipeline_ControlService; } });
36
+ var fam_embedding_cost_control_service_1 = require("./_services/fam-embedding-cost.control-service");
37
+ Object.defineProperty(exports, "FAM_EmbeddingCost_ControlService", { enumerable: true, get: function () { return fam_embedding_cost_control_service_1.FAM_EmbeddingCost_ControlService; } });
38
+ // boot-hook (App.postProcess)
39
+ var fam_embedding_bootstrap_control_service_1 = require("./_services/fam-embedding-bootstrap.control-service");
40
+ Object.defineProperty(exports, "FAM_EmbeddingBootstrap_ControlService", { enumerable: true, get: function () { return fam_embedding_bootstrap_control_service_1.FAM_EmbeddingBootstrap_ControlService; } });
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FAM_ContentHash_Util = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const crypto = tslib_1.__importStar(require("crypto"));
6
+ /**
7
+ * `FAM_ContentHash_Util` (SP-4.3, dsgn-004 §4.1) — a delta-detection tartalom-ujjlenyomata. A
8
+ * `contentHash = SHA-256(normalizált content)`, ahol a normalizálás **trim + sor-vég-egységesítés**
9
+ * (`\r\n` → `\n`), hogy a whitespace-/sor-vég-zaj NE triggereljen fals `modified`-et (dsgn-004
10
+ * §8.6). A hash determinisztikus — ugyanaz a (normalizált) tartalom mindig ugyanazt a hash-t adja.
11
+ *
12
+ * **FIGYELEM — a normalizálás SZŰKEBB, mint az embedding-pipeline `sha256`-é** (ami `\s+ → ' '`
13
+ * összevonást is csinál a vektor-content-kompozícióhoz): a delta-detection a forrás-tartalom
14
+ * SOR-szerkezetét megőrzi (a `\r\n→\n` + trim elég a platform-/editor-zaj kiszűrésére, de a
15
+ * kódstruktúra-jelentős sortörések megmaradnak). Ez a util a delta-réteg SSOT-ja; a vektor-hash
16
+ * (`FAM_EmbeddingPipeline_ControlService.sha256`) attól FÜGGETLEN célt szolgál.
17
+ */
18
+ class FAM_ContentHash_Util {
19
+ /**
20
+ * A normalizált tartalom SHA-256 ujjlenyomata hex-stringként (dsgn-004 §4.1). Determinisztikus;
21
+ * a `FAM_Entry.contentHash` mezőbe kerül. A normalizálás a `normalize`-on át (trim + `\r\n→\n`).
22
+ */
23
+ static hash(content) {
24
+ return crypto.createHash('sha256').update(FAM_ContentHash_Util.normalize(content)).digest('hex');
25
+ }
26
+ /**
27
+ * A delta-detection normalizálása (dsgn-004 §4.1): a sor-végeket egységesíti (`\r\n` és magányos
28
+ * `\r` → `\n`), majd trim-eli a teljes szöveget. A belső sortörések + indentáció MEGMARADNAK
29
+ * (csak a platform-/editor-zaj tűnik el), így a tartalmi változás detektálható marad.
30
+ */
31
+ static normalize(content) {
32
+ return content.replace(/\r\n/g, '\n').replace(/\r/g, '\n').trim();
33
+ }
34
+ }
35
+ exports.FAM_ContentHash_Util = FAM_ContentHash_Util;
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FAM_FileRouting_Util = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const path = tslib_1.__importStar(require("path"));
6
+ const fam_table_type_enum_1 = require("../../../_enums/fam-table.type-enum");
7
+ /**
8
+ * `FAM_FileRouting_Util` (SP-4.1, dsgn-004 §2) — a file-típus-routing (intelligens szelekció): egy
9
+ * felderített fájlt a kiterjesztése / fájlnév-mintája alapján `documents` / `codebase` / `skip`
10
+ * cél-tárra route-ol. Statikus util (nincs DB-/service-függés).
11
+ *
12
+ * Routing-térkép (dsgn-004 §2):
13
+ * - `documents` ← `.md` `.mdx` `.txt` `.rst`
14
+ * - `codebase` ← `.html` `.ts` `.tsx` `.js` `.jsx` `.py` `.css` `.scss` `.json` + config-fájlok
15
+ * (`*.config.*`, `tsconfig*.json`, `package.json`, `.eslintrc*`, `.prettierrc*`,
16
+ * `*.yml`/`*.yaml`/`*.toml`/`*.ini`, `Dockerfile`)
17
+ * - `skip` ← minden egyéb (bináris, kép, lock-fájl, stb.) — verdikt nélkül, `filesSkipped`-ben.
18
+ *
19
+ * **A `.html` markup KIZÁRÓLAG `codebase`** (NEM `documents`) — a `documents` route a tartalmi
20
+ * dokumentációt célozza (`.md`/`.mdx`/`.txt`/`.rst`), nem a markup-ot (dsgn-004 §2). A
21
+ * `chunkStrategyFor` a route + kiterjesztés alapján adja a chunker-stratégiát (SP-4.2 dispatch).
22
+ */
23
+ class FAM_FileRouting_Util {
24
+ /** Dokumentum-kiterjesztések → `documents` (dsgn-004 §2). */
25
+ static DOCUMENT_EXTENSIONS = ['.md', '.mdx', '.txt', '.rst'];
26
+ /** Kód-/markup-kiterjesztések → `codebase` (a `.html` markup IDE tartozik, dsgn-004 §2). */
27
+ static CODE_EXTENSIONS = [
28
+ '.html', '.ts', '.tsx', '.mts', '.js', '.jsx', '.py', '.css', '.scss', '.json',
29
+ '.yml', '.yaml', '.toml', '.ini',
30
+ ];
31
+ /** Markdown-kiterjesztések (a header-aware chunker célja, dsgn-004 §3.2). */
32
+ static MARKDOWN_EXTENSIONS = ['.md', '.mdx'];
33
+ /** TS-szemantikus chunker kiterjesztései (dsgn-004 §3.1). */
34
+ static TS_EXTENSIONS = ['.ts', '.tsx', '.mts', '.js', '.jsx'];
35
+ /** Extension-nélküli config-fájlnevek → `codebase` (dsgn-004 §2). */
36
+ static CONFIG_FILENAMES = ['dockerfile'];
37
+ /**
38
+ * Egy fájl route-verdiktje (dsgn-004 §2). A kiterjesztés-térkép, majd a config-fájlnév-minták
39
+ * (extension-nélküli `Dockerfile`, `*.config.*`, `tsconfig*.json`, `.eslintrc*`, `.prettierrc*`)
40
+ * → `codebase`; egyébként `skip`. A vizsgálat case-insensitive a kiterjesztésre/fájlnévre.
41
+ */
42
+ static route(relativePath) {
43
+ const fileName = path.basename(relativePath).toLowerCase();
44
+ const extension = path.extname(fileName);
45
+ if (FAM_FileRouting_Util.DOCUMENT_EXTENSIONS.includes(extension)) {
46
+ return fam_table_type_enum_1.FAM_Table.documents;
47
+ }
48
+ if (FAM_FileRouting_Util.CODE_EXTENSIONS.includes(extension)) {
49
+ return fam_table_type_enum_1.FAM_Table.codebase;
50
+ }
51
+ if (FAM_FileRouting_Util.isConfigFile(fileName)) {
52
+ return fam_table_type_enum_1.FAM_Table.codebase;
53
+ }
54
+ return 'skip';
55
+ }
56
+ /**
57
+ * Config-fájl-e a fájlnév (dsgn-004 §2): extension-nélküli ismert config (`Dockerfile`) VAGY
58
+ * a `*.config.*` / `tsconfig*.json` / `package.json` / `.eslintrc*` / `.prettierrc*` minta.
59
+ */
60
+ static isConfigFile(fileName) {
61
+ if (FAM_FileRouting_Util.CONFIG_FILENAMES.includes(fileName)) {
62
+ return true;
63
+ }
64
+ if (fileName === 'package.json' || fileName === 'package-lock.json') {
65
+ return true;
66
+ }
67
+ if (/^tsconfig.*\.json$/.test(fileName)) {
68
+ return true;
69
+ }
70
+ if (/\.config\./.test(fileName)) {
71
+ return true;
72
+ }
73
+ if (/^\.eslintrc/.test(fileName) || /^\.prettierrc/.test(fileName)) {
74
+ return true;
75
+ }
76
+ return false;
77
+ }
78
+ /**
79
+ * A chunker-stratégia kulcsa egy fájlhoz (SP-4.2 dispatch, dsgn-004 §3). `ts` a TS-szemantikus
80
+ * (.ts/.tsx/.mts/.js/.jsx), `md` a markdown-header-aware (.md/.mdx), `generic` minden más
81
+ * `documents`/`codebase` fájlra (config/JSON/HTML-markup/.txt/.rst). A `route` előfeltétel:
82
+ * `skip` fájlra NEM hívandó.
83
+ */
84
+ static chunkStrategyFor(relativePath) {
85
+ const extension = path.extname(path.basename(relativePath).toLowerCase());
86
+ if (FAM_FileRouting_Util.TS_EXTENSIONS.includes(extension)) {
87
+ return 'ts';
88
+ }
89
+ if (FAM_FileRouting_Util.MARKDOWN_EXTENSIONS.includes(extension)) {
90
+ return 'md';
91
+ }
92
+ return 'generic';
93
+ }
94
+ }
95
+ exports.FAM_FileRouting_Util = FAM_FileRouting_Util;
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FAM_GlobMatch_Util = void 0;
4
+ /**
5
+ * `FAM_GlobMatch_Util` (SP-4.1) — minimál glob→regex matcher a scan ignore-/include-/exclude
6
+ * mintáihoz (dsgn-004 §6.2, dsgn-003 §3.1). Nincs külső glob-dependency (a `scan.ignorePatterns`
7
+ * a `**` / `*` / `?` szintaxist használja, dsgn-007 §4.5); a Node `fs`-bejárás minden útvonalra
8
+ * ezt a matchert hívja.
9
+ *
10
+ * **Boundary:** ez NEM teljes-értékű glob-engine (nincs `{a,b}` brace-expansion, nincs `[..]`
11
+ * char-class) — a scan-minták egyszerűek (`**​/node_modules/**`, `*.min.js`, `**​/.env.*`). A
12
+ * komplexebb minta-szintaxis (ha kell) BACKLOG / bedrock-igény. A matcher mind a teljes
13
+ * (projekt-relatív) útvonalra, mind a fájlnévre próbál — így a `*.min.js` (nincs `/`) az útvonal
14
+ * BÁRMELY szegmensére illeszkedik.
15
+ */
16
+ class FAM_GlobMatch_Util {
17
+ /**
18
+ * Illeszkedik-e a (projekt-relatív, `/`-normalizált) útvonal a glob-mintára. A `**` bármennyi
19
+ * szegmens (a `/`-okat is átfedve), a `*` egy szegmensen belül bármennyi nem-`/` karakter, a
20
+ * `?` egyetlen nem-`/` karakter. A `/`-ot NEM tartalmazó minta (`*.min.js`) a fájlnévre (az
21
+ * útvonal utolsó szegmensére) is illeszkedhet.
22
+ */
23
+ static matches(relativePath, pattern) {
24
+ const normalizedPath = FAM_GlobMatch_Util.normalizeSlashes(relativePath);
25
+ const regex = FAM_GlobMatch_Util.toRegex(pattern);
26
+ if (regex.test(normalizedPath)) {
27
+ return true;
28
+ }
29
+ // A `/`-ot nem tartalmazó minta (pl. `*.min.js`) a fájlnévre is illeszkedhet.
30
+ if (!pattern.includes('/')) {
31
+ const fileName = normalizedPath.split('/').pop() ?? normalizedPath;
32
+ return FAM_GlobMatch_Util.toRegex(pattern).test(fileName);
33
+ }
34
+ return false;
35
+ }
36
+ /** Illeszkedik-e az útvonal a minták BÁRMELYIKÉRE (üres lista → `false`). */
37
+ static matchesAny(relativePath, patterns) {
38
+ return patterns.some((pattern) => FAM_GlobMatch_Util.matches(relativePath, pattern));
39
+ }
40
+ /**
41
+ * Egy glob-minta → `RegExp`. A regex-metakaraktereket escape-eli, majd a glob-wildcardokat
42
+ * (`**` / `*` / `?`) regex-fragmentekre cseréli. A `**` a `/`-okat is átfedi (`.*`); a `*` egy
43
+ * szegmensen belül marad (`[^/]*`); a `?` egyetlen nem-`/` karakter (`[^/]`). Teljes-string
44
+ * anchor (`^...$`).
45
+ */
46
+ static toRegex(pattern) {
47
+ const normalized = FAM_GlobMatch_Util.normalizeSlashes(pattern);
48
+ let regexSource = '';
49
+ for (let i = 0; i < normalized.length; i++) {
50
+ const char = normalized[i];
51
+ if (char === '*') {
52
+ if (normalized[i + 1] === '*') {
53
+ // `**` (esetleg záró `/`-val) → bármennyi szegmens (a `/`-okat átfedve).
54
+ i++;
55
+ if (normalized[i + 1] === '/') {
56
+ i++;
57
+ }
58
+ regexSource += '.*';
59
+ }
60
+ else {
61
+ // `*` → egy szegmensen belül bármennyi nem-`/` karakter.
62
+ regexSource += '[^/]*';
63
+ }
64
+ continue;
65
+ }
66
+ if (char === '?') {
67
+ regexSource += '[^/]';
68
+ continue;
69
+ }
70
+ // Regex-metakarakterek escape-elése (a `.` / `+` / `(` stb. literál legyen).
71
+ regexSource += FAM_GlobMatch_Util.escapeRegexChar(char);
72
+ }
73
+ return new RegExp(`^${regexSource}$`);
74
+ }
75
+ /** Egyetlen karakter regex-escape-je (a glob-literál szegmensekhez). */
76
+ static escapeRegexChar(char) {
77
+ return /[.*+?^${}()|[\]\\]/.test(char) ? `\\${char}` : char;
78
+ }
79
+ /** A backslash-eket előre-perjelre normalizálja (Windows-útvonalak; a glob mindig `/`-os). */
80
+ static normalizeSlashes(value) {
81
+ return value.replace(/\\/g, '/');
82
+ }
83
+ }
84
+ exports.FAM_GlobMatch_Util = FAM_GlobMatch_Util;
@@ -0,0 +1,164 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FAM_MdChunker_Util = void 0;
4
+ /**
5
+ * `FAM_MdChunker_Util` (SP-4.2, dsgn-004 §3.2) — markdown-header-tudatos chunker `.md/.mdx`-re.
6
+ *
7
+ * **REUSE-DÖNTÉS (dsgn-004 §3.2 + feedback_read_actual_reference_code):** a Dynamo
8
+ * `DyNTS_DAI_DocChunking_Util.chunkMdContent()` a TÉNYLEGES forráskód szerint **NEM** plain
9
+ * `(content: string) => chunks` API — ClickUp-domain-objektumot követel (`DyNTS_DAI_DocPage` a
10
+ * `documentId`/`externalPageId`/`parentedPath`/`allFlaggedParents`-szel), `getPageLink` callback-et,
11
+ * `DyNTS_DAI_DocChunk` Mongo-DataModel-eket ad vissza (`pageLink`/`pageId`-vel), `CCAP-CUC-*`
12
+ * hibát dob ha `documentId`/`_id` hiányzik, és a méretet a ClickUp `DyNTS_global_settings.docChunking`-ből
13
+ * veszi (NEM a FAM `chunk.md.*` config-ból). Ezért NEM közvetlenül használható egy lokális fájl-scanre.
14
+ * A header-tudatos ALGORITMUS (heading-hierarchia + break-pointok) viszont kiváló — ezt a CCAP
15
+ * `PS_MarkdownChunker_Util` projekt-lokális portjával reprodukáljuk (a CCAP maga is ezt az
16
+ * algoritmust képezi le a Dynamo break-point-mintából). Méret a config-ból (`chunk.md.*`),
17
+ * default `1200/150/80`.
18
+ *
19
+ * - Minden szakasz `chunkType = 'heading-section'`.
20
+ * - A `headingPath` a teljes heading-LÁNCOT őrzi (`['# H1','## H2','### H3']`) — kontextus a
21
+ * retrieval-höz (dsgn-005) és a megjelenítéshez.
22
+ * - Heading nélküli tartalom → egyetlen szekció üres `headingPath`-szal (a sliding-window a
23
+ * nagy-szekció ágon, dsgn-004 §3.2).
24
+ */
25
+ class FAM_MdChunker_Util {
26
+ static HEADING_REGEX = /^(#{1,6})\s+(.+)$/;
27
+ /**
28
+ * Egy markdown forrás chunkolása heading-hierarchia mentén (dsgn-004 §3.2). Üres tartalom →
29
+ * üres lista (a dispatcher tiny-fallback-je veszi át). A `maxSize` feletti szekció a
30
+ * heading-prefixet megőrző sliding-window-ra bomlik.
31
+ */
32
+ static chunkText(text, sizing) {
33
+ if (!text || !text.trim()) {
34
+ return [];
35
+ }
36
+ const sections = FAM_MdChunker_Util.splitIntoSections(text);
37
+ const chunks = [];
38
+ for (const section of sections) {
39
+ const headingPrefix = section.headingPath.length ? section.headingPath.join('\n') + '\n\n' : '';
40
+ const sectionContent = headingPrefix + section.content;
41
+ if (sectionContent.length <= sizing.maxSize) {
42
+ if (sectionContent.trim().length >= sizing.minSize) {
43
+ chunks.push({
44
+ content: sectionContent.trim(),
45
+ position: {
46
+ charStart: section.charStart,
47
+ charEnd: section.charEnd,
48
+ lineStart: section.lineStart,
49
+ lineEnd: section.lineEnd,
50
+ },
51
+ chunkType: 'heading-section',
52
+ headingPath: section.headingPath,
53
+ });
54
+ }
55
+ continue;
56
+ }
57
+ chunks.push(...FAM_MdChunker_Util.slidingWithinSection(section, headingPrefix, sizing));
58
+ }
59
+ return chunks;
60
+ }
61
+ /** A forrás szekciókra bontása heading-ek mentén (a heading-stack tartja a hierarchiát). */
62
+ static splitIntoSections(text) {
63
+ const lines = text.split(/\r?\n/);
64
+ const sections = [];
65
+ const headingStack = [];
66
+ let currentContent = '';
67
+ let currentCharStart = 0;
68
+ let currentLineStart = 1;
69
+ let charCursor = 0;
70
+ for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
71
+ const line = lines[lineIdx];
72
+ const headingMatch = line.match(FAM_MdChunker_Util.HEADING_REGEX);
73
+ if (headingMatch) {
74
+ if (currentContent.trim()) {
75
+ sections.push({
76
+ headingPath: FAM_MdChunker_Util.snapshotStack(headingStack),
77
+ content: currentContent.trim(),
78
+ charStart: currentCharStart,
79
+ charEnd: charCursor,
80
+ lineStart: currentLineStart,
81
+ lineEnd: lineIdx,
82
+ });
83
+ }
84
+ const level = headingMatch[1].length;
85
+ const headingText = headingMatch[2].trim();
86
+ while (headingStack.length && headingStack[headingStack.length - 1].level >= level) {
87
+ headingStack.pop();
88
+ }
89
+ headingStack.push({ level: level, text: headingText });
90
+ currentContent = '';
91
+ currentCharStart = charCursor;
92
+ currentLineStart = lineIdx + 1;
93
+ }
94
+ else {
95
+ currentContent += (currentContent ? '\n' : '') + line;
96
+ }
97
+ charCursor += line.length + 1;
98
+ }
99
+ if (currentContent.trim()) {
100
+ sections.push({
101
+ headingPath: FAM_MdChunker_Util.snapshotStack(headingStack),
102
+ content: currentContent.trim(),
103
+ charStart: currentCharStart,
104
+ charEnd: charCursor,
105
+ lineStart: currentLineStart,
106
+ lineEnd: lines.length,
107
+ });
108
+ }
109
+ // Nincs heading: az egész szöveg egy szekció (üres heading-lánc).
110
+ if (!sections.length && text.trim()) {
111
+ sections.push({
112
+ headingPath: [],
113
+ content: text.trim(),
114
+ charStart: 0,
115
+ charEnd: text.length,
116
+ lineStart: 1,
117
+ lineEnd: lines.length,
118
+ });
119
+ }
120
+ return sections;
121
+ }
122
+ /** A heading-stack pillanatfelvétele teljes heading-láncként (`['# H1','## H2']`). */
123
+ static snapshotStack(stack) {
124
+ return stack.map((heading) => '#'.repeat(heading.level) + ' ' + heading.text);
125
+ }
126
+ /** Nagy szekció sliding-window-ja a heading-prefixet megőrizve (sorvégre igazított break). */
127
+ static slidingWithinSection(section, headingPrefix, sizing) {
128
+ const chunks = [];
129
+ const available = Math.max(1, sizing.maxSize - headingPrefix.length);
130
+ const body = section.content;
131
+ let offset = 0;
132
+ while (offset < body.length) {
133
+ const end = Math.min(offset + available, body.length);
134
+ let segment = body.slice(offset, end);
135
+ // Sorvégnél törünk (ne szó közepén), ha nem a szekció vége.
136
+ if (end < body.length) {
137
+ const lastNewline = segment.lastIndexOf('\n');
138
+ if (lastNewline > available * 0.3) {
139
+ segment = segment.slice(0, lastNewline);
140
+ }
141
+ }
142
+ const fullText = (headingPrefix + segment.trim()).trim();
143
+ if (fullText.length >= sizing.minSize) {
144
+ chunks.push({
145
+ content: fullText,
146
+ position: {
147
+ charStart: section.charStart + offset,
148
+ charEnd: section.charStart + offset + segment.length,
149
+ lineStart: section.lineStart,
150
+ lineEnd: section.lineEnd,
151
+ },
152
+ chunkType: 'heading-section',
153
+ headingPath: section.headingPath,
154
+ });
155
+ }
156
+ if (offset + segment.length >= body.length) {
157
+ break;
158
+ }
159
+ offset += Math.max(1, segment.length - sizing.overlap);
160
+ }
161
+ return chunks;
162
+ }
163
+ }
164
+ exports.FAM_MdChunker_Util = FAM_MdChunker_Util;