@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,1180 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FAM_CapabilityRegistry = void 0;
4
+ const fam_table_type_enum_1 = require("../../../_enums/fam-table.type-enum");
5
+ const error_codes_const_1 = require("../../../_collections/error-codes.const");
6
+ const fam_error_factory_util_1 = require("../../../_collections/fam-error-factory.util");
7
+ const config_control_service_1 = require("../../../_routes/server/config/config.control-service");
8
+ const config_data_service_1 = require("../../../_routes/server/config/config.data-service");
9
+ const errors_control_service_1 = require("../../../_routes/server/errors/errors.control-service");
10
+ const embedding_1 = require("../../embedding");
11
+ const ingest_1 = require("../../ingest");
12
+ const retrieval_1 = require("../../retrieval");
13
+ const migration_1 = require("../../migration");
14
+ const scope_reference_1 = require("../../scope-reference");
15
+ const fam_mcp_interface_1 = require("../_models/interfaces/fam-mcp.interface");
16
+ /** A főtár-nevek a `list_tables` / `get_table_stats` capability-khez. */
17
+ const MAIN_TABLES = embedding_1.FAM_STORE_REGISTRY.map((entry) => entry.table);
18
+ /**
19
+ * `FAM_CapabilityRegistry` (SP-6.4, dsgn-003 §4.2) — a **discoverable speciális eszközök** belső
20
+ * registry-je. Singleton. A 6 kategória (config-diagnostics / high-level-stats / inspection /
21
+ * maintenance / import-migration / debug) minden eszköze ITT él (név + kategória + leírás + schema +
22
+ * handler), és **kizárólag** a `capabilities` tool-on át érhető el (`list`/`describe`/`invoke`).
23
+ *
24
+ * **Diszjunkció (dsgn-003 §6.6):** ezek az eszközök SOHA nem kerülnek a SDK advertised tool-listájába
25
+ * (a `read`/`write`/`capabilities` az egyetlen hirdetett halmaz). A registry a katalógus SINGLE
26
+ * SOURCE-a — a `list` ebből generál, a contract-teszt a dsgn-003 §4.2 nevekkel veti össze.
27
+ *
28
+ * **Delegálás (nem duplikálás):** minden handler a megfelelő upstream engine-re delegál (config: MP-7;
29
+ * stats: MP-1/MP-2/MP-3; inspection: MP-1/MP-3/MP-4/MP-8; maintenance: MP-2/MP-3; debug: MP-3).
30
+ * Az import/migration + néhány még-nem-épült engine handler **late-bind** (clear "pending MP-X" hiba az
31
+ * `invoke`-on; a `list`/`describe` ettől teljes).
32
+ */
33
+ class FAM_CapabilityRegistry {
34
+ static _instance;
35
+ /** A capability-eszközök `name` → deszkriptor map-je (a single-source katalógus). */
36
+ registry = new Map();
37
+ /** Default issuer (a capability-hibák issuer-éhez; dsgn-008). */
38
+ issuer = 'FAM_CapabilityRegistry';
39
+ constructor() {
40
+ this.registerAll();
41
+ }
42
+ static getInstance() {
43
+ if (!FAM_CapabilityRegistry._instance) {
44
+ FAM_CapabilityRegistry._instance = new FAM_CapabilityRegistry();
45
+ }
46
+ return FAM_CapabilityRegistry._instance;
47
+ }
48
+ // =========================================================================
49
+ // Public registry API (a `capabilities` tool — SP-6.4 — fogyasztja)
50
+ // =========================================================================
51
+ /** Egy deszkriptor a nevéből (vagy undefined, ha ismeretlen). */
52
+ get(name) {
53
+ return this.registry.get(name);
54
+ }
55
+ /** A teljes katalógus (a `list` + a contract-teszt fogyasztja). */
56
+ list(category) {
57
+ const all = Array.from(this.registry.values());
58
+ return category ? all.filter((descriptor) => descriptor.category === category) : all;
59
+ }
60
+ /** A capability-nevek halmaza (a diszjunkció-teszthez: ezek SOHA nem advertised tool-ok). */
61
+ names() {
62
+ return Array.from(this.registry.keys());
63
+ }
64
+ // =========================================================================
65
+ // Katalógus-feltöltés (dsgn-003 §4.2 — 6 kategória)
66
+ // =========================================================================
67
+ /** A teljes dsgn-003 §4.2 katalógus regisztrálása (a registry single-source). */
68
+ registerAll() {
69
+ this.registerConfigDiagnostics();
70
+ this.registerHighLevelStats();
71
+ this.registerInspection();
72
+ this.registerMaintenance();
73
+ this.registerImportMigration();
74
+ this.registerDebug();
75
+ }
76
+ /** config / diagnostics → MP-7 (config) + MP-2 (embedding) + MP-0 (env/mongo). */
77
+ registerConfigDiagnostics() {
78
+ const category = fam_mcp_interface_1.FAM_CapabilityCategory.configDiagnostics;
79
+ this.register({
80
+ name: 'get_config_status', category: category,
81
+ description: 'A teljes effektív config egy adott kontextusra (table/scope), a precedencia-forrással '
82
+ + '(scope>table>global>builtin).',
83
+ inputSchema: this.objectSchema({
84
+ table: this.tableSchema(false),
85
+ scopePath: this.scopeLayerArraySchema(),
86
+ }),
87
+ handler: async (args) => {
88
+ const input = this.asObject(args);
89
+ const ctx = await this.toConfigContext(input);
90
+ return this.ok(await config_control_service_1.FAM_Config_ControlService.getInstance().resolveAll(ctx));
91
+ },
92
+ });
93
+ this.register({
94
+ name: 'get_embedding_config', category: category,
95
+ description: 'A feloldott embedding-provider + modell + dimenzió + batch-méret az adott kontextusra.',
96
+ inputSchema: this.objectSchema({ table: this.tableSchema(false) }),
97
+ handler: async (args) => {
98
+ const input = this.asObject(args);
99
+ const probe = await embedding_1.FAM_Embedding_ControlService.getInstance()
100
+ .testProvider({ table: this.optionalTable(input.table) });
101
+ return this.ok(probe.resolved);
102
+ },
103
+ });
104
+ this.register({
105
+ name: 'test_embedding_provider', category: category,
106
+ description: 'A jelenleg feloldott embedding-provider elérhetőség-próbája (available + resolved provider).',
107
+ inputSchema: this.objectSchema({ table: this.tableSchema(false) }),
108
+ handler: async (args) => {
109
+ const input = this.asObject(args);
110
+ return this.ok(await embedding_1.FAM_Embedding_ControlService.getInstance()
111
+ .testProvider({ table: this.optionalTable(input.table) }));
112
+ },
113
+ });
114
+ // validate_environment / test_mongodb_connection / test_vector_search (MP-12) — a doctor-réteg
115
+ // 3 infra-diag-je. A `fam doctor` env/mongodb/vector al-checkjei ezeket invokálják (dsgn-010 §5.2).
116
+ // NEM új diag-engine: az env a `process.env`-et olvassa, a Mongo a Dynamo DataService-en keresztül
117
+ // pingel, a vektor az MP-2 embedding + VectorSearch smoke-ját futtatja (REUSE).
118
+ this.register({
119
+ name: 'validate_environment', category: category,
120
+ description: 'Env-változók (MONGODB_URI / EMBEDDING_PROVIDER / OPENAI_API_KEY / LMSTUDIO_* …) jelenlét- '
121
+ + 'ellenőrzése provider-feltételes kötelezőséggel (doctor `env`, dsgn-012 §7.1).',
122
+ inputSchema: this.objectSchema({}),
123
+ handler: async () => this.ok(this.validateEnvironment()),
124
+ });
125
+ this.register({
126
+ name: 'test_mongodb_connection', category: category,
127
+ description: 'MongoDB-kapcsolat próbája: egy könnyű olvasás a `fam_config` collection-ön (doctor `mongodb`). '
128
+ + 'Sikertelen connect → FAM-DB-CONNECT-001 (nem néma).',
129
+ inputSchema: this.objectSchema({}),
130
+ handler: async () => this.ok(await this.testMongoConnection()),
131
+ });
132
+ this.register({
133
+ name: 'test_vector_search', category: category,
134
+ description: 'A Memory Vector Search pool hidratáltság-/keresés-próbája (doctor `vector`): egy próba-szöveg '
135
+ + 'embed-elése + koszinusz-keresés a tár pool-jában (LVS smoke; dsgn-006 §4).',
136
+ inputSchema: this.objectSchema({ table: this.tableSchema(false) }),
137
+ handler: async (args) => {
138
+ const input = this.asObject(args);
139
+ return this.ok(await this.testVectorSearch(this.optionalTable(input.table)));
140
+ },
141
+ });
142
+ }
143
+ /** high-level stats → MP-1 (count) + MP-3 (scope/reference) + MP-2 (cost). */
144
+ registerHighLevelStats() {
145
+ const category = fam_mcp_interface_1.FAM_CapabilityCategory.highLevelStats;
146
+ this.register({
147
+ name: 'get_table_stats', category: category,
148
+ description: 'Per-tár statisztika: aktív elem-szám, embedding-státusz bontás (pending/completed/error).',
149
+ inputSchema: this.objectSchema({ table: this.tableSchema(false) }),
150
+ handler: async (args) => {
151
+ const input = this.asObject(args);
152
+ return this.ok(await this.tableStats(this.optionalTable(input.table)));
153
+ },
154
+ });
155
+ this.register({
156
+ name: 'get_scope_tree', category: category,
157
+ description: 'A teljes scope-fa (root→leaf hierarchia, layer + canonical-név + parent).',
158
+ inputSchema: this.objectSchema({}),
159
+ handler: async () => {
160
+ const scope_DS = new scope_reference_1.FAM_Scope_DataService({ issuer: this.issuer });
161
+ const all = await scope_DS.findAllActive();
162
+ return this.ok(all.map((scope) => ({
163
+ scopeId: scope._id, layer: scope.layer, canonicalName: scope.canonicalName,
164
+ parentScopeId: scope.parentScopeId, aliases: scope.aliases ?? [],
165
+ })));
166
+ },
167
+ });
168
+ this.register({
169
+ name: 'get_reference_stats', category: category,
170
+ description: 'A reference helper-tár statisztikája: aktív bejegyzés-szám.',
171
+ inputSchema: this.objectSchema({}),
172
+ handler: async () => {
173
+ const reference_DS = new scope_reference_1.FAM_Reference_DataService({ issuer: this.issuer });
174
+ const active = await reference_DS.findActive();
175
+ return this.ok({ activeReferences: active.length });
176
+ },
177
+ });
178
+ this.register({
179
+ name: 'get_embedding_cost_stats', category: category,
180
+ description: 'Az aktuális process-futás embedding-költség aggregátuma hívás-típusonként (BFR-AM-007).',
181
+ inputSchema: this.objectSchema({}),
182
+ handler: async () => this.ok(embedding_1.FAM_EmbeddingCost_ControlService.getInstance().getStats()),
183
+ });
184
+ // get_layer_stats / get_storage_usage (MP-12) — a stats-réteg aggregátum-bővítése. A layer-stats
185
+ // a scope-fát layerenként összesíti (MP-3), a storage-usage a fő + helper collection-ök aktív
186
+ // doc-számát adja (MP-1) — collection-méret-becslés (a Dynamo base nincs natív count, ezért aktív-szám).
187
+ this.register({
188
+ name: 'get_layer_stats', category: category,
189
+ description: 'Layer-szintű scope-aggregátum (layer → scope-szám + root/child bontás). A scope-fát '
190
+ + 'layerenként összesíti (MP-3).',
191
+ inputSchema: this.objectSchema({}),
192
+ handler: async () => this.ok(await this.layerStats()),
193
+ });
194
+ this.register({
195
+ name: 'get_storage_usage', category: category,
196
+ description: 'Mongo storage-használat tárakra bontva: a fő RAG-tárak + a helper collection-ök '
197
+ + '(reference/scope/ingest-run/config) aktív doc-száma (MP-1).',
198
+ inputSchema: this.objectSchema({}),
199
+ handler: async () => this.ok(await this.storageUsage()),
200
+ });
201
+ }
202
+ /** inspection → MP-1 (tables) + MP-3 (scopes) + MP-4 (scan-runs) + MP-8 (errors). */
203
+ registerInspection() {
204
+ const category = fam_mcp_interface_1.FAM_CapabilityCategory.inspection;
205
+ this.register({
206
+ name: 'list_tables', category: category,
207
+ description: 'A 6 fő RAG-tár felsorolása (+ a reference helper jelölése).',
208
+ inputSchema: this.objectSchema({}),
209
+ handler: async () => this.ok({
210
+ mainTables: MAIN_TABLES, helperTables: [fam_table_type_enum_1.FAM_Table.reference],
211
+ }),
212
+ });
213
+ this.register({
214
+ name: 'list_scopes', category: category,
215
+ description: 'A scope-entitások lapozható listája (layer/parent szűréssel).',
216
+ inputSchema: this.objectSchema({
217
+ layer: { type: 'string', description: 'Opcionális layer-szűrés.' },
218
+ parentScopeId: { type: 'string', description: 'Opcionális parent-szűrés.' },
219
+ page: { type: 'number', description: '1-alapú lapszám.' },
220
+ pageSize: { type: 'number', description: 'Lapméret (default 50, cap 500).' },
221
+ }),
222
+ handler: async (args) => {
223
+ const input = this.asObject(args);
224
+ const scope_DS = new scope_reference_1.FAM_Scope_DataService({ issuer: this.issuer });
225
+ return this.ok(await scope_DS.listScopes({
226
+ layer: this.asString(input.layer),
227
+ parentScopeId: this.asString(input.parentScopeId),
228
+ page: this.asNumber(input.page),
229
+ pageSize: this.asNumber(input.pageSize),
230
+ }));
231
+ },
232
+ });
233
+ this.register({
234
+ name: 'list_scan_runs', category: category,
235
+ description: 'Az ingest-run-ok listája (a `FAM_IngestRun` rekordok; idővonalhoz).',
236
+ inputSchema: this.objectSchema({ table: this.tableSchema(false) }),
237
+ handler: async (args) => {
238
+ const input = this.asObject(args);
239
+ const table = this.optionalTable(input.table);
240
+ return this.ok(await this.listScanRuns(table));
241
+ },
242
+ });
243
+ this.register({
244
+ name: 'list_errors', category: category,
245
+ description: 'A persistált FAM_Error rekordok (level/source/handled szűréssel) — dsgn-008.',
246
+ inputSchema: this.objectSchema({
247
+ level: { type: 'string', description: 'Szint-szűrés (error/warning/user/...).' },
248
+ source: { type: 'string', description: 'Forrás-réteg szűrés (pl. FAM-EMB).' },
249
+ handled: { type: 'boolean', description: 'Kezelt-állapot szűrés.' },
250
+ }),
251
+ handler: async (args) => {
252
+ const input = this.asObject(args);
253
+ return this.ok(await errors_control_service_1.FAM_Error_ControlService.getInstance().listErrors({
254
+ level: this.asString(input.level),
255
+ source: this.asString(input.source),
256
+ handled: typeof input.handled === 'boolean' ? input.handled : undefined,
257
+ }));
258
+ },
259
+ });
260
+ // list_kinds_tags / list_recent_writes (MP-12) — a tag/kind-aggregátum + recent-writes. A kind/tag
261
+ // eloszlás az adott tár (vagy minden fő tár) aktív entry-iből; a recent-writes a `__created` szerint
262
+ // csökkenő (legfrissebb felül) — mindkettő MP-1 entry-réteg (REUSE).
263
+ this.register({
264
+ name: 'list_kinds_tags', category: category,
265
+ description: 'Per-tár kind- és tag-vokabulár + eloszlás (distinct kind/tag → előfordulás-szám) az aktív '
266
+ + 'entry-kből.',
267
+ inputSchema: this.objectSchema({ table: this.tableSchema(false) }),
268
+ handler: async (args) => {
269
+ const input = this.asObject(args);
270
+ return this.ok(await this.kindsAndTags(this.optionalTable(input.table)));
271
+ },
272
+ });
273
+ this.register({
274
+ name: 'list_recent_writes', category: category,
275
+ description: 'A legutóbbi write-ok (entry-k) `__created` szerint csökkenőben (legfrissebb felül). '
276
+ + 'Opcionális tár-szűrés + limit (default 20, cap 200).',
277
+ inputSchema: this.objectSchema({
278
+ table: this.tableSchema(false),
279
+ limit: { type: 'number', description: 'Visszaadott elemszám (default 20, cap 200).' },
280
+ }),
281
+ handler: async (args) => {
282
+ const input = this.asObject(args);
283
+ return this.ok(await this.recentWrites(this.optionalTable(input.table), this.asNumber(input.limit)));
284
+ },
285
+ });
286
+ }
287
+ /** maintenance → MP-2 (re-embed) + MP-3 (rebuild reference index). */
288
+ registerMaintenance() {
289
+ const category = fam_mcp_interface_1.FAM_CapabilityCategory.maintenance;
290
+ this.register({
291
+ name: 'reembed_table', category: category,
292
+ description: 'Egy teljes tár újra-embeddelése az aktuális modellel (modell-/provider-váltás után).',
293
+ inputSchema: this.objectSchema({ table: this.tableSchema(true) }),
294
+ handler: async (args) => {
295
+ const input = this.asObject(args);
296
+ const table = this.requireTable(input.table, 'reembed_table');
297
+ return this.ok(await embedding_1.FAM_EmbeddingPipeline_ControlService.getInstance().reEmbedTable(table));
298
+ },
299
+ });
300
+ this.register({
301
+ name: 'reembed_scope', category: category,
302
+ description: 'Egy tár adott scope-ágának újra-embeddelése.',
303
+ inputSchema: this.objectSchema({
304
+ table: this.tableSchema(true), scopePath: this.scopeLayerArraySchema(),
305
+ }),
306
+ handler: async (args) => {
307
+ const input = this.asObject(args);
308
+ const table = this.requireTable(input.table, 'reembed_scope');
309
+ const rawScope = this.asScopeLayers(input.scopePath);
310
+ const resolve = await scope_reference_1.FAM_ScopeResolver_ControlService.getInstance().resolveForWrite(rawScope);
311
+ return this.ok(await embedding_1.FAM_EmbeddingPipeline_ControlService.getInstance()
312
+ .reEmbedScope(table, resolve.scopePath));
313
+ },
314
+ });
315
+ this.register({
316
+ name: 'rebuild_reference_index', category: category,
317
+ description: 'A reference-index TTL-cache azonnali újraépítése (a friss reference-ek azonnal látszanak).',
318
+ inputSchema: this.objectSchema({}),
319
+ handler: async () => this.ok(await scope_reference_1.FAM_ScopeResolver_ControlService.getInstance().rebuildReferenceIndex()),
320
+ });
321
+ // repair_scope_links / deduplicate_entries (MP-12) — DRY-RUN-ONLY MVP1 (safe). A destruktív
322
+ // write-ág (entry-mutáció / soft-delete-összevonás) BACKLOG: vak destruktív bulk-op TILOS
323
+ // (dsgn-003 §4.2), ezért a `dryRun:false` kérés clear "BACKLOG" hibát ad — a `dryRun:true` (default)
324
+ // diagnosztikus riportot ad (mit JAVÍTANA / VONNA ÖSSZE), de NEM ír semmit.
325
+ this.register({
326
+ name: 'repair_scope_links', category: category,
327
+ description: 'Az entry-k scopePath-jának konzisztencia-AUDITja: feltárja a törött scope-linkeket '
328
+ + '(nem-létező / soft-delete-elt scopeId-ra mutató entry-ket). DRY-RUN MVP1: riportál, NEM ír. '
329
+ + 'A tényleges javítás (entry-mutáció) BACKLOG — `dryRun:false` → clear BACKLOG-hiba.',
330
+ inputSchema: this.objectSchema({
331
+ table: this.tableSchema(false),
332
+ dryRun: { type: 'boolean', description: 'MVP1: mindig true (a write-ág BACKLOG). false → BACKLOG-hiba.' },
333
+ }),
334
+ handler: async (args) => {
335
+ const input = this.asObject(args);
336
+ this.assertDryRunOnly(input.dryRun, 'repair_scope_links', 'scope-link entry-mutáció');
337
+ return this.ok(await this.repairScopeLinksDryRun(this.optionalTable(input.table)));
338
+ },
339
+ });
340
+ this.register({
341
+ name: 'deduplicate_entries', category: category,
342
+ description: 'Duplikált entry-k AUDITja: contentHash-alapú dup-csoportok feltárása (a megtartandó + a '
343
+ + 'beolvasztható jelöltek). DRY-RUN MVP1: riportál, NEM ír. A tényleges összevonás (soft-delete) '
344
+ + 'BACKLOG — `dryRun:false` → clear BACKLOG-hiba (vak destruktív op TILOS).',
345
+ inputSchema: this.objectSchema({
346
+ table: this.tableSchema(false),
347
+ dryRun: { type: 'boolean', description: 'MVP1: mindig true (az összevonás BACKLOG). false → BACKLOG-hiba.' },
348
+ }),
349
+ handler: async (args) => {
350
+ const input = this.asObject(args);
351
+ this.assertDryRunOnly(input.dryRun, 'deduplicate_entries', 'duplikátum-összevonás (soft-delete)');
352
+ return this.ok(await this.deduplicateEntriesDryRun(this.optionalTable(input.table)));
353
+ },
354
+ });
355
+ }
356
+ /**
357
+ * import / migration → MP-11 (claude-mem migráció; dsgn-009). A 4 fő művelet + a rollback wired
358
+ * a `FAM_ClaudeMemImport_ControlService`-re (preview → map → run/import → rollback). **Egyirányú**
359
+ * (dsgn-009 §7 HARD): NINCS reverse/export-capability — csak claude-mem → FAM beolvasás. Az
360
+ * `import_claude_mem` és a `run_import` UGYANAZ a write-művelet (alias — dsgn-009 §5.3).
361
+ */
362
+ registerImportMigration() {
363
+ const category = fam_mcp_interface_1.FAM_CapabilityCategory.importMigration;
364
+ // preview_claude_mem_import — nem-destruktív összegzés + dedup-előrejelzés (dsgn-009 §5.1).
365
+ this.register({
366
+ name: 'preview_claude_mem_import', category: category,
367
+ description: 'claude-mem import előnézet (nem-destruktív): counts + byType + dedup-előrejelzés + '
368
+ + 'projectScopes + leképezési minta. SEMMIT nem ír (dsgn-009 §5.1).',
369
+ inputSchema: this.importSourceSchema(),
370
+ handler: async (args) => this.ok(await migration_1.FAM_ClaudeMemImport_ControlService.getInstance().preview(this.toImportRequest(args))),
371
+ });
372
+ // map_claude_mem_entries — a §4 default leképezés override-ja (dsgn-009 §5.2); STILL nem ír.
373
+ this.register({
374
+ name: 'map_claude_mem_entries', category: category,
375
+ description: 'A claude-mem → FAM target-leképezés megjelenítése + OVERRIDE (byType/byId/scope/'
376
+ + 'includePrompts). A feloldott leképezési tervet adja vissza — STILL nem ír (dsgn-009 §5.2).',
377
+ inputSchema: this.importMapSchema(),
378
+ handler: async (args) => this.ok(await migration_1.FAM_ClaudeMemImport_ControlService.getInstance().map(this.toImportRequest(args))),
379
+ });
380
+ // import_claude_mem / run_import — a tényleges beírás egy batchId alatt (dsgn-009 §5.3). Alias.
381
+ const runHandler = async (args) => this.ok(await migration_1.FAM_ClaudeMemImport_ControlService.getInstance().run(this.toImportRequest(args)));
382
+ this.register({
383
+ name: 'import_claude_mem', category: category,
384
+ description: 'A claude-mem migráció tényleges beírása egy import-batch (batchId) alatt: dedup-skip + '
385
+ + 'map + persist + embed. Output: { ok, batchId, ingested, skipped, failed, byTable, status } (dsgn-009 §5.3).',
386
+ inputSchema: this.importMapSchema(), handler: runHandler,
387
+ });
388
+ this.register({
389
+ name: 'run_import', category: category,
390
+ description: 'A claude-mem import tényleges futtatása (az `import_claude_mem` aliasza, dsgn-009 §5.3).',
391
+ inputSchema: this.importMapSchema(), handler: runHandler,
392
+ });
393
+ // rollback_import — pontosan EGY import-batch visszavonása (soft-delete; dsgn-009 §5.4).
394
+ this.register({
395
+ name: 'rollback_import', category: category,
396
+ description: 'Egy korábbi import-batch visszavonása (soft-delete; pontosan az adott batchId, más '
397
+ + 'batch-et nem; idempotens — már visszavont → reverted:0). dsgn-009 §5.4.',
398
+ inputSchema: this.objectSchema({ batchId: { type: 'string', description: 'Az import-batch azonosítója.' } }),
399
+ handler: async (args) => {
400
+ const input = this.asObject(args);
401
+ return this.ok(await migration_1.FAM_ClaudeMemImport_ControlService.getInstance()
402
+ .rollback({ batchId: this.asString(input.batchId) ?? '' }));
403
+ },
404
+ });
405
+ }
406
+ /** debug → MP-3 (trace scope/reference). */
407
+ registerDebug() {
408
+ const category = fam_mcp_interface_1.FAM_CapabilityCategory.debug;
409
+ this.register({
410
+ name: 'trace_scope_resolution', category: category,
411
+ description: 'Egy scope-token write-feloldásának lépés-naplója (token→normalizált→kandidátusok→döntés).',
412
+ inputSchema: this.objectSchema({ scopePath: this.scopeLayerArraySchema() }),
413
+ handler: async (args) => {
414
+ const input = this.asObject(args);
415
+ const rawScope = this.asScopeLayers(input.scopePath);
416
+ return this.ok(await scope_reference_1.FAM_ScopeResolver_ControlService.getInstance()
417
+ .traceResolution(rawScope, 'write-resolve'));
418
+ },
419
+ });
420
+ this.register({
421
+ name: 'trace_reference_expansion', category: category,
422
+ description: 'Egy scope-filter read-expand feloldásának lépés-naplója (subtree-prefilter trace).',
423
+ inputSchema: this.objectSchema({ scopePath: this.scopeLayerArraySchema() }),
424
+ handler: async (args) => {
425
+ const input = this.asObject(args);
426
+ const rawScope = this.asScopeLayers(input.scopePath);
427
+ return this.ok(await scope_reference_1.FAM_ScopeResolver_ControlService.getInstance()
428
+ .traceResolution(rawScope, 'read-expand'));
429
+ },
430
+ });
431
+ // explain_search / inspect_chunk / inspect_embedding (MP-12) — a search-explain + chunk/embedding
432
+ // introspekció. Az explain_search a VALÓS retrieval-pipeline-t futtatja (MP-5) és a scoring-breakdown-t
433
+ // adja; az inspect_chunk/inspect_embedding az entry-t + a vektor-metát olvassa (MP-1; a nyers vektort
434
+ // NEM dump-olja — csak dimenzió/status/model + minta).
435
+ this.register({
436
+ name: 'explain_search', category: category,
437
+ description: 'Egy read-query pipeline-lépéseinek magyarázata: a VALÓS retrieval-pipeline-t futtatja '
438
+ + '(prefilter→vektor→weight→rank), és a per-hit scoring-breakdown-t (score/weight/finalScore) + a '
439
+ + 'dense-jelzőket + uncertaintyNotes-t adja vissza (NEM néma, NEM ír).',
440
+ inputSchema: this.objectSchema({
441
+ tables: {
442
+ type: 'array', items: { type: 'string', enum: Object.values(fam_table_type_enum_1.FAM_Table) },
443
+ description: '1+ fő tár (a `reference` tilos — helper).',
444
+ },
445
+ query: { type: 'string', description: 'A keresési phrase.' },
446
+ topK: { type: 'number', description: 'Találat-limit (config-default 5).' },
447
+ minScore: { type: 'number', description: 'Minimum finalScore-küszöb.' },
448
+ scopeFilter: this.scopeLayerArraySchema(),
449
+ tagFilter: { type: 'array', items: { type: 'string' }, description: 'Tag-szűrő (OR).' },
450
+ kindFilter: { type: 'string', description: 'Egyetlen kind-szűrő.' },
451
+ }),
452
+ handler: async (args) => this.ok(await this.explainSearch(this.asObject(args))),
453
+ });
454
+ this.register({
455
+ name: 'inspect_chunk', category: category,
456
+ description: 'Egy chunk (entry) teljes mező-introspekciója (content/kind/tags/scopePath/source/position/'
457
+ + 'chunk-meta + embedding-státusz). A `table` szűkíti a keresést (vagy minden fő tár).',
458
+ inputSchema: this.objectSchema({
459
+ id: { type: 'string', description: 'Az entry _id-ja.' },
460
+ table: this.tableSchema(false),
461
+ }),
462
+ handler: async (args) => {
463
+ const input = this.asObject(args);
464
+ return this.ok(await this.inspectChunk(this.asString(input.id), this.optionalTable(input.table)));
465
+ },
466
+ });
467
+ this.register({
468
+ name: 'inspect_embedding', category: category,
469
+ description: 'Egy entry embedding-vektorának introspekciója: dimenzió / embeddingStatus / embeddingModel '
470
+ + '+ egy rövid előnézet-minta (a nyers teljes vektort NEM dump-olja — context-tisztaság).',
471
+ inputSchema: this.objectSchema({
472
+ id: { type: 'string', description: 'Az entry _id-ja.' },
473
+ table: this.tableSchema(false),
474
+ }),
475
+ handler: async (args) => {
476
+ const input = this.asObject(args);
477
+ return this.ok(await this.inspectEmbedding(this.asString(input.id), this.optionalTable(input.table)));
478
+ },
479
+ });
480
+ }
481
+ // =========================================================================
482
+ // Wired handler-helpers (delegálás az engine-ekre)
483
+ // =========================================================================
484
+ /** Per-tár statisztika (aktív elem-szám + embedding-státusz bontás) — MP-1. */
485
+ async tableStats(table) {
486
+ const tables = table ? [table] : MAIN_TABLES;
487
+ const result = [];
488
+ for (const target of tables) {
489
+ const registryEntry = embedding_1.FAM_STORE_REGISTRY.find((entry) => entry.table === target);
490
+ if (!registryEntry) {
491
+ continue;
492
+ }
493
+ const dataService = new embedding_1.FAM_Entry_DataService({ dataParams: registryEntry.dataParams, issuer: this.issuer });
494
+ const entries = await dataService.findHydratableList({});
495
+ const byStatus = { pending: 0, completed: 0, error: 0 };
496
+ for (const entry of entries) {
497
+ const status = entry.embeddingStatus ?? 'pending';
498
+ byStatus[status] = (byStatus[status] ?? 0) + 1;
499
+ }
500
+ result.push({ table: target, count: entries.length, embeddingStatus: byStatus });
501
+ }
502
+ return table ? result[0] ?? { table: table, count: 0 } : result;
503
+ }
504
+ /** Az ingest-run-ok listája (a `FAM_IngestRun` rekordok) — MP-4. */
505
+ async listScanRuns(table) {
506
+ const runDataService = new ingest_1.FAM_IngestRun_DataService({ issuer: this.issuer });
507
+ const runs = await runDataService.listRuns();
508
+ return table ? runs.filter((run) => this.runHasTable(run.table, table)) : runs;
509
+ }
510
+ /** Egy run érinti-e az adott tárat (a `table` mező lehet egyetlen tár vagy tömb). */
511
+ runHasTable(runTable, table) {
512
+ if (!runTable) {
513
+ return false;
514
+ }
515
+ return Array.isArray(runTable) ? runTable.includes(table) : runTable === table;
516
+ }
517
+ // =========================================================================
518
+ // MP-12 config-diagnostics: env / Mongo / vector (a `fam doctor` invokálja)
519
+ // =========================================================================
520
+ /**
521
+ * `validate_environment` (MP-12, dsgn-012 §7.1) — a `process.env` infra-szintű változóinak
522
+ * jelenlét-ellenőrzése provider-feltételes kötelezőséggel. NEM olvas DB-t, NEM dob (a hiányt
523
+ * az `ok:false` + a `missing[]` jelzi — deskriptív, nem néma). A titok-értékeket NEM logolja
524
+ * (csak a jelenlét-tényt). A `doctor` env-checkje ezt invokálja.
525
+ */
526
+ validateEnvironment() {
527
+ const provider = (process.env.EMBEDDING_PROVIDER || 'openai').trim();
528
+ const present = [];
529
+ const missing = [];
530
+ // Kötelező infra-változók (dsgn-012 §7.1): MONGODB_URI + a provider-választás.
531
+ this.checkEnv('MONGODB_URI', 'A saját MongoDB connection string (NEM Atlas), pl. mongodb://127.0.0.1:27017.', present, missing);
532
+ this.checkEnv('EMBEDDING_PROVIDER', "Az aktív embedding-provider: 'openai' | 'lmstudio'.", present, missing, true);
533
+ // Provider-feltételes kötelezőség.
534
+ if (provider === 'openai') {
535
+ this.checkEnv('OPENAI_API_KEY', 'OpenAI provider esetén kötelező az API-kulcs.', present, missing);
536
+ }
537
+ else if (provider === 'lmstudio') {
538
+ this.checkEnv('LMSTUDIO_BASE_URL', 'LM Studio provider esetén kötelező a lokális OpenAI-kompatibilis endpoint.', present, missing);
539
+ this.checkEnv('LMSTUDIO_EMBEDDING_MODEL', 'LM Studio provider esetén kötelező a betöltött embedding-modell neve.', present, missing);
540
+ }
541
+ return { ok: missing.length === 0, provider: provider, present: present, missing: missing };
542
+ }
543
+ /** Egy env-változó jelenlét-ellenőrzése (`treatDefaultAsPresent`: van builtin default, ezért sosem hiányzik). */
544
+ checkEnv(name, hint, present, missing, treatDefaultAsPresent = false) {
545
+ const value = process.env[name];
546
+ if ((value && value.trim().length) || treatDefaultAsPresent) {
547
+ present.push(name);
548
+ return;
549
+ }
550
+ missing.push({ name: name, hint: hint });
551
+ }
552
+ /**
553
+ * `test_mongodb_connection` (MP-12) — egy könnyű olvasás a `fam_config` collection-ön (a Dynamo
554
+ * DataService a global service-collection-ön át pingel). Siker → `{ ok:true, reachable:true }`;
555
+ * connect-hiba → `FAM-DB-CONNECT-001` (a `doctor` mongodb-checkje így FAIL-t kap, nem néma).
556
+ */
557
+ async testMongoConnection() {
558
+ try {
559
+ const dataService = new config_data_service_1.FAM_Config_DataService({ issuer: this.issuer });
560
+ // A lekérdezés ténye a ping (a tartalom nem érdekes); üres collection is OK (reachable).
561
+ await dataService.findActiveList({});
562
+ return { ok: true, reachable: true };
563
+ }
564
+ catch (error) {
565
+ throw fam_error_factory_util_1.FAM_Error_Util.create({
566
+ errorCode: error_codes_const_1.FAM_ERROR_CODES.dbConnect,
567
+ message: 'A MongoDB-kapcsolat próbája sikertelen — a `fam_config` collection nem olvasható. '
568
+ + 'Ellenőrizd, hogy fut-e a Mongo, és helyes-e a MONGODB_URI (NEM Atlas; lokális/saját).',
569
+ issuer: this.issuer,
570
+ cause: error,
571
+ context: { operation: 'test-mongodb-connection' },
572
+ });
573
+ }
574
+ }
575
+ /**
576
+ * `test_vector_search` (MP-12, dsgn-006 §4) — LVS smoke: egy próba-szöveg embed-elése + koszinusz-
577
+ * keresés a tár(ak) pool-jában. Per-tár visszaadja a pool-méretet + a próba-keresés státuszát
578
+ * (`searched`/`empty-pool`/`embedding-unavailable`). Üres pool / embedding-hiány → NEM dob (a
579
+ * `doctor` ezt a bootstrap-skip állapotot jelzi), de a struktúra deskriptív (no-silent-failure).
580
+ */
581
+ async testVectorSearch(table) {
582
+ const probe = 'fdp agent memory vector search probe';
583
+ const tables = table ? [table] : MAIN_TABLES;
584
+ const vectorSearch = embedding_1.FAM_VectorSearch_ControlService.getInstance();
585
+ // A query-vektort EGYSZER embeddeljük (graceful: embedding-hiány → string-only smoke).
586
+ let queryVector = [];
587
+ let embeddingAvailable = false;
588
+ try {
589
+ queryVector = await embedding_1.FAM_Embedding_ControlService.getInstance()
590
+ .embedOne({ text: probe, callType: 'embed-query', issuer: this.issuer });
591
+ embeddingAvailable = queryVector.length > 0;
592
+ }
593
+ catch {
594
+ embeddingAvailable = false;
595
+ }
596
+ const results = [];
597
+ for (const target of tables) {
598
+ const poolSize = vectorSearch.getPoolSize(target);
599
+ const hydrated = vectorSearch.isHydrated(target);
600
+ let topHitScore = null;
601
+ let status;
602
+ if (!embeddingAvailable) {
603
+ status = 'embedding-unavailable';
604
+ }
605
+ else if (!poolSize) {
606
+ status = 'empty-pool';
607
+ }
608
+ else {
609
+ try {
610
+ const hits = await vectorSearch.search({ table: target, queryVector: queryVector, topK: 1 });
611
+ topHitScore = hits.length ? hits[0].score : null;
612
+ status = 'searched';
613
+ }
614
+ catch {
615
+ status = 'empty-pool';
616
+ }
617
+ }
618
+ results.push({ table: target, poolSize: poolSize, hydrated: hydrated, topHitScore: topHitScore, status: status });
619
+ }
620
+ return { probe: probe, embeddingAvailable: embeddingAvailable, tables: results };
621
+ }
622
+ // =========================================================================
623
+ // MP-12 high-level-stats: layer-stats / storage-usage
624
+ // =========================================================================
625
+ /** `get_layer_stats` (MP-12) — a scope-fa layerenkénti aggregátuma (scope-szám + root/child bontás). */
626
+ async layerStats() {
627
+ const scope_DS = new scope_reference_1.FAM_Scope_DataService({ issuer: this.issuer });
628
+ const all = await scope_DS.findAllActive();
629
+ const byLayer = new Map();
630
+ for (const scope of all) {
631
+ const layer = scope.layer ?? '(unknown)';
632
+ const bucket = byLayer.get(layer) ?? { scopeCount: 0, rootCount: 0, childCount: 0 };
633
+ bucket.scopeCount++;
634
+ if (scope.parentScopeId) {
635
+ bucket.childCount++;
636
+ }
637
+ else {
638
+ bucket.rootCount++;
639
+ }
640
+ byLayer.set(layer, bucket);
641
+ }
642
+ return Array.from(byLayer.entries()).map(([layer, bucket]) => ({ layer: layer, ...bucket }));
643
+ }
644
+ /**
645
+ * `get_storage_usage` (MP-12) — a fő RAG-tárak + a helper collection-ök aktív doc-száma. A Dynamo
646
+ * base-nek nincs natív `count`, ezért aktív-elemszám (`findDataList(...).length`) — collection-méret-becslés.
647
+ */
648
+ async storageUsage() {
649
+ const collections = [];
650
+ // Fő RAG-tárak (a store-registry-n át, a per-tár dataParams-szal).
651
+ for (const registryEntry of embedding_1.FAM_STORE_REGISTRY) {
652
+ const dataService = new embedding_1.FAM_Entry_DataService({ dataParams: registryEntry.dataParams, issuer: this.issuer });
653
+ const entries = await dataService.findHydratableList({});
654
+ collections.push({ collection: `fam_${registryEntry.table}`, activeDocs: entries.length });
655
+ }
656
+ // Helper collection-ök (reference / scope / ingest-run / config).
657
+ const reference_DS = new scope_reference_1.FAM_Reference_DataService({ issuer: this.issuer });
658
+ collections.push({ collection: 'fam_reference', activeDocs: (await reference_DS.findActive()).length });
659
+ const scope_DS = new scope_reference_1.FAM_Scope_DataService({ issuer: this.issuer });
660
+ collections.push({ collection: 'fam_scopes', activeDocs: (await scope_DS.findAllActive()).length });
661
+ const run_DS = new ingest_1.FAM_IngestRun_DataService({ issuer: this.issuer });
662
+ collections.push({ collection: 'fam_ingest_runs', activeDocs: (await run_DS.listRuns()).length });
663
+ const config_DS = new config_data_service_1.FAM_Config_DataService({ issuer: this.issuer });
664
+ collections.push({ collection: 'fam_config', activeDocs: (await config_DS.findActiveList({})).length });
665
+ const totalActiveDocs = collections.reduce((sum, entry) => sum + entry.activeDocs, 0);
666
+ return { collections: collections, totalActiveDocs: totalActiveDocs };
667
+ }
668
+ // =========================================================================
669
+ // MP-12 inspection: kinds/tags / recent-writes
670
+ // =========================================================================
671
+ /** `list_kinds_tags` (MP-12) — per-tár distinct kind/tag eloszlás (előfordulás-szám) az aktív entry-kből. */
672
+ async kindsAndTags(table) {
673
+ const tables = table ? [table] : MAIN_TABLES;
674
+ const result = [];
675
+ for (const target of tables) {
676
+ const entries = await this.loadTableEntries(target);
677
+ const kindCounts = new Map();
678
+ const tagCounts = new Map();
679
+ for (const entry of entries) {
680
+ if (entry.kind) {
681
+ kindCounts.set(entry.kind, (kindCounts.get(entry.kind) ?? 0) + 1);
682
+ }
683
+ for (const tag of entry.tags ?? []) {
684
+ tagCounts.set(tag, (tagCounts.get(tag) ?? 0) + 1);
685
+ }
686
+ }
687
+ result.push({
688
+ table: target,
689
+ kinds: this.sortedCounts(kindCounts).map((entry) => ({ kind: entry.label, count: entry.count })),
690
+ tags: this.sortedCounts(tagCounts).map((entry) => ({ tag: entry.label, count: entry.count })),
691
+ });
692
+ }
693
+ return result;
694
+ }
695
+ /** `list_recent_writes` (MP-12) — a legutóbbi entry-k `__created` szerint csökkenőben (legfrissebb felül). */
696
+ async recentWrites(table, limit) {
697
+ const cappedLimit = Math.min(Math.max(limit ?? 20, 1), 200);
698
+ const tables = table ? [table] : MAIN_TABLES;
699
+ const collected = [];
700
+ for (const target of tables) {
701
+ const entries = await this.loadTableEntries(target);
702
+ for (const entry of entries) {
703
+ if (!entry._id) {
704
+ continue;
705
+ }
706
+ const created = entry.__created;
707
+ collected.push({
708
+ id: entry._id,
709
+ table: target,
710
+ kind: entry.kind,
711
+ tags: entry.tags ?? [],
712
+ createdMs: created ? new Date(created).getTime() : 0,
713
+ contentPreview: this.preview(entry.content),
714
+ });
715
+ }
716
+ }
717
+ collected.sort((a, b) => b.createdMs - a.createdMs);
718
+ return collected.slice(0, cappedLimit);
719
+ }
720
+ // =========================================================================
721
+ // MP-12 maintenance: repair-scope-links / deduplicate-entries — DRY-RUN-ONLY
722
+ // =========================================================================
723
+ /**
724
+ * A maintenance-eszközök destruktív-ág kapuja: MVP1-ben CSAK dry-run (`dryRun` undefined/true). A
725
+ * `dryRun:false` clear BACKLOG-hibát ad — a vak destruktív bulk-op TILOS (dsgn-003 §4.2), a tényleges
726
+ * írás-ág későbbi fázis (külön maintenance-engine + per-entitás megerősítés).
727
+ */
728
+ assertDryRunOnly(dryRun, capability, destructiveOp) {
729
+ if (dryRun === false) {
730
+ throw fam_error_factory_util_1.FAM_Error_Util.create({
731
+ errorCode: error_codes_const_1.FAM_ERROR_CODES.mcpDispatchUnknownCapability,
732
+ message: `A(z) '${capability}' destruktív ága (${destructiveOp}) BACKLOG — MVP1-ben CSAK dry-run `
733
+ + '(diagnosztikus riport, NEM ír). Vak destruktív bulk-op nem engedélyezett. Hívd `dryRun:true`-val '
734
+ + '(default) a riportért; a tényleges karbantartás későbbi fázis.',
735
+ issuer: this.issuer,
736
+ context: { operation: `capability-invoke:${capability}` },
737
+ });
738
+ }
739
+ }
740
+ /**
741
+ * `repair_scope_links` DRY-RUN (MVP1) — feltárja a törött scope-linkeket: az aktív entry-k, amelyek
742
+ * `scopePath`-ja olyan `scopeId`-ra mutat, ami már NEM aktív a scope-fán. NEM ír — csak riportál
743
+ * (mit JAVÍTANA). A tényleges javítás BACKLOG.
744
+ */
745
+ async repairScopeLinksDryRun(table) {
746
+ const scope_DS = new scope_reference_1.FAM_Scope_DataService({ issuer: this.issuer });
747
+ const activeScopeIds = new Set((await scope_DS.findAllActive()).map((scope) => scope._id ?? ''));
748
+ const tables = table ? [table] : MAIN_TABLES;
749
+ let scannedEntries = 0;
750
+ const brokenLinks = [];
751
+ for (const target of tables) {
752
+ const entries = await this.loadTableEntries(target);
753
+ for (const entry of entries) {
754
+ scannedEntries++;
755
+ const refs = (entry.scopePath ?? []);
756
+ const missing = refs
757
+ .map((ref) => ref.scopeId)
758
+ .filter((scopeId) => scopeId && !activeScopeIds.has(scopeId));
759
+ if (missing.length && entry._id) {
760
+ brokenLinks.push({ id: entry._id, table: target, missingScopeIds: missing });
761
+ }
762
+ }
763
+ }
764
+ return { dryRun: true, scannedEntries: scannedEntries, brokenLinks: brokenLinks };
765
+ }
766
+ /**
767
+ * `deduplicate_entries` DRY-RUN (MVP1) — feltárja a `contentHash`-azonos dup-csoportokat: a megtartandó
768
+ * (legrégebbi `__created`) + a beolvasztható jelöltek. NEM ír — csak riportál (mit VONNA ÖSSZE). A
769
+ * tényleges összevonás (soft-delete) BACKLOG.
770
+ */
771
+ async deduplicateEntriesDryRun(table) {
772
+ const tables = table ? [table] : MAIN_TABLES;
773
+ let scannedEntries = 0;
774
+ const duplicateGroups = [];
775
+ for (const target of tables) {
776
+ const entries = await this.loadTableEntries(target);
777
+ const byHash = new Map();
778
+ for (const entry of entries) {
779
+ scannedEntries++;
780
+ if (!entry.contentHash || !entry._id) {
781
+ continue;
782
+ }
783
+ const bucket = byHash.get(entry.contentHash) ?? [];
784
+ bucket.push(entry);
785
+ byHash.set(entry.contentHash, bucket);
786
+ }
787
+ for (const [contentHash, group] of byHash.entries()) {
788
+ if (group.length < 2) {
789
+ continue;
790
+ }
791
+ // A megtartandó = a legrégebbi (`__created` ASC); a többi a merge-jelölt.
792
+ const sorted = group.slice().sort((a, b) => this.createdMs(a) - this.createdMs(b));
793
+ const keepId = sorted[0]._id ?? '';
794
+ const mergeIds = sorted.slice(1).map((entry) => entry._id ?? '').filter((id) => id.length > 0);
795
+ duplicateGroups.push({ table: target, contentHash: contentHash, keepId: keepId, mergeIds: mergeIds });
796
+ }
797
+ }
798
+ return { dryRun: true, scannedEntries: scannedEntries, duplicateGroups: duplicateGroups };
799
+ }
800
+ // =========================================================================
801
+ // MP-12 debug: explain-search / inspect-chunk / inspect-embedding
802
+ // =========================================================================
803
+ /**
804
+ * `explain_search` (MP-12, dsgn-005) — a VALÓS retrieval-pipeline-t futtatja egyetlen query-re, és a
805
+ * per-hit scoring-breakdown-t (score/weight/finalScore) + a dense-jelzőket + uncertaintyNotes-t adja.
806
+ * NEM ír. A `tables` kötelező + a `reference` tilos (helper) — defense-in-depth kihagyás.
807
+ */
808
+ async explainSearch(input) {
809
+ const tables = this.asTableArray(input.tables).filter((target) => target !== fam_table_type_enum_1.FAM_Table.reference);
810
+ const query = this.asString(input.query);
811
+ if (!tables.length || !query) {
812
+ throw fam_error_factory_util_1.FAM_Error_Util.create({
813
+ errorCode: error_codes_const_1.FAM_ERROR_CODES.mcpDispatchUnknownCapability,
814
+ message: "Az 'explain_search'-hez kötelező a `tables` (1+ fő tár, a `reference` tilos) + a `query`.",
815
+ issuer: this.issuer,
816
+ context: { operation: 'capability-invoke:explain_search' },
817
+ });
818
+ }
819
+ const request = {
820
+ queries: [{
821
+ tables: tables,
822
+ query: query,
823
+ topK: this.asNumber(input.topK),
824
+ minScore: this.asNumber(input.minScore),
825
+ scopeFilter: this.asScopeLayers(input.scopeFilter),
826
+ tagFilter: this.asStringArray(input.tagFilter),
827
+ kindFilter: this.asString(input.kindFilter),
828
+ }],
829
+ includeContent: false,
830
+ };
831
+ const response = await retrieval_1.FAM_Retrieval_ControlService.getInstance().read(request);
832
+ const result = response.results[0];
833
+ return {
834
+ query: result?.query ?? query,
835
+ tables: result?.tables ?? tables,
836
+ scoring: (result?.hits ?? []).map((hit) => ({
837
+ id: hit.id, table: hit.table, score: hit.score, weight: hit.weight, finalScore: hit.finalScore,
838
+ kind: hit.kind, tags: hit.tags,
839
+ })),
840
+ denseResults: result?.denseResults ?? false,
841
+ totalRelevant: result?.totalRelevant ?? 0,
842
+ truncated: result?.truncated ?? false,
843
+ suggestions: result?.suggestions ?? [],
844
+ uncertaintyNotes: result?.uncertaintyNotes ?? [],
845
+ };
846
+ }
847
+ /** `inspect_chunk` (MP-12) — egy entry teljes mező-introspekciója (a megadott `id`-re; opc. tár-szűkítés). */
848
+ async inspectChunk(id, table) {
849
+ const entry = await this.requireEntryById(id, table, 'inspect_chunk');
850
+ return {
851
+ id: entry._id,
852
+ table: entry.table,
853
+ kind: entry.kind,
854
+ tags: entry.tags ?? [],
855
+ content: entry.content,
856
+ contentHash: entry.contentHash,
857
+ scopePath: (entry.scopePath ?? []),
858
+ referenceLinks: entry.referenceLinks ?? [],
859
+ weight: entry.weight,
860
+ importance: entry.importance,
861
+ source: entry.source,
862
+ sourceFilePath: entry.sourceFilePath ?? null,
863
+ chunkIndex: entry.chunkIndex,
864
+ chunkTotal: entry.chunkTotal,
865
+ chunkType: entry.chunkType,
866
+ position: entry.position,
867
+ headingPath: entry.headingPath ?? [],
868
+ embeddingStatus: entry.embeddingStatus ?? 'pending',
869
+ embeddingModel: entry.embeddingModel,
870
+ createdMs: this.createdMs(entry),
871
+ lastModifiedMs: entry.__lastModified ? new Date(entry.__lastModified).getTime() : 0,
872
+ };
873
+ }
874
+ /**
875
+ * `inspect_embedding` (MP-12) — egy entry embedding-vektor-metája: dimenzió / status / model + egy
876
+ * rövid előnézet-minta (az első néhány komponens). A nyers TELJES vektort NEM dump-olja (context-tisztaság).
877
+ */
878
+ async inspectEmbedding(id, table) {
879
+ const entry = await this.requireEntryById(id, table, 'inspect_embedding');
880
+ const vector = entry.contentVector ?? [];
881
+ return {
882
+ id: entry._id,
883
+ table: entry.table,
884
+ embeddingStatus: entry.embeddingStatus ?? 'pending',
885
+ embeddingModel: entry.embeddingModel ?? null,
886
+ dimensions: vector.length,
887
+ hasVector: vector.length > 0,
888
+ samplePreview: vector.slice(0, 8),
889
+ };
890
+ }
891
+ // =========================================================================
892
+ // MP-12 közös entry-helperek (lazy DataService — eager-resolve elkerülés)
893
+ // =========================================================================
894
+ /** Egy fő tár aktív entry-i (a per-tár dataParams-szal). Nem-fő/ismeretlen tár → üres. */
895
+ async loadTableEntries(table) {
896
+ const registryEntry = embedding_1.FAM_STORE_REGISTRY.find((entry) => entry.table === table);
897
+ if (!registryEntry) {
898
+ return [];
899
+ }
900
+ const dataService = new embedding_1.FAM_Entry_DataService({ dataParams: registryEntry.dataParams, issuer: this.issuer });
901
+ return dataService.findHydratableList({});
902
+ }
903
+ /** Egy entry feloldása `id`-ből (a megadott tárból, vagy minden fő tárból). Nincs → `FAM-MCP-DISPATCH-002`. */
904
+ async requireEntryById(id, table, capability) {
905
+ if (!id) {
906
+ throw fam_error_factory_util_1.FAM_Error_Util.create({
907
+ errorCode: error_codes_const_1.FAM_ERROR_CODES.mcpDispatchUnknownCapability,
908
+ message: `A(z) '${capability}' capability-hez kötelező az 'id' (az entry _id-ja).`,
909
+ issuer: this.issuer,
910
+ context: { operation: `capability-invoke:${capability}` },
911
+ });
912
+ }
913
+ const tables = table ? [table] : MAIN_TABLES;
914
+ for (const target of tables) {
915
+ const registryEntry = embedding_1.FAM_STORE_REGISTRY.find((entry) => entry.table === target);
916
+ if (!registryEntry) {
917
+ continue;
918
+ }
919
+ const dataService = new embedding_1.FAM_Entry_DataService({ dataParams: registryEntry.dataParams, issuer: this.issuer });
920
+ const entry = await dataService.findData({ _id: id }, true);
921
+ if (entry && entry._id) {
922
+ return entry;
923
+ }
924
+ }
925
+ throw fam_error_factory_util_1.FAM_Error_Util.create({
926
+ errorCode: error_codes_const_1.FAM_ERROR_CODES.mcpDispatchUnknownCapability,
927
+ message: `A(z) '${capability}' nem talált entry-t a(z) '${id}' _id-ra `
928
+ + `${table ? `a(z) '${table}' tárban` : 'egyetlen fő tárban sem'}.`,
929
+ issuer: this.issuer,
930
+ context: { operation: `capability-invoke:${capability}` },
931
+ });
932
+ }
933
+ /** Egy entry `__created` epoch-ms-e (vagy 0). */
934
+ createdMs(entry) {
935
+ return entry.__created ? new Date(entry.__created).getTime() : 0;
936
+ }
937
+ /** Egy content rövid előnézete (max 160 karakter, sortörés-normalizálva). */
938
+ preview(content) {
939
+ if (!content) {
940
+ return '';
941
+ }
942
+ const normalized = content.replace(/\s+/g, ' ').trim();
943
+ return normalized.length > 160 ? `${normalized.slice(0, 160)}…` : normalized;
944
+ }
945
+ /** Egy count-map → rendezett `{ label, count }[]` (count DESC, majd label ASC). */
946
+ sortedCounts(counts) {
947
+ return Array.from(counts.entries())
948
+ .map(([label, count]) => ({ label: label, count: count }))
949
+ .sort((a, b) => (b.count - a.count) || (a.label < b.label ? -1 : 1));
950
+ }
951
+ /** Egy érték `FAM_Table[]`-ként (a fő + helper tárakból; ismeretlen érték kihagyva). */
952
+ asTableArray(value) {
953
+ if (!Array.isArray(value)) {
954
+ return [];
955
+ }
956
+ const tables = [];
957
+ for (const item of value) {
958
+ const table = this.optionalTable(item);
959
+ if (table) {
960
+ tables.push(table);
961
+ }
962
+ }
963
+ return tables;
964
+ }
965
+ /** Egy érték `string[]`-ként (a nem-string elemeket kihagyva; üres → undefined). */
966
+ asStringArray(value) {
967
+ if (!Array.isArray(value)) {
968
+ return undefined;
969
+ }
970
+ const result = value.filter((item) => typeof item === 'string' && item.length > 0);
971
+ return result.length ? result : undefined;
972
+ }
973
+ // =========================================================================
974
+ // registry-építés + sablon-helperek
975
+ // =========================================================================
976
+ /** Egy wired capability regisztrálása. */
977
+ register(descriptor) {
978
+ this.registry.set(descriptor.name, descriptor);
979
+ }
980
+ // (Eltávolítva: `registerLateBound` — FAM-REV-022. MVP1-ben MINDEN capability valós handlerrel
981
+ // regisztrált [`register`]; nincs "pending MP-X" late-bind stub. A descriptor `lateBound`/`pendingMp`
982
+ // mezője megmarad [defenzív, a `list`/`describe` riport olvassa], de jelenleg egyik capability sem
983
+ // állítja `true`-ra.)
984
+ /** Egy `object` JSON-schema a megadott property-kből. */
985
+ objectSchema(properties) {
986
+ return { type: 'object', properties: properties, additionalProperties: false };
987
+ }
988
+ /** A `table` property-schema (a fő tárak enuma; required jelzéssel). */
989
+ tableSchema(required) {
990
+ const schema = {
991
+ type: 'string',
992
+ enum: Object.values(fam_table_type_enum_1.FAM_Table),
993
+ description: required ? 'A cél-tár (kötelező).' : 'Opcionális tár-szűrés (üres → minden fő tár).',
994
+ };
995
+ return schema;
996
+ }
997
+ /** A `scopePath` array-schema (raw layer-lánc). */
998
+ scopeLayerArraySchema() {
999
+ return {
1000
+ type: 'array',
1001
+ description: 'Nyers scope-lánc (root→leaf): [{ layer, rawName }] párok.',
1002
+ items: this.objectSchema({
1003
+ layer: { type: 'string', description: 'A dinamikus layer-név (pl. organization/project).' },
1004
+ rawName: { type: 'string', description: 'A nyers (feloldatlan) scope-név.' },
1005
+ }),
1006
+ };
1007
+ }
1008
+ // =========================================================================
1009
+ // import / migration schema + arg-coerce (MP-11, dsgn-009)
1010
+ // =========================================================================
1011
+ /** A claude-mem import forrás-schema (preview/source-readers): from + path + includePrompts. */
1012
+ importSourceSchema() {
1013
+ return this.objectSchema({
1014
+ from: {
1015
+ type: 'string', enum: Object.values(migration_1.FAM_ClaudeMemSource_Type),
1016
+ description: 'A claude-mem forrás-csatorna (default `export-json`, függőség-mentes).',
1017
+ },
1018
+ path: { type: 'string', description: 'export-JSON/SQLite útvonal VAGY worker base-URL.' },
1019
+ includePrompts: { type: 'boolean', description: 'A user_prompt-ok be-importálása (default false).' },
1020
+ });
1021
+ }
1022
+ /** A map/run import-schema: a forrás-schema + az `overrides` blokk (byType/byId/scope/includePrompts). */
1023
+ importMapSchema() {
1024
+ const schema = this.importSourceSchema();
1025
+ if (schema.properties) {
1026
+ schema.properties.overrides = {
1027
+ type: 'object',
1028
+ description: 'A §4 default leképezés felülírása (byType / byId / scope / includePrompts).',
1029
+ additionalProperties: true,
1030
+ };
1031
+ }
1032
+ return schema;
1033
+ }
1034
+ /** A nyers args → `FAM_ImportRequest` (source default `claude-mem`; from default `export-json`). */
1035
+ toImportRequest(args) {
1036
+ const input = this.asObject(args);
1037
+ return {
1038
+ source: this.asString(input.source) ?? 'claude-mem',
1039
+ from: this.asImportFrom(input.from),
1040
+ path: this.asString(input.path),
1041
+ includePrompts: typeof input.includePrompts === 'boolean' ? input.includePrompts : undefined,
1042
+ overrides: this.asImportOverrides(input.overrides),
1043
+ };
1044
+ }
1045
+ /** Egy érték `FAM_ClaudeMemSource_Type`-ként (vagy undefined → a engine default-ol export-json-ra). */
1046
+ asImportFrom(value) {
1047
+ const from = this.asString(value);
1048
+ return from
1049
+ ? Object.values(migration_1.FAM_ClaudeMemSource_Type).find((source) => source === from)
1050
+ : undefined;
1051
+ }
1052
+ /** A nyers `overrides` → `FAM_ImportOverrides` (byType/byId target-override + scope + includePrompts). */
1053
+ asImportOverrides(value) {
1054
+ if (!value || typeof value !== 'object') {
1055
+ return undefined;
1056
+ }
1057
+ const raw = value;
1058
+ const overrides = {};
1059
+ const byType = this.asTargetOverrideMap(raw.byType);
1060
+ if (Object.keys(byType).length) {
1061
+ overrides.byType = byType;
1062
+ }
1063
+ const byId = this.asTargetOverrideMap(raw.byId);
1064
+ if (Object.keys(byId).length) {
1065
+ overrides.byId = byId;
1066
+ }
1067
+ const scope = this.asScopeOverrideMap(raw.scope);
1068
+ if (Object.keys(scope).length) {
1069
+ overrides.scope = scope;
1070
+ }
1071
+ if (typeof raw.includePrompts === 'boolean') {
1072
+ overrides.includePrompts = raw.includePrompts;
1073
+ }
1074
+ return Object.keys(overrides).length ? overrides : undefined;
1075
+ }
1076
+ /** Egy nyers map → `{ key: { table?, kind?, tags? } }` target-override map. */
1077
+ asTargetOverrideMap(value) {
1078
+ const result = {};
1079
+ if (!value || typeof value !== 'object') {
1080
+ return result;
1081
+ }
1082
+ for (const [key, raw] of Object.entries(value)) {
1083
+ const entry = this.asObject(raw);
1084
+ const override = {};
1085
+ const table = this.optionalTable(entry.table);
1086
+ if (table) {
1087
+ override.table = table;
1088
+ }
1089
+ if (typeof entry.kind === 'string') {
1090
+ override.kind = entry.kind;
1091
+ }
1092
+ if (Array.isArray(entry.tags)) {
1093
+ override.tags = entry.tags.filter((tag) => typeof tag === 'string');
1094
+ }
1095
+ if (Object.keys(override).length) {
1096
+ result[key] = override;
1097
+ }
1098
+ }
1099
+ return result;
1100
+ }
1101
+ /** Egy nyers scope-override map → `{ 'project:<name>': { scopePath: [{layer,rawName}] } }`. */
1102
+ asScopeOverrideMap(value) {
1103
+ const result = {};
1104
+ if (!value || typeof value !== 'object') {
1105
+ return result;
1106
+ }
1107
+ for (const [key, raw] of Object.entries(value)) {
1108
+ const entry = this.asObject(raw);
1109
+ const layers = this.asScopeLayers(entry.scopePath);
1110
+ if (layers.length) {
1111
+ result[key] = { scopePath: layers.map((layer) => ({ layer: layer.layer, rawName: layer.rawName })) };
1112
+ }
1113
+ }
1114
+ return result;
1115
+ }
1116
+ /** A `ok` outcome (a wired handlerek strukturált eredménye). */
1117
+ ok(data) {
1118
+ return { data: data };
1119
+ }
1120
+ // =========================================================================
1121
+ // argumentum-coerce helperek (a nyers JSON args típus-szűrése)
1122
+ // =========================================================================
1123
+ /** A nyers args → kulcs→érték objektum (nem-objektum → üres). */
1124
+ asObject(args) {
1125
+ return args && typeof args === 'object' ? args : {};
1126
+ }
1127
+ /** Egy érték string-ként (vagy undefined). */
1128
+ asString(value) {
1129
+ return typeof value === 'string' && value.length ? value : undefined;
1130
+ }
1131
+ /** Egy érték number-ként (vagy undefined). */
1132
+ asNumber(value) {
1133
+ return typeof value === 'number' ? value : undefined;
1134
+ }
1135
+ /** Egy érték opcionális `FAM_Table`-ként (a fő tárak közül; egyébként undefined). */
1136
+ optionalTable(value) {
1137
+ const tableValue = this.asString(value);
1138
+ return tableValue ? Object.values(fam_table_type_enum_1.FAM_Table).find((table) => table === tableValue) : undefined;
1139
+ }
1140
+ /** Egy kötelező `FAM_Table` (hiányzó/ismeretlen → `FAM-CAP` hiba). */
1141
+ requireTable(value, capability) {
1142
+ const table = this.optionalTable(value);
1143
+ if (!table) {
1144
+ throw fam_error_factory_util_1.FAM_Error_Util.create({
1145
+ errorCode: error_codes_const_1.FAM_ERROR_CODES.mcpDispatchUnknownCapability,
1146
+ message: `A(z) '${capability}' capability-hez kötelező a 'table' (a 6 fő tár egyike).`,
1147
+ issuer: this.issuer,
1148
+ context: { operation: `capability-invoke:${capability}` },
1149
+ });
1150
+ }
1151
+ return table;
1152
+ }
1153
+ /** Egy érték nyers scope-lánc-ként (`{ layer, rawName }[]`; nem-tömb → üres). */
1154
+ asScopeLayers(value) {
1155
+ if (!Array.isArray(value)) {
1156
+ return [];
1157
+ }
1158
+ const layers = [];
1159
+ for (const item of value) {
1160
+ const obj = this.asObject(item);
1161
+ const layer = this.asString(obj.layer);
1162
+ const rawName = this.asString(obj.rawName);
1163
+ if (layer && rawName) {
1164
+ layers.push({ layer: layer, rawName: rawName });
1165
+ }
1166
+ }
1167
+ return layers;
1168
+ }
1169
+ /** A config-context feloldása az args-ból (table + opc. scopePath canonical-resolve). */
1170
+ async toConfigContext(input) {
1171
+ const table = this.optionalTable(input.table);
1172
+ const rawScope = this.asScopeLayers(input.scopePath);
1173
+ if (!rawScope.length) {
1174
+ return { table: table };
1175
+ }
1176
+ const expand = await scope_reference_1.FAM_ScopeResolver_ControlService.getInstance().resolveForWrite(rawScope);
1177
+ return { table: table, scopePath: expand.scopePath };
1178
+ }
1179
+ }
1180
+ exports.FAM_CapabilityRegistry = FAM_CapabilityRegistry;