@futdevpro/fdp-agent-memory 1.1.21 → 1.1.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/package.json +2 -2
- package/build/src/_cli/_commands/scan-projects.command.js +1 -1
- package/build/src/_collections/config-catalog.const.js +4 -27
- package/build/src/_collections/title.const.js +26 -26
- package/build/src/_modules/capture/_services/fam-auto-capture.control-service.js +1 -1
- package/build/src/_modules/embedding/_services/fam-embedding-pipeline.control-service.js +1 -1
- package/build/src/_modules/embedding/_services/fam-entry.data-service.js +23 -1
- package/build/src/_modules/ingest/_services/fam-ingest.control-service.js +5 -1
- package/build/src/_modules/mcp/_collections/fam-core-tools.const.js +1 -1
- package/build/src/_modules/mcp/_services/fam-capability-registry.service.js +113 -33
- package/build/src/_modules/mcp/_services/fam-read-tool.service.js +1 -1
- package/build/src/_modules/migration/_services/fam-claude-mem-import.control-service.js +1 -1
- package/build/src/_modules/scope-reference/_collections/fam-scope-maintenance.util.js +58 -0
- package/build/src/_modules/scope-reference/_services/fam-scope-maintenance.control-service.js +190 -0
- package/build/src/_modules/scope-reference/_services/fam-scope-resolver.control-service.js +2 -2
- package/build/src/_modules/scope-reference/index.js +4 -1
- package/build/src/_routes/server/api/api.controller.js +54 -23
- package/build/src/_routes/server/config/config.control-service.js +2 -2
- package/build/src/app.server.js +26 -27
- package/package.json +2 -2
- package/build/src/_collections/fam-heap-guard.control-service.js +0 -176
package/build/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@futdevpro/fdp-agent-memory",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.30",
|
|
4
4
|
"description": "Local-first, vector-backed multi-table agent memory exposed as an MCP server (read/write/capabilities). Public, FDP-Templates-free, no auth.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"license": "FDP",
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"@futdevpro/fsm-dynamo": "^1.16.7",
|
|
58
|
-
"@futdevpro/nts-dynamo": "^1.15.
|
|
58
|
+
"@futdevpro/nts-dynamo": "^1.15.74",
|
|
59
59
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
60
60
|
"axios": "~1.8.1",
|
|
61
61
|
"better-sqlite3": "^12.4.1",
|
|
@@ -22,7 +22,7 @@ class FAM_ScanProjects_Util {
|
|
|
22
22
|
.description('Több projekt SOROS scan-je (auto-felderítés --root-tal vagy explicit --config-gal; nincs '
|
|
23
23
|
+ 'konkurrens thrash). ⚠️ MINDEN projekt-gyökérbe előbb `.fdpfamignore` + `--dry-run` (lásd lent).')
|
|
24
24
|
.option('--root <dir>', 'projekt-gyökerek auto-felderítése e mappa alatt (package.json/.git markerek)')
|
|
25
|
-
.option('--config <file.json>', 'explicit projekt-lista
|
|
25
|
+
.option('--config <file.json>', 'explicit projekt-lista JSON — elemenként `path` + `project` (+ opcionális `org`, `table`)')
|
|
26
26
|
.option('--depth <n>', 'auto-felderítés max mélysége (default 3)', '3')
|
|
27
27
|
.option('--org <name>', 'default organization az auto-felderítéshez', 'FutDevPro')
|
|
28
28
|
.option('--table <documents|codebase>', 'default cél-tár', 'codebase')
|
|
@@ -87,7 +87,7 @@ exports.CONFIG_CATALOG = {
|
|
|
87
87
|
'read.importChunkWeight': {
|
|
88
88
|
type: 'number', default: 0.4, min: 0.0, max: 1.0, levels: ALL_LEVELS,
|
|
89
89
|
description: 'Az import-only chunkok (`chunkType:"imports"`) read-idejű rangsor-szorzója (FAM-REV-060). '
|
|
90
|
-
+ 'A szimbólum-név query-knél az import-sorok (`import { X } from "./y"`) magas cosine-t kapnak, de '
|
|
90
|
+
+ 'A szimbólum-név query-knél az import-sorok (`import { X: X } from "./y"`) magas cosine-t kapnak, de '
|
|
91
91
|
+ 'alacsony az ÉRTÉKük (csak import, nem definíció) → e szorzóval a `finalScore = score × weight × '
|
|
92
92
|
+ 'importChunkWeight` a definíció-chunkok ALÁ rangsorolja őket. READ-time (nincs re-embed); a nyers '
|
|
93
93
|
+ 'cosine score (relevancia-jelzések) NEM változik, csak a ranking. 1 = nincs csökkentés.',
|
|
@@ -269,32 +269,9 @@ exports.CONFIG_CATALOG = {
|
|
|
269
269
|
type: 'number', default: 20, min: 1, max: 200, integer: true, levels: GLOBAL_TABLE,
|
|
270
270
|
description: 'Párhuzamos ingest flow-limit (CCAP DEFAULT_INGEST_FLOW_LIMIT=20, cap 200).',
|
|
271
271
|
},
|
|
272
|
-
//
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
description: 'A heap-guard (OOM-hibakezelés) be/ki. `true` (default): watchdog (heap-arány + GC-hányad) + a '
|
|
276
|
-
+ 'nehéz write-ek (scan/import/re-embed) load-shed-je critical nyomásnál + graceful-exit a near-limit '
|
|
277
|
-
+ 'zónában (a kriptikus exit-134 helyett). `false`: csak a crash-handlerek (uncaught/unhandled) aktívak.',
|
|
278
|
-
},
|
|
279
|
-
'memory.warnRatio': {
|
|
280
|
-
type: 'number', default: 0.70, min: 0.1, max: 1.0, levels: GLOBAL_ONLY,
|
|
281
|
-
description: 'Heap-arány (used/limit), ami fölött a guard `warn` szintet jelez (log; még nincs load-shed).',
|
|
282
|
-
},
|
|
283
|
-
'memory.shedRatio': {
|
|
284
|
-
type: 'number', default: 0.82, min: 0.1, max: 1.0, levels: GLOBAL_ONLY,
|
|
285
|
-
description: 'Heap-arány, ami fölött `critical` → a nehéz write-ek (scan/import/re-embed) 503-mal elutasítva '
|
|
286
|
-
+ '(a könnyű read/create/update fut tovább). A GC-hányad ≥60% is critical-t vált (mark-compact thrash-jel).',
|
|
287
|
-
},
|
|
288
|
-
'memory.exitRatio': {
|
|
289
|
-
type: 'number', default: 0.92, min: 0.1, max: 1.0, levels: GLOBAL_ONLY,
|
|
290
|
-
description: 'Heap-arány, ami fölött tartósan (GC-vel sem csökken) a guard TISZTÁN kilép (exit 137) → a `fam '
|
|
291
|
-
+ 'serve` ps1 prediktíven újraindít. A kriptikus „Ineffective mark-compacts" exit-134 elkerülése. shed < exit ajánlott.',
|
|
292
|
-
},
|
|
293
|
-
'memory.pollMs': {
|
|
294
|
-
type: 'number', default: 3000, min: 500, max: 60000, integer: true, levels: GLOBAL_ONLY,
|
|
295
|
-
description: 'A heap-watchdog mintavételi időköze (ms) + a GC-hányad gördülő-ablaka. Kisebb = gyorsabb '
|
|
296
|
-
+ 'reakció a hirtelen növekedésre, több CPU. 3000 = jó alapérték.',
|
|
297
|
-
},
|
|
272
|
+
// (Az OOM-hibakezelés/heap-guard configja a BEDROCK `DyNTS_global_settings.memoryGuard`-ban van — NEM FAM-config:
|
|
273
|
+
// a bedrock `DyNTS_MemoryGuard`-ot a base App auto-installálja, a FAM a thresholdokat az `overrideDynamoNTSGlobalSettings`-ben
|
|
274
|
+
// állítja. Ezért nincs FAM `memory.*` config-kulcs.)
|
|
298
275
|
'patterns.defaultWeight': {
|
|
299
276
|
type: 'number', default: 5, min: 0, levels: [fam_config_level_type_enum_1.FAM_ConfigLevel.table], tableOnly: fam_table_type_enum_1.FAM_Table.codingPatterns,
|
|
300
277
|
description: 'A coding_patterns tár default retrieval-súlya (CCAP weight 5; csak coding_patterns).',
|
|
@@ -2,30 +2,30 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.FAM_title = void 0;
|
|
4
4
|
exports.FAM_title = `
|
|
5
|
-
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⠤⠤⠤⠤⣄⠀⠀⠀⠀⠀⠀⢀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
|
6
|
-
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣶⡊⠉⠉⣉⣱⡷⠶⢢⣠⢴⣶⡝⠒⠉⢉⣭⡽⠟⢉⣀⡀⠹⢭⠒⢤⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
|
7
|
-
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡠⠔⢚⣩⡽⠿⠊⢉⣉⡂⣀⣩⠭⢴⠟⠋⠉⠉⠉⠛⠳⢦⣬⣤⡴⠞⠛⠁⠛⠳⣾⣧⠀⠟⠀⠉⠲⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
|
8
|
-
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⢚⠁⠀⠰⠋⢡⠄⠀⠞⣫⢟⡥⠒⠉⠹⣿⡀⠀⠀⢦⡀⠀⠀⠀⠈⠻⡧⡀⠀⠀⠀⠀⠈⠻⣗⡶⠶⠶⢤⡀⠱⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀
|
|
9
|
-
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢮⣤⡾⠀⠀⣠⡴⠋⠀⡠⣚⠥⠒⢛⡲⠄⠀⠈⢻⡆⠀⠀⠻⣦⣀⠀⠀⠀⣿⠻⣦⣀⣴⠶⠂⠀⠘⣷⡄⠀⠀⢀⣴⡿⠈⠢⡀⠀⠀⠀⠀⠀⠀
|
|
10
|
-
⠀⠀⠀⠀⠀⠀⠀⡠⠖⣉⣁⡀⠀⢀⣾⠋⠴⢿⣽⠋⠀⠞⢉⣉⣽⣳⣄⣀⠀⠋⠀⠀⠀⠈⠙⣷⡄⠀⠁⠀⠙⢤⣯⡀⠀⠀⠀⣼⡇⠀⡾⠋⠁⠀⠳⣄⠘⢆⠀⠀⠀⠀⠀
|
|
11
|
-
⠀⠀⠀⠀⠀⡰⠋⠰⠛⢻⡞⢉⣠⣼⡇⢀⣴⠟⠛⠒⣴⠟⠋⠉⠀⠀⢀⣀⣀⡀⠀⠀⠀⠀⠀⣸⡇⠀⠀⠳⣄⠀⠉⢿⣄⠀⢰⣿⣧⡀⠀⣴⠶⠶⣦⡼⢧⠈⢣⠀⠀⠀⠀
|
|
12
|
-
⠀⠀⠀⢀⡞⣡⣶⡄⠀⡟⣳⠿⠋⠙⡍⡽⠁⣀⣤⣤⣿⡄⠀⠀⠀⠀⡿⡉⣀⣀⣤⣤⣤⣴⠾⠥⠽⣦⣄⠀⠉⠻⢶⡼⢻⠀⠈⠇⠘⡷⡄⠘⠂⠀⢀⡍⠻⣷⣄⡇⠀⠀⠀
|
|
13
|
-
⠀⠀⠀⠘⣺⠏⢸⢃⣼⠟⢁⡤⠀⣠⢟⡷⠟⢋⣉⣤⡿⠇⠀⠀⠀⢰⣣⠞⠋⠉⠉⠁⡀⠀⠀⠀⠀⠀⠙⢷⣄⠀⠀⢹⣾⠀⠀⠀⠀⢸⡇⠀⣀⡀⣾⠀⠀⠈⢻⡁⢦⠀⠀
|
|
14
|
-
⠀⠀⣠⢚⣵⣄⠈⣼⡇⠀⢸⠧⢞⡵⠋⠠⠚⠉⠉⠀⠀⢀⡇⠀⣰⣟⣁⣀⠀⠀⠀⠀⠉⠒⠶⣤⣤⣀⠀⠀⠙⠀⠀⢸⡇⢰⣟⠛⢶⡋⣇⠀⠉⠻⡟⡄⠀⢀⢀⣿⠀⢧⠀
|
|
15
|
-
⠀⣰⠃⢸⠁⣿⠀⠸⣧⠀⢸⢣⠋⠀⣠⣤⠶⢶⢒⣤⣔⣻⠣⢼⠟⠁⠀⠙⢷⡄⠀⠀⢀⠀⠀⠀⠈⠓⢟⢦⠀⠀⠀⢸⡇⠈⠻⣦⡀⠈⠻⣷⣄⠀⠘⣿⠀⠸⣿⣇⣀⢸⠀
|
|
16
|
-
⠀⡇⠀⠀⣼⠇⠀⢀⣿⠀⣇⣇⣴⠟⠋⢠⣾⠟⠉⠀⠀⠈⠳⣼⠀⠀⠀⠀⠀⠳⠀⠀⠈⢳⣄⠀⠀⠀⢸⣼⠀⠀⠀⠈⡟⢆⠀⠈⢻⡀⠀⠈⢻⣆⠀⣻⠃⠀⠀⢹⡟⠻⡀
|
|
17
|
-
⠀⢧⡆⣼⠏⠀⣾⠟⠁⢰⠃⡵⠃⢀⣴⡿⠁⠀⡀⠀⠀⠀⠀⠹⣧⡀⠰⣦⡀⠀⠀⠀⠀⠀⣻⢦⣀⣠⡾⣇⠀⠀⢀⣰⠟⠙⢷⣄⠀⠀⠀⠀⠀⣿⠀⠉⢠⠄⠀⣼⡇⠀⢧
|
|
18
|
-
⢀⠞⢡⡟⠀⠀⣿⠀⢀⡏⡼⠁⣴⠟⠁⠀⠀⠀⣿⠀⣀⣀⢀⣴⠘⣷⡀⠈⢻⣦⣀⠀⢀⣾⠟⠉⠀⠀⠉⠻⣷⣄⠀⠀⠀⠀⠀⠙⢷⡄⠀⠀⠀⠉⠀⣠⡟⠀⣼⣟⠀⠀⢸
|
|
19
|
-
⢸⠀⠘⣧⠀⡴⠛⠳⢸⢰⠁⢰⠏⠀⠀⠀⢀⣼⡯⠟⠋⠙⠻⣷⡀⠘⠀⠀⠀⠈⠉⠻⣿⠁⠀⠀⢰⡟⠉⠀⠈⢻⣦⠀⠀⠀⣄⠀⠀⡗⠀⢸⡇⣠⣾⠟⢀⣾⠋⠹⣷⢀⡇
|
|
20
|
-
⠈⢆⠀⠹⢷⣤⣀⣠⠎⡇⠀⠸⠀⠀⢀⣴⠟⠉⠀⠀⠀⢄⠀⠹⣧⡀⠀⠀⣀⡀⠀⠀⣿⠀⠀⠀⠘⣿⡄⠀⠀⠀⢹⣦⡀⠀⢿⣄⠀⢀⣠⡿⠽⣯⡁⠀⠸⠃⠀⠀⡏⠉⠀
|
|
21
|
-
⠀⢠⢷⣄⠀⠈⣉⣉⢢⢳⡀⠀⠀⠀⣾⡏⠀⠀⠠⣀⡤⢿⠀⠀⠙⠷⠶⠛⠉⠈⠀⣰⠟⠀⠀⠀⠀⠘⣷⡀⠀⠠⠛⠉⠉⠀⢈⣯⠗⠛⠁⠀⠀⠈⠃⠀⢀⣴⠇⢠⠇⠀⠀
|
|
22
|
-
⠀⢸⡀⠻⣧⠈⠉⠹⣏⢀⣑⠤⣀⣀⠼⠳⣄⠀⠀⠀⠙⠺⠖⣦⣤⠤⣀⡀⠀⠀⠘⠁⠀⠀⠀⠀⠀⠀⢸⢧⡀⠀⠀⢀⣀⢴⣿⣅⡀⠠⠶⢿⢦⣀⣠⣴⠟⠃⡠⠋⠀⠀⠀
|
|
23
|
-
⠀⠀⠳⡀⠘⠃⠀⡤⠸⣼⠀⠉⠛⠋⠉⠉⠙⠻⣦⣄⠀⠀⠀⠀⠈⠉⠙⠻⣦⠀⠀⠀⠀⡀⠀⠀⠀⢀⣾⠖⠚⠛⠛⠛⠋⠁⠀⠙⣷⠀⠀⣸⡴⠛⠉⢁⡤⠊⠁⠀⠀⠀⠀
|
|
24
|
-
⠀⠀⠀⠘⢦⡀⠸⣧⠀⢻⢇⠀⠳⡤⣤⠆⠀⠀⠈⢻⡇⠀⠀⠀⢰⡄⠀⠀⣿⡇⢀⡾⠛⠛⠻⡝⣲⠟⠋⠀⢀⡄⠀⠀⠀⣀⡄⠀⠋⢀⡴⣻⡄⣤⡶⡍⠀⠀⠀⠀⠀⠀⠀
|
|
25
|
-
⠀⠀⠀⠀⠀⠈⠙⠁⠉⠉⠈⠣⡀⠹⣇⠀⠀⠀⠀⠘⠀⠀⠀⢀⡾⢳⡶⠾⠋⠀⠈⠃⠀⠀⣠⠟⢄⣀⣠⡴⠋⠀⠀⠀⣼⢻⣤⣴⠶⠟⠋⣡⡷⣏⢿⡧⠀⠀⠀⠀⠀⠀⠀
|
|
26
|
-
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠓⢫⣲⣤⣀⣀⣀⣀⣤⣶⣻⠋⠛⠷⣦⣤⣤⣄⡤⢤⣺⠕⠋⠉⠉⠁⠀⠀⣀⣤⣾⠏⢩⠀⠀⢀⣤⣾⠛⣧⢻⣼⠀⠀⠀⠀⠀⠀⠀⠀
|
|
27
|
-
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠓⠮⢭⣉⣉⡩⠥⠚⠈⢇⠀⢠⡄⠀⠉⠉⠙⣿⠀⢠⠶⠖⢫⣩⠟⠛⠛⠉⠀⣠⣿⣦⠶⠿⣭⣸⣇⡿⠞⠁⠀⠀⠀⠀⠀⠀⠀⠀
|
|
28
|
-
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⣌⡿⣄⠀⠒⠚⠋⠀⠀⠀⣠⡾⠃⠀⢀⣀⠴⠚⠉⠣⢍⣛⣶⡶⠝⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
|
29
|
-
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠒⠂⠀⠒⠒⠉⠀⠉⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
|
30
|
-
|
|
5
|
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⠤⠤⠤⠤⣄⠀⠀⠀⠀⠀⠀⢀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
|
6
|
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣶⡊⠉⠉⣉⣱⡷⠶⢢⣠⢴⣶⡝⠒⠉⢉⣭⡽⠟⢉⣀⡀⠹⢭⠒⢤⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
|
7
|
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡠⠔⢚⣩⡽⠿⠊⢉⣉⡂⣀⣩⠭⢴⠟⠋⠉⠉⠉⠛⠳⢦⣬⣤⡴⠞⠛⠁⠛⠳⣾⣧⠀⠟⠀⠉⠲⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
|
8
|
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⢚⠁⠀⠰⠋⢡⠄⠀⠞⣫⢟⡥⠒⠉⠹⣿⡀⠀⠀⢦⡀⠀⠀⠀⠈⠻⡧⡀⠀⠀⠀⠀⠈⠻⣗⡶⠶⠶⢤⡀⠱⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀
|
|
9
|
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢮⣤⡾⠀⠀⣠⡴⠋⠀⡠⣚⠥⠒⢛⡲⠄⠀⠈⢻⡆⠀⠀⠻⣦⣀⠀⠀⠀⣿⠻⣦⣀⣴⠶⠂⠀⠘⣷⡄⠀⠀⢀⣴⡿⠈⠢⡀⠀⠀⠀⠀⠀⠀
|
|
10
|
+
⠀⠀⠀⠀⠀⠀⠀⡠⠖⣉⣁⡀⠀⢀⣾⠋⠴⢿⣽⠋⠀⠞⢉⣉⣽⣳⣄⣀⠀⠋⠀⠀⠀⠈⠙⣷⡄⠀⠁⠀⠙⢤⣯⡀⠀⠀⠀⣼⡇⠀⡾⠋⠁⠀⠳⣄⠘⢆⠀⠀⠀⠀⠀
|
|
11
|
+
⠀⠀⠀⠀⠀⡰⠋⠰⠛⢻⡞⢉⣠⣼⡇⢀⣴⠟⠛⠒⣴⠟⠋⠉⠀⠀⢀⣀⣀⡀⠀⠀⠀⠀⠀⣸⡇⠀⠀⠳⣄⠀⠉⢿⣄⠀⢰⣿⣧⡀⠀⣴⠶⠶⣦⡼⢧⠈⢣⠀⠀⠀⠀
|
|
12
|
+
⠀⠀⠀⢀⡞⣡⣶⡄⠀⡟⣳⠿⠋⠙⡍⡽⠁⣀⣤⣤⣿⡄⠀⠀⠀⠀⡿⡉⣀⣀⣤⣤⣤⣴⠾⠥⠽⣦⣄⠀⠉⠻⢶⡼⢻⠀⠈⠇⠘⡷⡄⠘⠂⠀⢀⡍⠻⣷⣄⡇⠀⠀⠀
|
|
13
|
+
⠀⠀⠀⠘⣺⠏⢸⢃⣼⠟⢁⡤⠀⣠⢟⡷⠟⢋⣉⣤⡿⠇⠀⠀⠀⢰⣣⠞⠋⠉⠉⠁⡀⠀⠀⠀⠀⠀⠙⢷⣄⠀⠀⢹⣾⠀⠀⠀⠀⢸⡇⠀⣀⡀⣾⠀⠀⠈⢻⡁⢦⠀⠀
|
|
14
|
+
⠀⠀⣠⢚⣵⣄⠈⣼⡇⠀⢸⠧⢞⡵⠋⠠⠚⠉⠉⠀⠀⢀⡇⠀⣰⣟⣁⣀⠀⠀⠀⠀⠉⠒⠶⣤⣤⣀⠀⠀⠙⠀⠀⢸⡇⢰⣟⠛⢶⡋⣇⠀⠉⠻⡟⡄⠀⢀⢀⣿⠀⢧⠀
|
|
15
|
+
⠀⣰⠃⢸⠁⣿⠀⠸⣧⠀⢸⢣⠋⠀⣠⣤⠶⢶⢒⣤⣔⣻⠣⢼⠟⠁⠀⠙⢷⡄⠀⠀⢀⠀⠀⠀⠈⠓⢟⢦⠀⠀⠀⢸⡇⠈⠻⣦⡀⠈⠻⣷⣄⠀⠘⣿⠀⠸⣿⣇⣀⢸⠀
|
|
16
|
+
⠀⡇⠀⠀⣼⠇⠀⢀⣿⠀⣇⣇⣴⠟⠋⢠⣾⠟⠉⠀⠀⠈⠳⣼⠀⠀⠀⠀⠀⠳⠀⠀⠈⢳⣄⠀⠀⠀⢸⣼⠀⠀⠀⠈⡟⢆⠀⠈⢻⡀⠀⠈⢻⣆⠀⣻⠃⠀⠀⢹⡟⠻⡀
|
|
17
|
+
⠀⢧⡆⣼⠏⠀⣾⠟⠁⢰⠃⡵⠃⢀⣴⡿⠁⠀⡀⠀⠀⠀⠀⠹⣧⡀⠰⣦⡀⠀⠀⠀⠀⠀⣻⢦⣀⣠⡾⣇⠀⠀⢀⣰⠟⠙⢷⣄⠀⠀⠀⠀⠀⣿⠀⠉⢠⠄⠀⣼⡇⠀⢧
|
|
18
|
+
⢀⠞⢡⡟⠀⠀⣿⠀⢀⡏⡼⠁⣴⠟⠁⠀⠀⠀⣿⠀⣀⣀⢀⣴⠘⣷⡀⠈⢻⣦⣀⠀⢀⣾⠟⠉⠀⠀⠉⠻⣷⣄⠀⠀⠀⠀⠀⠙⢷⡄⠀⠀⠀⠉⠀⣠⡟⠀⣼⣟⠀⠀⢸
|
|
19
|
+
⢸⠀⠘⣧⠀⡴⠛⠳⢸⢰⠁⢰⠏⠀⠀⠀⢀⣼⡯⠟⠋⠙⠻⣷⡀⠘⠀⠀⠀⠈⠉⠻⣿⠁⠀⠀⢰⡟⠉⠀⠈⢻⣦⠀⠀⠀⣄⠀⠀⡗⠀⢸⡇⣠⣾⠟⢀⣾⠋⠹⣷⢀⡇
|
|
20
|
+
⠈⢆⠀⠹⢷⣤⣀⣠⠎⡇⠀⠸⠀⠀⢀⣴⠟⠉⠀⠀⠀⢄⠀⠹⣧⡀⠀⠀⣀⡀⠀⠀⣿⠀⠀⠀⠘⣿⡄⠀⠀⠀⢹⣦⡀⠀⢿⣄⠀⢀⣠⡿⠽⣯⡁⠀⠸⠃⠀⠀⡏⠉⠀
|
|
21
|
+
⠀⢠⢷⣄⠀⠈⣉⣉⢢⢳⡀⠀⠀⠀⣾⡏⠀⠀⠠⣀⡤⢿⠀⠀⠙⠷⠶⠛⠉⠈⠀⣰⠟⠀⠀⠀⠀⠘⣷⡀⠀⠠⠛⠉⠉⠀⢈⣯⠗⠛⠁⠀⠀⠈⠃⠀⢀⣴⠇⢠⠇⠀⠀
|
|
22
|
+
⠀⢸⡀⠻⣧⠈⠉⠹⣏⢀⣑⠤⣀⣀⠼⠳⣄⠀⠀⠀⠙⠺⠖⣦⣤⠤⣀⡀⠀⠀⠘⠁⠀⠀⠀⠀⠀⠀⢸⢧⡀⠀⠀⢀⣀⢴⣿⣅⡀⠠⠶⢿⢦⣀⣠⣴⠟⠃⡠⠋⠀⠀⠀
|
|
23
|
+
⠀⠀⠳⡀⠘⠃⠀⡤⠸⣼⠀⠉⠛⠋⠉⠉⠙⠻⣦⣄⠀⠀⠀⠀⠈⠉⠙⠻⣦⠀⠀⠀⠀⡀⠀⠀⠀⢀⣾⠖⠚⠛⠛⠛⠋⠁⠀⠙⣷⠀⠀⣸⡴⠛⠉⢁⡤⠊⠁⠀⠀⠀⠀
|
|
24
|
+
⠀⠀⠀⠘⢦⡀⠸⣧⠀⢻⢇⠀⠳⡤⣤⠆⠀⠀⠈⢻⡇⠀⠀⠀⢰⡄⠀⠀⣿⡇⢀⡾⠛⠛⠻⡝⣲⠟⠋⠀⢀⡄⠀⠀⠀⣀⡄⠀⠋⢀⡴⣻⡄⣤⡶⡍⠀⠀⠀⠀⠀⠀⠀
|
|
25
|
+
⠀⠀⠀⠀⠀⠈⠙⠁⠉⠉⠈⠣⡀⠹⣇⠀⠀⠀⠀⠘⠀⠀⠀⢀⡾⢳⡶⠾⠋⠀⠈⠃⠀⠀⣠⠟⢄⣀⣠⡴⠋⠀⠀⠀⣼⢻⣤⣴⠶⠟⠋⣡⡷⣏⢿⡧⠀⠀⠀⠀⠀⠀⠀
|
|
26
|
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠓⢫⣲⣤⣀⣀⣀⣀⣤⣶⣻⠋⠛⠷⣦⣤⣤⣄⡤⢤⣺⠕⠋⠉⠉⠁⠀⠀⣀⣤⣾⠏⢩⠀⠀⢀⣤⣾⠛⣧⢻⣼⠀⠀⠀⠀⠀⠀⠀⠀
|
|
27
|
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠓⠮⢭⣉⣉⡩⠥⠚⠈⢇⠀⢠⡄⠀⠉⠉⠙⣿⠀⢠⠶⠖⢫⣩⠟⠛⠛⠉⠀⣠⣿⣦⠶⠿⣭⣸⣇⡿⠞⠁⠀⠀⠀⠀⠀⠀⠀⠀
|
|
28
|
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⣌⡿⣄⠀⠒⠚⠋⠀⠀⠀⣠⡾⠃⠀⢀⣀⠴⠚⠉⠣⢍⣛⣶⡶⠝⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
|
29
|
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠒⠂⠀⠒⠒⠉⠀⠉⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
|
30
|
+
|
|
31
31
|
FDP Agent Memory - By Future Development Program Kft.`;
|
|
@@ -48,7 +48,7 @@ class FAM_AutoCapture_ControlService {
|
|
|
48
48
|
const contentHash = ingest_1.FAM_ContentHash_Util.hash(content);
|
|
49
49
|
// Dedup: van-e MÁR aktív memory-bejegyzés azonos content-hash-sel? → idempotens skip (nincs duplikátum).
|
|
50
50
|
const existing = await this.findByHash(contentHash);
|
|
51
|
-
if (existing
|
|
51
|
+
if (existing?._id) {
|
|
52
52
|
return { captured: false, skipped: 'duplicate', id: existing._id, contentHash: contentHash };
|
|
53
53
|
}
|
|
54
54
|
// Új capture → a `write` create-úton (agent-provenance, auto-embed). A `source.path` a sessionId
|
|
@@ -215,7 +215,7 @@ class FAM_EmbeddingPipeline_ControlService {
|
|
|
215
215
|
}
|
|
216
216
|
/** A scope-szűkítő filter (a `scopePath` LEVÉL scopeId-jára). Üres scope → teljes tár (`!_deleted`). */
|
|
217
217
|
buildScopeFilter(scopePath) {
|
|
218
|
-
if (scopePath
|
|
218
|
+
if (scopePath?.length) {
|
|
219
219
|
const leaf = scopePath[scopePath.length - 1];
|
|
220
220
|
const filter = { 'scopePath.scopeId': leaf.scopeId };
|
|
221
221
|
return filter;
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.FAM_Entry_DataService = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
-
const nts_dynamo_1 = require("@futdevpro/nts-dynamo");
|
|
6
5
|
const mongoose_1 = tslib_1.__importDefault(require("mongoose"));
|
|
6
|
+
const nts_dynamo_1 = require("@futdevpro/nts-dynamo");
|
|
7
7
|
const fam_entry_data_model_1 = require("../../../_models/data-models/fam-entry.data-model");
|
|
8
8
|
/**
|
|
9
9
|
* `FAM_Entry_DataService` (SP-2.2) — egy GENERIKUS, tár-paraméteres entry-CRUD a vektor-rétegnek.
|
|
@@ -195,5 +195,27 @@ class FAM_Entry_DataService extends nts_dynamo_1.DyNTS_DataService {
|
|
|
195
195
|
filterBy['position.charStart'] = { $ne: position.charStart };
|
|
196
196
|
await this.updateData({ filterBy: filterBy, update: update });
|
|
197
197
|
}
|
|
198
|
+
/**
|
|
199
|
+
* A **git-repo provenance** (`source.repoUrl` / `repoName` / `repoRelativePath` / `repoBranch`) FELTÉTELES
|
|
200
|
+
* backfill-je (user-FR 2026-06-22) — a re-scan `equal` (változatlan-content) ágán hívva, hogy a CSAK-metaadat
|
|
201
|
+
* frissítés (a git-provenance feature ELŐTT ingestelt korpuszra) RE-EMBED NÉLKÜL megtörténjen. A hívó (ingest)
|
|
202
|
+
* adja a kiszámolt `git`-mezőket (a `FAM_GitRepo_Util`-t a control-service importálja — nincs modul-ciklus). Nem
|
|
203
|
+
* git-repo (nincs `repoUrl`) → no-op. Feltétel: a per-fájl `repoRelativePath` `$ne` (a Mongo no-op, ha már stimmel).
|
|
204
|
+
*/
|
|
205
|
+
async backfillGitProvenance(id, git) {
|
|
206
|
+
if (!git.repoUrl) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const setBlock = {
|
|
210
|
+
['source.repoUrl']: git.repoUrl,
|
|
211
|
+
['source.repoName']: git.repoName,
|
|
212
|
+
['source.repoRelativePath']: git.repoRelativePath,
|
|
213
|
+
['source.repoBranch']: git.repoBranch,
|
|
214
|
+
};
|
|
215
|
+
const update = { $set: setBlock };
|
|
216
|
+
const filterBy = { _id: id };
|
|
217
|
+
filterBy['source.repoRelativePath'] = { $ne: git.repoRelativePath };
|
|
218
|
+
await this.updateData({ filterBy: filterBy, update: update });
|
|
219
|
+
}
|
|
198
220
|
}
|
|
199
221
|
exports.FAM_Entry_DataService = FAM_Entry_DataService;
|
|
@@ -373,6 +373,10 @@ class FAM_Ingest_ControlService {
|
|
|
373
373
|
// re-embed nélkül (a friss chunk charStart/charEnd/sor-pozíciója felülír, ha eltér).
|
|
374
374
|
await set.dataService.backfillPosition(item.existingId, item.chunk.position);
|
|
375
375
|
}
|
|
376
|
+
// Git-provenance backfill (user-FR 2026-06-22): a CSAK-metaadat frissítés — a git-provenance
|
|
377
|
+
// feature ELŐTT ingestelt korpusz `equal`-chunkjai is megkapják a repoUrl/repoRelativePath/
|
|
378
|
+
// repoBranch-et RE-EMBED NÉLKÜL (a control-service számolja, így nincs data-service↔ingest ciklus).
|
|
379
|
+
await set.dataService.backfillGitProvenance(item.existingId, fam_git_repo_util_1.FAM_GitRepo_Util.sourceFieldsFor(set.file.absolutePath));
|
|
376
380
|
}
|
|
377
381
|
catch {
|
|
378
382
|
// best-effort metaadat-frissítés — a content már naprakész (equal), nem eszkaláljuk.
|
|
@@ -479,7 +483,7 @@ class FAM_Ingest_ControlService {
|
|
|
479
483
|
const existing = await dataService.findHydratableList(filter);
|
|
480
484
|
const existingEntry = existing[0];
|
|
481
485
|
// Változatlan fájl-lista → NO-OP (nincs fölös re-embed).
|
|
482
|
-
if (existingEntry
|
|
486
|
+
if (existingEntry?.contentHash === contentHash) {
|
|
483
487
|
return;
|
|
484
488
|
}
|
|
485
489
|
const entry = new fam_entry_data_model_1.FAM_Entry({
|
|
@@ -25,7 +25,7 @@ const MAIN_TABLE_ENUM = Object.values(fam_table_type_enum_1.FAM_Table).filter((t
|
|
|
25
25
|
/** A nyers scope-lánc array-schema (`{ layer, rawName }[]`). */
|
|
26
26
|
const SCOPE_LAYER_ARRAY = {
|
|
27
27
|
type: 'array',
|
|
28
|
-
description: 'Nyers scope-lánc (root→leaf): [{ layer, rawName }] párok (pl. organization=FutDevPro, project=Adventor).',
|
|
28
|
+
description: 'Nyers scope-lánc (root→leaf): [{ layer: layer, rawName: rawName }] párok (pl. organization=FutDevPro, project=Adventor).',
|
|
29
29
|
items: {
|
|
30
30
|
type: 'object',
|
|
31
31
|
properties: {
|
|
@@ -347,7 +347,7 @@ class FAM_CapabilityRegistry_Service {
|
|
|
347
347
|
});
|
|
348
348
|
this.register({
|
|
349
349
|
name: 'export_authored', category: category,
|
|
350
|
-
description: 'FEAT-007: a non-file-derived (kézzel/agent-írt, `source.type ∈ {manual, agent}`) bejegyzések '
|
|
350
|
+
description: 'FEAT-007: a non-file-derived (kézzel/agent-írt, `source.type ∈ {manual: manual, agent: agent}`) bejegyzések '
|
|
351
351
|
+ 'exportja MINDEN tárból — a re-scan-nel NEM regenerálható, pótolhatatlan kurált érték biztonsági '
|
|
352
352
|
+ 'mentése (a `contentVector` nélkül; a hívó/CLI JSON-fájlba írja).',
|
|
353
353
|
inputSchema: this.objectSchema({}),
|
|
@@ -393,6 +393,56 @@ class FAM_CapabilityRegistry_Service {
|
|
|
393
393
|
}),
|
|
394
394
|
handler: async (args) => this.ok(await this.manageReference(this.asObject(args))),
|
|
395
395
|
});
|
|
396
|
+
this.register({
|
|
397
|
+
name: 'rename_scope', category: category,
|
|
398
|
+
description: 'Egy scope ÁTNEVEZÉSE (user-FR): a `canonicalName`-t a denormalizált scopePath-okon (MINDEN '
|
|
399
|
+
+ 'RAG-entry) + a reference-en + a scope-doc-on bulk-ban átírja (a scopeId VÁLTOZATLAN). A scope '
|
|
400
|
+
+ 'megadható `scopeId`-val VAGY `canonicalName`(+`layer`)-rel. **`dryRun:true` (DEFAULT) csak az érintett-'
|
|
401
|
+
+ 'számot adja, NEM ír**; a tényleges íráshoz `dryRun:false` explicit. Nagy átírás után szerver-restart '
|
|
402
|
+
+ 'ajánlott a LVS-pool friss scope-metaadatához.',
|
|
403
|
+
inputSchema: this.objectSchema({
|
|
404
|
+
scopeId: { type: 'string', description: 'A scope _id-ja (egyértelmű azonosítás).' },
|
|
405
|
+
layer: { type: 'string', description: 'Opcionális layer a canonicalName egyértelműsítéséhez.' },
|
|
406
|
+
canonicalName: { type: 'string', description: 'A scope JELENLEGI neve (scopeId helyett).' },
|
|
407
|
+
newName: { type: 'string', description: 'Az ÚJ canonicalName — KÖTELEZŐ.' },
|
|
408
|
+
dryRun: { type: 'boolean', description: 'true (default): csak preview; false: ír.' },
|
|
409
|
+
}),
|
|
410
|
+
handler: async (args) => {
|
|
411
|
+
const input = this.asObject(args);
|
|
412
|
+
return this.ok(await scope_reference_1.FAM_ScopeMaintenance_ControlService.getInstance().renameScope({
|
|
413
|
+
scopeId: this.asString(input.scopeId),
|
|
414
|
+
layer: this.asString(input.layer),
|
|
415
|
+
canonicalName: this.asString(input.canonicalName),
|
|
416
|
+
newName: this.asString(input.newName) ?? '',
|
|
417
|
+
dryRun: input.dryRun !== false,
|
|
418
|
+
}));
|
|
419
|
+
},
|
|
420
|
+
});
|
|
421
|
+
this.register({
|
|
422
|
+
name: 'merge_scopes', category: category,
|
|
423
|
+
description: 'Két scope ÖSSZEOLVASZTÁSA (user-FR): a `from` scope MINDEN entry-je az `into` scope-ra mutat '
|
|
424
|
+
+ '(scopePath + reference átírva), majd a `from` scope soft-delete (az `into` MARAD). from/into megadható '
|
|
425
|
+
+ '`...ScopeId`-val VAGY `...Name`(+`...Layer`)-rel. **`dryRun:true` (DEFAULT) csak az érintett-számot, NEM '
|
|
426
|
+
+ 'ír**; `dryRun:false` ír. NEM dedup-ol (a content-hash-összevonás a `deduplicate_entries`). Nagy merge '
|
|
427
|
+
+ 'után szerver-restart ajánlott.',
|
|
428
|
+
inputSchema: this.objectSchema({
|
|
429
|
+
fromScopeId: { type: 'string', description: 'A FORRÁS scope _id-ja.' },
|
|
430
|
+
fromName: { type: 'string', description: 'A forrás scope neve (scopeId helyett).' },
|
|
431
|
+
fromLayer: { type: 'string', description: 'Opcionális forrás-layer az egyértelműsítéshez.' },
|
|
432
|
+
intoScopeId: { type: 'string', description: 'A CÉL scope _id-ja (ebbe olvad).' },
|
|
433
|
+
intoName: { type: 'string', description: 'A cél scope neve (scopeId helyett).' },
|
|
434
|
+
intoLayer: { type: 'string', description: 'Opcionális cél-layer az egyértelműsítéshez.' },
|
|
435
|
+
dryRun: { type: 'boolean', description: 'true (default): csak preview; false: ír.' },
|
|
436
|
+
}),
|
|
437
|
+
handler: async (args) => {
|
|
438
|
+
const input = this.asObject(args);
|
|
439
|
+
return this.ok(await scope_reference_1.FAM_ScopeMaintenance_ControlService.getInstance().mergeScopes({
|
|
440
|
+
from: { scopeId: this.asString(input.fromScopeId), canonicalName: this.asString(input.fromName), layer: this.asString(input.fromLayer) },
|
|
441
|
+
into: { scopeId: this.asString(input.intoScopeId), canonicalName: this.asString(input.intoName), layer: this.asString(input.intoLayer) },
|
|
442
|
+
dryRun: input.dryRun !== false,
|
|
443
|
+
}));
|
|
444
|
+
},
|
|
445
|
+
});
|
|
396
446
|
// repair_scope_links / deduplicate_entries (MP-12) — DRY-RUN-ONLY MVP1 (safe). A destruktív
|
|
397
447
|
// write-ág (entry-mutáció / soft-delete-összevonás) BACKLOG: vak destruktív bulk-op TILOS
|
|
398
448
|
// (dsgn-003 §4.2), ezért a `dryRun:false` kérés clear "BACKLOG" hibát ad — a `dryRun:true` (default)
|
|
@@ -457,7 +507,7 @@ class FAM_CapabilityRegistry_Service {
|
|
|
457
507
|
this.register({
|
|
458
508
|
name: 'import_claude_mem', category: category,
|
|
459
509
|
description: 'A claude-mem migráció tényleges beírása egy import-batch (batchId) alatt: dedup-skip + '
|
|
460
|
-
+ 'map + persist + embed. Output: { ok, batchId, ingested, skipped, failed, byTable, status } (dsgn-009 §5.3).',
|
|
510
|
+
+ 'map + persist + embed. Output: { ok: ok, batchId: batchId, ingested: ingested, skipped: skipped, failed: failed, byTable: byTable, status: status } (dsgn-009 §5.3).',
|
|
461
511
|
inputSchema: this.importMapSchema(), handler: runHandler,
|
|
462
512
|
});
|
|
463
513
|
this.register({
|
|
@@ -556,7 +606,10 @@ class FAM_CapabilityRegistry_Service {
|
|
|
556
606
|
// =========================================================================
|
|
557
607
|
// Wired handler-helpers (delegálás az engine-ekre)
|
|
558
608
|
// =========================================================================
|
|
559
|
-
/**
|
|
609
|
+
/**
|
|
610
|
+
* Per-tár statisztika (aktív elem-szám + embedding-státusz bontás) — MP-1.
|
|
611
|
+
* FIX (OOM): `countDocuments` + `$group`-aggregáció — NEM tölti a doc-okat memóriába (a 126k-vektoros load OOM-ozott).
|
|
612
|
+
*/
|
|
560
613
|
async tableStats(table) {
|
|
561
614
|
const tables = table ? [table] : MAIN_TABLES;
|
|
562
615
|
const result = [];
|
|
@@ -565,14 +618,22 @@ class FAM_CapabilityRegistry_Service {
|
|
|
565
618
|
if (!registryEntry) {
|
|
566
619
|
continue;
|
|
567
620
|
}
|
|
568
|
-
const
|
|
569
|
-
const
|
|
621
|
+
const collection = this.entryCollection(registryEntry);
|
|
622
|
+
const count = await collection.countDocuments({});
|
|
570
623
|
const byStatus = { pending: 0, completed: 0, error: 0 };
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
624
|
+
const grouped = await collection
|
|
625
|
+
.aggregate([{ $group: { _id: '$embeddingStatus', n: { $sum: 1 } } }])
|
|
626
|
+
.toArray();
|
|
627
|
+
for (const group of grouped) {
|
|
628
|
+
const status = group._id ?? 'pending';
|
|
629
|
+
if (status in byStatus) {
|
|
630
|
+
byStatus[status] += group.n;
|
|
631
|
+
}
|
|
632
|
+
else {
|
|
633
|
+
byStatus.pending += group.n;
|
|
634
|
+
}
|
|
574
635
|
}
|
|
575
|
-
result.push({ table: target, count:
|
|
636
|
+
result.push({ table: target, count: count, embeddingStatus: byStatus });
|
|
576
637
|
}
|
|
577
638
|
return table ? result[0] ?? { table: table, count: 0 } : result;
|
|
578
639
|
}
|
|
@@ -618,7 +679,7 @@ class FAM_CapabilityRegistry_Service {
|
|
|
618
679
|
/** Egy env-változó jelenlét-ellenőrzése (`treatDefaultAsPresent`: van builtin default, ezért sosem hiányzik). */
|
|
619
680
|
checkEnv(name, hint, present, missing, treatDefaultAsPresent = false) {
|
|
620
681
|
const value = process.env[name];
|
|
621
|
-
if ((value
|
|
682
|
+
if ((value?.trim().length) || treatDefaultAsPresent) {
|
|
622
683
|
present.push(name);
|
|
623
684
|
return;
|
|
624
685
|
}
|
|
@@ -750,11 +811,10 @@ class FAM_CapabilityRegistry_Service {
|
|
|
750
811
|
*/
|
|
751
812
|
async storageUsage() {
|
|
752
813
|
const collections = [];
|
|
753
|
-
// Fő RAG-tárak (a
|
|
814
|
+
// Fő RAG-tárak — FIX (OOM): `countDocuments` (NEM a doc-ok memóriába töltése; a 126k-vektoros load OOM-ozott).
|
|
754
815
|
for (const registryEntry of embedding_1.FAM_STORE_REGISTRY) {
|
|
755
|
-
const
|
|
756
|
-
|
|
757
|
-
collections.push({ collection: this.realCollectionName(registryEntry.dataParams.dataName), activeDocs: entries.length });
|
|
816
|
+
const activeDocs = await this.entryCollection(registryEntry).countDocuments({});
|
|
817
|
+
collections.push({ collection: this.realCollectionName(registryEntry.dataParams.dataName), activeDocs: activeDocs });
|
|
758
818
|
}
|
|
759
819
|
// Helper collection-ök (reference / scope / ingest-run / config) — a DS-példányosítás regisztrálja a modellt.
|
|
760
820
|
const reference_DS = new scope_reference_1.FAM_Reference_DataService({ issuer: this.issuer });
|
|
@@ -865,21 +925,24 @@ class FAM_CapabilityRegistry_Service {
|
|
|
865
925
|
const tables = table ? [table] : MAIN_TABLES;
|
|
866
926
|
const result = [];
|
|
867
927
|
for (const target of tables) {
|
|
868
|
-
const
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
for (const entry of entries) {
|
|
872
|
-
if (entry.kind) {
|
|
873
|
-
kindCounts.set(entry.kind, (kindCounts.get(entry.kind) ?? 0) + 1);
|
|
874
|
-
}
|
|
875
|
-
for (const tag of entry.tags ?? []) {
|
|
876
|
-
tagCounts.set(tag, (tagCounts.get(tag) ?? 0) + 1);
|
|
877
|
-
}
|
|
928
|
+
const registryEntry = embedding_1.FAM_STORE_REGISTRY.find((entry) => entry.table === target);
|
|
929
|
+
if (!registryEntry) {
|
|
930
|
+
continue;
|
|
878
931
|
}
|
|
932
|
+
// FIX (OOM): Mongo-aggregáció ($group kind / $unwind+$group tag) — NEM tölti a doc-okat memóriába.
|
|
933
|
+
const collection = this.entryCollection(registryEntry);
|
|
934
|
+
const kindAgg = await collection
|
|
935
|
+
.aggregate([
|
|
936
|
+
{ $match: { kind: { $ne: null } } }, { $group: { _id: '$kind', n: { $sum: 1 } } }, { $sort: { n: -1 } },
|
|
937
|
+
]).toArray();
|
|
938
|
+
const tagAgg = await collection
|
|
939
|
+
.aggregate([
|
|
940
|
+
{ $unwind: '$tags' }, { $group: { _id: '$tags', n: { $sum: 1 } } }, { $sort: { n: -1 } },
|
|
941
|
+
]).toArray();
|
|
879
942
|
result.push({
|
|
880
943
|
table: target,
|
|
881
|
-
kinds:
|
|
882
|
-
tags:
|
|
944
|
+
kinds: kindAgg.filter((group) => group._id).map((group) => ({ kind: group._id, count: group.n })),
|
|
945
|
+
tags: tagAgg.filter((group) => group._id).map((group) => ({ tag: group._id, count: group.n })),
|
|
883
946
|
});
|
|
884
947
|
}
|
|
885
948
|
return result;
|
|
@@ -890,8 +953,17 @@ class FAM_CapabilityRegistry_Service {
|
|
|
890
953
|
const tables = table ? [table] : MAIN_TABLES;
|
|
891
954
|
const collected = [];
|
|
892
955
|
for (const target of tables) {
|
|
893
|
-
const
|
|
894
|
-
|
|
956
|
+
const registryEntry = embedding_1.FAM_STORE_REGISTRY.find((entry) => entry.table === target);
|
|
957
|
+
if (!registryEntry) {
|
|
958
|
+
continue;
|
|
959
|
+
}
|
|
960
|
+
// FIX (OOM): per-tár BOUNDED query (`sort __created` + `limit`), NEM a teljes tár betöltése N elemért.
|
|
961
|
+
const docs = await this.entryCollection(registryEntry)
|
|
962
|
+
.find({}, { projection: { kind: 1, tags: 1, __created: 1, content: 1 } })
|
|
963
|
+
.sort({ __created: -1 })
|
|
964
|
+
.limit(cappedLimit)
|
|
965
|
+
.toArray();
|
|
966
|
+
for (const entry of docs) {
|
|
895
967
|
if (!entry._id) {
|
|
896
968
|
continue;
|
|
897
969
|
}
|
|
@@ -1083,14 +1155,22 @@ class FAM_CapabilityRegistry_Service {
|
|
|
1083
1155
|
// =========================================================================
|
|
1084
1156
|
// MP-12 közös entry-helperek (lazy DataService — eager-resolve elkerülés)
|
|
1085
1157
|
// =========================================================================
|
|
1086
|
-
/**
|
|
1158
|
+
/**
|
|
1159
|
+
* Egy fő tár aktív entry-i (a per-tár dataParams-szal). Nem-fő/ismeretlen tár → üres.
|
|
1160
|
+
* FIX (OOM): a nagy `contentVector` (2560 float/doc) PROJEKCIÓVAL KIHAGYVA — a fogyasztók (kinds/recent/repair/
|
|
1161
|
+
* dedup) NEM használják; e nélkül a teljes-tár betöltés ~20×-kal kisebb (a 126k vektor a heap-OOM forrása volt).
|
|
1162
|
+
*/
|
|
1087
1163
|
async loadTableEntries(table) {
|
|
1088
1164
|
const registryEntry = embedding_1.FAM_STORE_REGISTRY.find((entry) => entry.table === table);
|
|
1089
1165
|
if (!registryEntry) {
|
|
1090
1166
|
return [];
|
|
1091
1167
|
}
|
|
1092
|
-
|
|
1093
|
-
|
|
1168
|
+
return this.entryCollection(registryEntry).find({}, { projection: { contentVector: 0 } }).toArray();
|
|
1169
|
+
}
|
|
1170
|
+
/** Egy fő RAG-tár mongoose-collection-je (a DS-példányosítás regisztrálja a modellt; onnan a valós collection). */
|
|
1171
|
+
entryCollection(registryEntry) {
|
|
1172
|
+
new embedding_1.FAM_Entry_DataService({ dataParams: registryEntry.dataParams, issuer: this.issuer });
|
|
1173
|
+
return mongoose_1.default.models[registryEntry.dataParams.dataName].collection;
|
|
1094
1174
|
}
|
|
1095
1175
|
/** Egy entry feloldása `id`-ből (a megadott tárból, vagy minden fő tárból). Nincs → `FAM-MCP-DISPATCH-002`. */
|
|
1096
1176
|
async requireEntryById(id, table, capability) {
|
|
@@ -1110,7 +1190,7 @@ class FAM_CapabilityRegistry_Service {
|
|
|
1110
1190
|
}
|
|
1111
1191
|
const dataService = new embedding_1.FAM_Entry_DataService({ dataParams: registryEntry.dataParams, issuer: this.issuer });
|
|
1112
1192
|
const entry = await dataService.findData({ _id: id }, true);
|
|
1113
|
-
if (entry
|
|
1193
|
+
if (entry?._id) {
|
|
1114
1194
|
return entry;
|
|
1115
1195
|
}
|
|
1116
1196
|
}
|
|
@@ -1190,7 +1270,7 @@ class FAM_CapabilityRegistry_Service {
|
|
|
1190
1270
|
scopeLayerArraySchema() {
|
|
1191
1271
|
return {
|
|
1192
1272
|
type: 'array',
|
|
1193
|
-
description: 'Nyers scope-lánc (root→leaf): [{ layer, rawName }] párok.',
|
|
1273
|
+
description: 'Nyers scope-lánc (root→leaf): [{ layer: layer, rawName: rawName }] párok.',
|
|
1194
1274
|
items: this.objectSchema({
|
|
1195
1275
|
layer: { type: 'string', description: 'A dinamikus layer-név (pl. organization/project).' },
|
|
1196
1276
|
rawName: { type: 'string', description: 'A nyers (feloldatlan) scope-név.' },
|
|
@@ -98,7 +98,7 @@ class FAM_ReadTool_Service {
|
|
|
98
98
|
throw fam_error_factory_util_1.FAM_Error_Util.create({
|
|
99
99
|
errorCode: error_codes_const_1.FAM_ERROR_CODES.valReadEmptyOrReference,
|
|
100
100
|
message: 'A `read` legalább egy query-t igényel (`queries[]`). Adj meg legalább egy '
|
|
101
|
-
+ '{ tables, query } blokkot.',
|
|
101
|
+
+ '{ tables: tables, query: query } blokkot.',
|
|
102
102
|
issuer: this.issuer,
|
|
103
103
|
context: { operation: 'read-validate' },
|
|
104
104
|
});
|
|
@@ -417,7 +417,7 @@ class FAM_ClaudeMemImport_ControlService {
|
|
|
417
417
|
}
|
|
418
418
|
const overrideKey = `project:${record.project}`;
|
|
419
419
|
const override = request.overrides?.scope?.[overrideKey];
|
|
420
|
-
if (override
|
|
420
|
+
if (override?.scopePath.length) {
|
|
421
421
|
return override.scopePath.map((layer) => ({ layer: layer.layer, rawName: layer.rawName }));
|
|
422
422
|
}
|
|
423
423
|
return [{ layer: PROJECT_SCOPE_LAYER, rawName: record.project }];
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FAM_ScopeMaintenance_Util = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* `FAM_ScopeMaintenance_Util` (user-FR 2026-06-22) — a **scope rename/merge** Mongo-update-specjeinek
|
|
6
|
+
* TISZTA (pure) felépítője. A scope-azonosító a denormalizált `scopePath[]`-en (minden RAG-entry) ÉS a
|
|
7
|
+
* reference `canonicalScopeRef`-jén (egyetlen objektum) szerepel; a rename/merge ezeket írja át bulk-ban.
|
|
8
|
+
*
|
|
9
|
+
* **Rename** = a `scopeId`-hez tartozó `canonicalName` átírása (a scopeId marad). **Merge** = a `from`
|
|
10
|
+
* scopeId minden előfordulása az `into` scope `{scopeId, layer, canonicalName}`-jére cserélve (majd a
|
|
11
|
+
* hívó soft-delete-eli a `from` scope-ot). A spec-építés determinisztikus + külön tesztelhető; az
|
|
12
|
+
* alkalmazás (mongoose `updateMany`) a control-service-ben.
|
|
13
|
+
*/
|
|
14
|
+
class FAM_ScopeMaintenance_Util {
|
|
15
|
+
/** RENAME — entry-collection (`scopePath[]`): a `scopeId`-elem `canonicalName`-jét `newName`-re. */
|
|
16
|
+
static renameEntryUpdate(scopeId, newName) {
|
|
17
|
+
return {
|
|
18
|
+
filter: { 'scopePath.scopeId': scopeId },
|
|
19
|
+
update: { $set: { 'scopePath.$[el].canonicalName': newName } },
|
|
20
|
+
arrayFilters: [{ 'el.scopeId': scopeId }],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** MERGE — entry-collection (`scopePath[]`): a `fromId`-elemet az `into` ref `{scopeId,layer,canonicalName}`-jére. */
|
|
24
|
+
static mergeEntryUpdate(fromId, into) {
|
|
25
|
+
return {
|
|
26
|
+
filter: { 'scopePath.scopeId': fromId },
|
|
27
|
+
update: {
|
|
28
|
+
$set: {
|
|
29
|
+
'scopePath.$[el].scopeId': into.scopeId,
|
|
30
|
+
'scopePath.$[el].layer': into.layer,
|
|
31
|
+
'scopePath.$[el].canonicalName': into.canonicalName,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
arrayFilters: [{ 'el.scopeId': fromId }],
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/** RENAME — reference (`canonicalScopeRef`, egyetlen objektum): a `canonicalName` átírása. */
|
|
38
|
+
static renameReferenceUpdate(scopeId, newName) {
|
|
39
|
+
return {
|
|
40
|
+
filter: { 'canonicalScopeRef.scopeId': scopeId },
|
|
41
|
+
update: { $set: { 'canonicalScopeRef.canonicalName': newName } },
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** MERGE — reference (`canonicalScopeRef`): a `from` ref cseréje az `into`-ra. */
|
|
45
|
+
static mergeReferenceUpdate(fromId, into) {
|
|
46
|
+
return {
|
|
47
|
+
filter: { 'canonicalScopeRef.scopeId': fromId },
|
|
48
|
+
update: {
|
|
49
|
+
$set: {
|
|
50
|
+
'canonicalScopeRef.scopeId': into.scopeId,
|
|
51
|
+
'canonicalScopeRef.layer': into.layer,
|
|
52
|
+
'canonicalScopeRef.canonicalName': into.canonicalName,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.FAM_ScopeMaintenance_Util = FAM_ScopeMaintenance_Util;
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FAM_ScopeMaintenance_ControlService = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const mongoose_1 = tslib_1.__importDefault(require("mongoose"));
|
|
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 embedding_1 = require("../../embedding");
|
|
9
|
+
const fam_reference_data_service_1 = require("./fam-reference.data-service");
|
|
10
|
+
const fam_scope_data_service_1 = require("./fam-scope.data-service");
|
|
11
|
+
const fam_scope_normalize_util_1 = require("../_collections/fam-scope-normalize.util");
|
|
12
|
+
const fam_scope_maintenance_util_1 = require("../_collections/fam-scope-maintenance.util");
|
|
13
|
+
/**
|
|
14
|
+
* `FAM_ScopeMaintenance_ControlService` (user-FR 2026-06-22) — **scope rename + merge** maintenance-engine.
|
|
15
|
+
* A claude-mem import (és egyéb) rendetlen scope-neveit (pl. `10.3.3` → valójában ccap-revisioned) rendezi:
|
|
16
|
+
* a `canonicalName`-t a denormalizált `scopePath[]`-en (MINDEN RAG-entry) + a reference `canonicalScopeRef`-jén
|
|
17
|
+
* + a scope-doc-on bulk-ban átírja (rename), VAGY egy scope-ot egy másikba olvaszt (merge → a forrás-scope
|
|
18
|
+
* minden entry-je az `into`-ra mutat, majd a forrás-scope soft-delete).
|
|
19
|
+
*
|
|
20
|
+
* **dryRun (default true a destruktív-biztonsághoz):** csak az érintett-számokat adja vissza, NEM ír. A
|
|
21
|
+
* tényleges íráshoz `dryRun:false` explicit. A bulk array-update mongoose `updateMany` + `arrayFilters` (a spec
|
|
22
|
+
* a tiszta `FAM_ScopeMaintenance_Util`-ból). A LVS-pool a Mongo-ból hidratál → a változás a következő (vagy
|
|
23
|
+
* scope-scoped) keresésnél él; nagy átírás után szerver-restart ajánlott a pool friss scope-metaadatához.
|
|
24
|
+
*
|
|
25
|
+
* **Megjegyzés (dedup):** a merge NEM dedup-ol — ha az `into` és a `from` ugyanazt a tartalmat tartalmazta,
|
|
26
|
+
* a `deduplicate_entries` (külön capability) felel a content-hash-összevonásért.
|
|
27
|
+
*/
|
|
28
|
+
class FAM_ScopeMaintenance_ControlService {
|
|
29
|
+
static _instance;
|
|
30
|
+
issuer = 'FAM_ScopeMaintenance_ControlService';
|
|
31
|
+
static getInstance() {
|
|
32
|
+
if (!FAM_ScopeMaintenance_ControlService._instance) {
|
|
33
|
+
FAM_ScopeMaintenance_ControlService._instance = new FAM_ScopeMaintenance_ControlService();
|
|
34
|
+
}
|
|
35
|
+
return FAM_ScopeMaintenance_ControlService._instance;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Egy scope ÁTNEVEZÉSE: a `scopeId` `canonicalName`-jét `newName`-re — a scopeId VÁLTOZATLAN, csak a
|
|
39
|
+
* denormalizált név (entry-k `scopePath` + reference + scope-doc). `dryRun` → csak az érintett-szám.
|
|
40
|
+
*/
|
|
41
|
+
async renameScope(set) {
|
|
42
|
+
const newName = (set.newName ?? '').trim();
|
|
43
|
+
if (!newName) {
|
|
44
|
+
throw this.invalid('A `newName` (nem-üres string) KÖTELEZŐ a rename_scope-hoz.', 'rename_scope');
|
|
45
|
+
}
|
|
46
|
+
const scope = await this.resolveScope(set);
|
|
47
|
+
const scopeId = scope._id;
|
|
48
|
+
const dryRun = set.dryRun !== false;
|
|
49
|
+
const affected = await this.countAffected(scopeId);
|
|
50
|
+
if (!dryRun) {
|
|
51
|
+
const entryUpdate = fam_scope_maintenance_util_1.FAM_ScopeMaintenance_Util.renameEntryUpdate(scopeId, newName);
|
|
52
|
+
await this.applyEntryUpdate(entryUpdate);
|
|
53
|
+
const refUpdate = fam_scope_maintenance_util_1.FAM_ScopeMaintenance_Util.renameReferenceUpdate(scopeId, newName);
|
|
54
|
+
await this.referenceCollection().collection.updateMany(refUpdate.filter, refUpdate.update);
|
|
55
|
+
// A scope-doc canonicalName-je ($set, atomikus — a denormalizált forrás).
|
|
56
|
+
await new fam_scope_data_service_1.FAM_Scope_DataService({ issuer: this.issuer })
|
|
57
|
+
.updateData({ filterBy: { _id: scopeId }, update: { $set: { canonicalName: newName } } });
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
operation: 'rename_scope',
|
|
61
|
+
dryRun: dryRun,
|
|
62
|
+
scope: { scopeId: scopeId, layer: scope.layer ?? '', oldName: scope.canonicalName ?? '', newName: newName },
|
|
63
|
+
affected: affected,
|
|
64
|
+
totalAffected: affected.reduce((sum, item) => sum + item.count, 0),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Két scope ÖSSZEOLVASZTÁSA: a `from` scope minden entry-je az `into` scope `{scopeId,layer,canonicalName}`-jére
|
|
69
|
+
* mutat, majd a `from` scope soft-delete. `dryRun` → csak az érintett-szám. Az `into` MARAD.
|
|
70
|
+
*/
|
|
71
|
+
async mergeScopes(set) {
|
|
72
|
+
const from = await this.resolveScope(set.from);
|
|
73
|
+
const into = await this.resolveScope(set.into);
|
|
74
|
+
const fromId = from._id;
|
|
75
|
+
const intoId = into._id;
|
|
76
|
+
if (fromId === intoId) {
|
|
77
|
+
throw this.invalid('A `from` és az `into` ugyanaz a scope — nincs mit összeolvasztani.', 'merge_scopes');
|
|
78
|
+
}
|
|
79
|
+
const dryRun = set.dryRun !== false;
|
|
80
|
+
const intoRef = { scopeId: intoId, layer: into.layer ?? '', canonicalName: into.canonicalName ?? '' };
|
|
81
|
+
const affected = await this.countAffected(fromId);
|
|
82
|
+
if (!dryRun) {
|
|
83
|
+
const entryUpdate = fam_scope_maintenance_util_1.FAM_ScopeMaintenance_Util.mergeEntryUpdate(fromId, intoRef);
|
|
84
|
+
await this.applyEntryUpdate(entryUpdate);
|
|
85
|
+
const refUpdate = fam_scope_maintenance_util_1.FAM_ScopeMaintenance_Util.mergeReferenceUpdate(fromId, intoRef);
|
|
86
|
+
await this.referenceCollection().collection.updateMany(refUpdate.filter, refUpdate.update);
|
|
87
|
+
// A forrás-scope soft-delete (move-to-archive) — már nincs rá hivatkozó entry.
|
|
88
|
+
await new fam_scope_data_service_1.FAM_Scope_DataService({ issuer: this.issuer }).deleteData(fromId);
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
operation: 'merge_scopes',
|
|
92
|
+
dryRun: dryRun,
|
|
93
|
+
from: { scopeId: fromId, layer: from.layer ?? '', canonicalName: from.canonicalName ?? '' },
|
|
94
|
+
into: { scopeId: intoId, layer: into.layer ?? '', canonicalName: into.canonicalName ?? '' },
|
|
95
|
+
affected: affected,
|
|
96
|
+
totalAffected: affected.reduce((sum, item) => sum + item.count, 0),
|
|
97
|
+
fromScopeDeleted: !dryRun,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/** Az `entryUpdate` spec alkalmazása MINDEN fő RAG-tár collection-jén (mongoose `updateMany` + arrayFilters). */
|
|
101
|
+
async applyEntryUpdate(entryUpdate) {
|
|
102
|
+
for (const named of this.entryCollections()) {
|
|
103
|
+
await named.collection.updateMany(entryUpdate.filter, entryUpdate.update, { arrayFilters: entryUpdate.arrayFilters });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/** A `scopeId`-t hivatkozó aktív entry-k száma per-collection (a 6 fő tár + a reference). */
|
|
107
|
+
async countAffected(scopeId) {
|
|
108
|
+
const affected = [];
|
|
109
|
+
for (const named of this.entryCollections()) {
|
|
110
|
+
affected.push({ collection: named.name, count: await named.collection.countDocuments({ 'scopePath.scopeId': scopeId }) });
|
|
111
|
+
}
|
|
112
|
+
const reference = this.referenceCollection();
|
|
113
|
+
affected.push({ collection: reference.name, count: await reference.collection.countDocuments({ 'canonicalScopeRef.scopeId': scopeId }) });
|
|
114
|
+
return affected;
|
|
115
|
+
}
|
|
116
|
+
/** A 6 fő RAG-tár mongoose-collection-jei (a DS-példányosítás regisztrálja a modellt, onnan a valós collection). */
|
|
117
|
+
entryCollections() {
|
|
118
|
+
const result = [];
|
|
119
|
+
for (const entry of embedding_1.FAM_STORE_REGISTRY) {
|
|
120
|
+
new embedding_1.FAM_Entry_DataService({ dataParams: entry.dataParams, issuer: this.issuer });
|
|
121
|
+
const model = mongoose_1.default.models[entry.dataParams.dataName];
|
|
122
|
+
if (model?.collection) {
|
|
123
|
+
result.push({ name: model.collection.collectionName, collection: model.collection });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return result;
|
|
127
|
+
}
|
|
128
|
+
/** A reference helper-tár mongoose-collection-je. */
|
|
129
|
+
referenceCollection() {
|
|
130
|
+
new fam_reference_data_service_1.FAM_Reference_DataService({ issuer: this.issuer });
|
|
131
|
+
const model = mongoose_1.default.models['fam_reference'];
|
|
132
|
+
return { name: model?.collection?.collectionName ?? 'fam_references', collection: model.collection };
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Egy scope feloldása a szelektorból: `scopeId` (közvetlen) VAGY `layer`+`canonicalName` (egyértelmű) VAGY
|
|
136
|
+
* csak `canonicalName` (minden layer-en, normalizált-egyezés — egyértelmű kell legyen). Nem-talált/kétértelmű → hiba.
|
|
137
|
+
*/
|
|
138
|
+
async resolveScope(selector) {
|
|
139
|
+
const dataService = new fam_scope_data_service_1.FAM_Scope_DataService({ issuer: this.issuer });
|
|
140
|
+
if (selector.scopeId) {
|
|
141
|
+
const scope = await dataService.findScopeById(selector.scopeId);
|
|
142
|
+
if (scope?._id) {
|
|
143
|
+
return scope;
|
|
144
|
+
}
|
|
145
|
+
throw this.notFound(`scopeId '${selector.scopeId}'`);
|
|
146
|
+
}
|
|
147
|
+
if (selector.canonicalName) {
|
|
148
|
+
const matches = selector.layer
|
|
149
|
+
? await dataService.findByCanonical(selector.layer, selector.canonicalName)
|
|
150
|
+
: await this.findByNameAnyLayer(dataService, selector.canonicalName);
|
|
151
|
+
if (matches.length === 1) {
|
|
152
|
+
return matches[0];
|
|
153
|
+
}
|
|
154
|
+
if (matches.length > 1) {
|
|
155
|
+
throw fam_error_factory_util_1.FAM_Error_Util.create({
|
|
156
|
+
errorCode: error_codes_const_1.FAM_ERROR_CODES.refResolveAmbiguous,
|
|
157
|
+
message: `A(z) '${selector.canonicalName}' név több scope-ra illeszkedik (${matches.length}) — adj meg `
|
|
158
|
+
+ '`layer`-t vagy `scopeId`-t az egyértelműsítéshez.',
|
|
159
|
+
issuer: this.issuer,
|
|
160
|
+
context: { operation: 'scope-maintenance:resolve' },
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
throw this.notFound(`'${selector.canonicalName}'${selector.layer ? ` (layer '${selector.layer}')` : ''}`);
|
|
164
|
+
}
|
|
165
|
+
throw this.invalid('Add meg a scope-ot: `scopeId` VAGY `canonicalName` (+ opcionális `layer`).', 'resolve-scope');
|
|
166
|
+
}
|
|
167
|
+
/** Egy név normalizált-egyezése MINDEN aktív scope-on (layer nélkül; a halmaz kicsi). */
|
|
168
|
+
async findByNameAnyLayer(dataService, name) {
|
|
169
|
+
const all = await dataService.findAllActive();
|
|
170
|
+
const target = fam_scope_normalize_util_1.FAM_ScopeNormalize_Util.normalize(name);
|
|
171
|
+
return all.filter((scope) => fam_scope_normalize_util_1.FAM_ScopeNormalize_Util.normalize(scope.canonicalName ?? '') === target);
|
|
172
|
+
}
|
|
173
|
+
notFound(what) {
|
|
174
|
+
return fam_error_factory_util_1.FAM_Error_Util.create({
|
|
175
|
+
errorCode: error_codes_const_1.FAM_ERROR_CODES.refResolveNoMatch,
|
|
176
|
+
message: `Nincs aktív scope: ${what}.`,
|
|
177
|
+
issuer: this.issuer,
|
|
178
|
+
context: { operation: 'scope-maintenance:resolve' },
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
invalid(message, operation) {
|
|
182
|
+
return fam_error_factory_util_1.FAM_Error_Util.create({
|
|
183
|
+
errorCode: error_codes_const_1.FAM_ERROR_CODES.mcpDispatchUnknownCapability,
|
|
184
|
+
message: message,
|
|
185
|
+
issuer: this.issuer,
|
|
186
|
+
context: { operation: `scope-maintenance:${operation}` },
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
exports.FAM_ScopeMaintenance_ControlService = FAM_ScopeMaintenance_ControlService;
|
|
@@ -409,7 +409,7 @@ class FAM_ScopeResolver_ControlService {
|
|
|
409
409
|
}
|
|
410
410
|
const scope_DS = new fam_scope_data_service_1.FAM_Scope_DataService({ issuer: this.issuer });
|
|
411
411
|
const scope = await scope_DS.findScopeById(scopeId);
|
|
412
|
-
return Boolean(scope
|
|
412
|
+
return Boolean(scope?._id);
|
|
413
413
|
}
|
|
414
414
|
// =========================================================================
|
|
415
415
|
// TRACE (dsgn-002 §4, dsgn-003 §4.2) — a debug-capability (MP-6) fogyasztja
|
|
@@ -460,7 +460,7 @@ class FAM_ScopeResolver_ControlService {
|
|
|
460
460
|
async traceEntityFallback(rawLayer, scopeDataService) {
|
|
461
461
|
const matches = await scopeDataService.findByCanonical(rawLayer.layer, rawLayer.rawName);
|
|
462
462
|
const hit = matches.find((scope) => !!scope._id);
|
|
463
|
-
if (hit
|
|
463
|
+
if (hit?._id) {
|
|
464
464
|
return { matched: true, scopeId: hit._id, canonicalName: hit.canonicalName };
|
|
465
465
|
}
|
|
466
466
|
return { matched: false };
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* a fogyasztó-MP-ké. A reference/scope collection-ök MP-1-ben már a `dbModels`-ben vannak.
|
|
10
10
|
*/
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.FAM_FuzzyMatch_Util = exports.FAM_ScopeNormalize_Util = exports.FAM_ScopeResolver_ControlService = exports.FAM_Reference_DataService = exports.FAM_Scope_DataService = void 0;
|
|
12
|
+
exports.FAM_FuzzyMatch_Util = exports.FAM_ScopeNormalize_Util = exports.FAM_ScopeMaintenance_ControlService = exports.FAM_ScopeResolver_ControlService = exports.FAM_Reference_DataService = exports.FAM_Scope_DataService = void 0;
|
|
13
13
|
// SP-3.1 — Scope CRUD + scope-fa traverzál
|
|
14
14
|
var fam_scope_data_service_1 = require("./_services/fam-scope.data-service");
|
|
15
15
|
Object.defineProperty(exports, "FAM_Scope_DataService", { enumerable: true, get: function () { return fam_scope_data_service_1.FAM_Scope_DataService; } });
|
|
@@ -19,6 +19,9 @@ Object.defineProperty(exports, "FAM_Reference_DataService", { enumerable: true,
|
|
|
19
19
|
// SP-3.3/SP-3.4 — a feloldó control-service (write-resolve / read-expand / cache / scopeExists / uncertainty)
|
|
20
20
|
var fam_scope_resolver_control_service_1 = require("./_services/fam-scope-resolver.control-service");
|
|
21
21
|
Object.defineProperty(exports, "FAM_ScopeResolver_ControlService", { enumerable: true, get: function () { return fam_scope_resolver_control_service_1.FAM_ScopeResolver_ControlService; } });
|
|
22
|
+
// user-FR 2026-06-22 — scope rename/merge maintenance (rendetlen claude-mem import scope-nevek rendezése)
|
|
23
|
+
var fam_scope_maintenance_control_service_1 = require("./_services/fam-scope-maintenance.control-service");
|
|
24
|
+
Object.defineProperty(exports, "FAM_ScopeMaintenance_ControlService", { enumerable: true, get: function () { return fam_scope_maintenance_control_service_1.FAM_ScopeMaintenance_ControlService; } });
|
|
22
25
|
// utils (SP-3.1/SP-3.2)
|
|
23
26
|
var fam_scope_normalize_util_1 = require("./_collections/fam-scope-normalize.util");
|
|
24
27
|
Object.defineProperty(exports, "FAM_ScopeNormalize_Util", { enumerable: true, get: function () { return fam_scope_normalize_util_1.FAM_ScopeNormalize_Util; } });
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Api_Controller = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const mongoose_1 = tslib_1.__importDefault(require("mongoose"));
|
|
4
6
|
const fsm_dynamo_1 = require("@futdevpro/fsm-dynamo");
|
|
5
7
|
const nts_dynamo_1 = require("@futdevpro/nts-dynamo");
|
|
8
|
+
const nts_dynamo_2 = require("@futdevpro/nts-dynamo");
|
|
6
9
|
const fam_version_const_1 = require("../../../_collections/fam-version.const");
|
|
7
|
-
const fam_heap_guard_control_service_1 = require("../../../_collections/fam-heap-guard.control-service");
|
|
8
10
|
const fam_table_type_enum_1 = require("../../../_enums/fam-table.type-enum");
|
|
9
11
|
const fam_config_level_type_enum_1 = require("../../../_enums/fam-config-level.type-enum");
|
|
10
12
|
const config_control_service_1 = require("../config/config.control-service");
|
|
@@ -141,12 +143,12 @@ class Api_Controller extends nts_dynamo_1.DyNTS_Controller {
|
|
|
141
143
|
endpoint: '/health',
|
|
142
144
|
tasks: [
|
|
143
145
|
async (req, res) => {
|
|
144
|
-
// A heap-nyomás IS kimegy a health-en (observability — a
|
|
145
|
-
const memory =
|
|
146
|
+
// A heap-nyomás IS kimegy a health-en (observability) — a BEDROCK DyNTS_MemoryGuard pressure-éből.
|
|
147
|
+
const memory = nts_dynamo_2.DyNTS_MemoryGuard.getInstance().pressure();
|
|
146
148
|
res.send({
|
|
147
149
|
ok: true, service: 'fdp-agent-memory', version: fam_version_const_1.FAM_VERSION, uptime: process.uptime(),
|
|
148
|
-
memory: { level: memory.level,
|
|
149
|
-
|
|
150
|
+
memory: { level: memory.level, heapUsedMb: memory.heapUsedMb, heapLimitMb: memory.heapLimitMb,
|
|
151
|
+
heapPct: memory.heapPct, rssMb: memory.rssMb, gcFraction: memory.gcFraction },
|
|
150
152
|
});
|
|
151
153
|
},
|
|
152
154
|
],
|
|
@@ -193,13 +195,27 @@ class Api_Controller extends nts_dynamo_1.DyNTS_Controller {
|
|
|
193
195
|
if (req.query.ingestRunId) {
|
|
194
196
|
filter.ingestRunId = String(req.query.ingestRunId);
|
|
195
197
|
}
|
|
196
|
-
const dataService = new embedding_1.FAM_Entry_DataService({ dataParams: registryEntry.dataParams, issuer: this.issuer });
|
|
197
|
-
const all = await dataService.findHydratableList(filter);
|
|
198
198
|
const pageSize = Math.min(this.parseNumber(req.query.pageSize, DEFAULT_PAGE_SIZE), MAX_PAGE_SIZE);
|
|
199
199
|
const page = Math.max(this.parseNumber(req.query.page, 1), 1);
|
|
200
200
|
const start = (page - 1) * pageSize;
|
|
201
|
-
|
|
202
|
-
|
|
201
|
+
// FIX (OOM): DB-OLDALI lapozás + `contentVector` projekció-kizárás. KORÁBBAN `findHydratableList({})` a TELJES
|
|
202
|
+
// tárat (126k+ teljes doc + 2560-dim vektor) memóriába töltötte ÉS memóriában szeletelt → a UI-polling többször
|
|
203
|
+
// GB-okat allokált → OOM + Mongo-flapping. Most: `countDocuments` + `skip/limit` + a nagy vektor-mező kihagyva.
|
|
204
|
+
const collection = this.entryCollection(registryEntry);
|
|
205
|
+
const total = await collection.countDocuments(filter);
|
|
206
|
+
const items = await collection
|
|
207
|
+
.find(filter, { projection: { contentVector: 0 } })
|
|
208
|
+
.sort({ __lastModified: -1 })
|
|
209
|
+
.skip(start)
|
|
210
|
+
.limit(pageSize)
|
|
211
|
+
.toArray();
|
|
212
|
+
return { table: table, total: total, page: page, pageSize: pageSize, items: items };
|
|
213
|
+
}
|
|
214
|
+
/** Egy fő RAG-tár mongoose-collection-je (a DS-példányosítás regisztrálja a modellt; onnan a valós collection). */
|
|
215
|
+
entryCollection(registryEntry) {
|
|
216
|
+
new embedding_1.FAM_Entry_DataService({ dataParams: registryEntry.dataParams, issuer: this.issuer });
|
|
217
|
+
const model = mongoose_1.default.models[registryEntry.dataParams.dataName];
|
|
218
|
+
return model.collection;
|
|
203
219
|
}
|
|
204
220
|
// =========================================================================
|
|
205
221
|
// /stats · /stats/:table — high-level stats aggregátum (dsgn-011 §3/§4.4)
|
|
@@ -234,7 +250,11 @@ class Api_Controller extends nts_dynamo_1.DyNTS_Controller {
|
|
|
234
250
|
],
|
|
235
251
|
});
|
|
236
252
|
}
|
|
237
|
-
/**
|
|
253
|
+
/**
|
|
254
|
+
* A tár-stats aggregátum (aktív elem-szám + embedding-státusz + utolsó ingest) — dsgn-011 §3/§4.4.
|
|
255
|
+
* FIX (OOM): `countDocuments` + `$group`-aggregáció + 1-doc last-modified projekció — KORÁBBAN `findHydratableList({})`
|
|
256
|
+
* a TELJES tárat memóriába töltötte (a UI stats-pollingja → ismételt GB-allokáció → OOM). Most semmit nem hidratál.
|
|
257
|
+
*/
|
|
238
258
|
async collectTableStats(tables) {
|
|
239
259
|
const result = [];
|
|
240
260
|
for (const table of tables) {
|
|
@@ -242,24 +262,35 @@ class Api_Controller extends nts_dynamo_1.DyNTS_Controller {
|
|
|
242
262
|
if (!registryEntry) {
|
|
243
263
|
continue;
|
|
244
264
|
}
|
|
245
|
-
const
|
|
246
|
-
const
|
|
265
|
+
const collection = this.entryCollection(registryEntry);
|
|
266
|
+
const count = await collection.countDocuments({});
|
|
267
|
+
// Embedding-státusz bontás Mongo-aggregációval ($group), NEM a doc-ok memóriába töltésével.
|
|
247
268
|
const byStatus = { pending: 0, completed: 0, error: 0 };
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const
|
|
253
|
-
if (
|
|
254
|
-
|
|
269
|
+
const grouped = await collection
|
|
270
|
+
.aggregate([{ $group: { _id: '$embeddingStatus', n: { $sum: 1 } } }])
|
|
271
|
+
.toArray();
|
|
272
|
+
for (const group of grouped) {
|
|
273
|
+
const status = group._id ?? 'pending';
|
|
274
|
+
if (status in byStatus) {
|
|
275
|
+
byStatus[status] += group.n;
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
byStatus.pending += group.n;
|
|
255
279
|
}
|
|
256
280
|
}
|
|
281
|
+
// Utolsó módosítás: EGYETLEN doc (a legfrissebb), csak az időbélyeg-mezőre projektálva.
|
|
282
|
+
const newest = await collection
|
|
283
|
+
.find({}, { projection: { __lastModified: 1 } })
|
|
284
|
+
.sort({ __lastModified: -1 })
|
|
285
|
+
.limit(1)
|
|
286
|
+
.toArray();
|
|
287
|
+
const lastModifiedMs = newest[0]?.__lastModified ? new Date(newest[0].__lastModified).getTime() : 0;
|
|
257
288
|
result.push({
|
|
258
289
|
table: table,
|
|
259
|
-
count:
|
|
290
|
+
count: count,
|
|
260
291
|
embeddingStatus: byStatus,
|
|
261
292
|
lastModified: lastModifiedMs ? new Date(lastModifiedMs).toISOString() : null,
|
|
262
|
-
status:
|
|
293
|
+
status: count ? 'ok' : 'empty',
|
|
263
294
|
});
|
|
264
295
|
}
|
|
265
296
|
return result;
|
|
@@ -434,14 +465,14 @@ class Api_Controller extends nts_dynamo_1.DyNTS_Controller {
|
|
|
434
465
|
if (!Api_Controller.HEAVY_WRITE_OPERATIONS.includes(operation)) {
|
|
435
466
|
return;
|
|
436
467
|
}
|
|
437
|
-
const guard =
|
|
468
|
+
const guard = nts_dynamo_2.DyNTS_MemoryGuard.getInstance();
|
|
438
469
|
if (!guard.shouldShedLoad()) {
|
|
439
470
|
return;
|
|
440
471
|
}
|
|
441
472
|
const pressure = guard.pressure();
|
|
442
473
|
throw new fsm_dynamo_1.DyFM_Error({
|
|
443
474
|
errorCode: 'FAM-MEM-SHED-001',
|
|
444
|
-
message: `Memória-nyomás (${pressure.
|
|
475
|
+
message: `Memória-nyomás (${pressure.heapUsedMb}/${pressure.heapLimitMb} MB, ${pressure.heapPct}%, `
|
|
445
476
|
+ `GC ${Math.round(pressure.gcFraction * 100)}%) — a nehéz '${operation}' művelet ideiglenesen elutasítva `
|
|
446
477
|
+ '(load-shed). Próbáld újra, amikor a nyomás enyhül (lásd /api/logs), vagy szakaszold a scant (cooldown).',
|
|
447
478
|
issuer: this.issuer,
|
|
@@ -163,7 +163,7 @@ class FAM_Config_ControlService {
|
|
|
163
163
|
const dataService = new config_data_service_1.FAM_Config_DataService({ issuer: options.issuer ?? this.issuer });
|
|
164
164
|
// Régi aktív rekord (ha van) → soft-delete (archív; history-megőrzés, dsgn-007 §7.2).
|
|
165
165
|
const existing = await dataService.findActive(filter);
|
|
166
|
-
if (existing
|
|
166
|
+
if (existing?._id) {
|
|
167
167
|
await dataService.deleteData(existing._id);
|
|
168
168
|
}
|
|
169
169
|
// Új aktív rekord — a `value` Mixed; a saveData $set-tel ír (Mongoose Mixed silent-drop ellen).
|
|
@@ -435,7 +435,7 @@ class FAM_Config_ControlService {
|
|
|
435
435
|
async seedGlobalIfMissing(key, value) {
|
|
436
436
|
const dataService = new config_data_service_1.FAM_Config_DataService({ issuer: this.issuer });
|
|
437
437
|
const existing = await dataService.findActive({ level: fam_config_level_type_enum_1.FAM_ConfigLevel.global, key: key });
|
|
438
|
-
if (existing
|
|
438
|
+
if (existing?._id) {
|
|
439
439
|
return;
|
|
440
440
|
}
|
|
441
441
|
await this.set(key, value, { level: fam_config_level_type_enum_1.FAM_ConfigLevel.global, setBy: 'env-seed', issuer: this.issuer });
|
package/build/src/app.server.js
CHANGED
|
@@ -12,8 +12,6 @@ const errors_controller_1 = require("./_routes/server/errors/errors.controller")
|
|
|
12
12
|
const errors_control_service_1 = require("./_routes/server/errors/errors.control-service");
|
|
13
13
|
const server_status_controller_1 = require("./_routes/server/server-status/server-status.controller");
|
|
14
14
|
const embedding_1 = require("./_modules/embedding");
|
|
15
|
-
const config_control_service_1 = require("./_routes/server/config/config.control-service");
|
|
16
|
-
const fam_heap_guard_control_service_1 = require("./_collections/fam-heap-guard.control-service");
|
|
17
15
|
const environment_1 = require("./environments/environment");
|
|
18
16
|
const title_const_1 = require("./_collections/title.const");
|
|
19
17
|
/**
|
|
@@ -54,6 +52,30 @@ class App extends nts_dynamo_1.DyNTS_App {
|
|
|
54
52
|
// lokális) lekérdezhetővé teszi → az agent IS olvashatja REST-en (nem csak a tmp-fájlból). Install + enable.
|
|
55
53
|
nts_dynamo_1.DyNTS_global_settings.log_settings.logs_endpoint = { enabled: true };
|
|
56
54
|
logs_1.DyNTS_Logs_Service.getInstance().install();
|
|
55
|
+
// OOM-hibakezelés a BEDROCK `DyNTS_MemoryGuard`-ból (a base App auto-installálja a startup-ban). FAM-specifikus
|
|
56
|
+
// hangolás: a GC-thrash a fő OOM-jel (a FAM korpusza nagy, a heap-% csak ~85%-on ütött, de GC ~0.87-en thrash-elt),
|
|
57
|
+
// ezért a GC-fraction-küszöbök a kritikusak; a `%`-küszöböt magasra tesszük (csak genuine-danger). A critical
|
|
58
|
+
// nyomás load-shed-eli a nehéz write-eket (scan/import) az api-controllerben, + crash-handler (uncaughtException →
|
|
59
|
+
// teljes-részletű error-entry) ON. **`exitOnSustainedCritical` ON** (nts-dynamo 1.15.74 boot-grace-szel): a FAM
|
|
60
|
+
// boot-hidratálása (126k vektor) ELEVE GC-thrash-el (mért gc≈0.87), ezért `bootGraceMs: 120000` — a ~80s hidratálás
|
|
61
|
+
// + margó alatt a sustained-critical NEM lép ki (csak logol + load-shed); a grace UTÁN a tartós critical TISZTÁN
|
|
62
|
+
// kilép (exit 137) → a `fam serve` ps1-wrapper prediktíven újraindít (a kriptikus OOM exit-134 helyett). Az eredeti
|
|
63
|
+
// OOM uptime 771s-nél volt — jóval a grace után, tehát kezelve.
|
|
64
|
+
nts_dynamo_1.DyNTS_global_settings.memoryGuard = {
|
|
65
|
+
enabled: true,
|
|
66
|
+
pollIntervalMs: 3000,
|
|
67
|
+
heapWarningThreshold: 80,
|
|
68
|
+
heapCriticalThreshold: 90,
|
|
69
|
+
recoveryMargin: 10,
|
|
70
|
+
maxHistoryCount: 100,
|
|
71
|
+
gcWarningFraction: 0.40,
|
|
72
|
+
gcCriticalFraction: 0.60,
|
|
73
|
+
installCrashHandlers: true,
|
|
74
|
+
exitOnSustainedCritical: true,
|
|
75
|
+
sustainedCriticalPolls: 3,
|
|
76
|
+
exitCode: 137,
|
|
77
|
+
bootGraceMs: 120000,
|
|
78
|
+
};
|
|
57
79
|
}
|
|
58
80
|
getGlobalServiceCollection() {
|
|
59
81
|
return {
|
|
@@ -119,34 +141,11 @@ class App extends nts_dynamo_1.DyNTS_App {
|
|
|
119
141
|
* át, amit a write/scan path (MP-4/MP-5) hív mentés után. Lásd a `_modules/embedding` modul-doksit.
|
|
120
142
|
*/
|
|
121
143
|
async postProcess() {
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
await this.installHeapGuard();
|
|
144
|
+
// Az OOM-hibakezelést (DyNTS_MemoryGuard) a base App auto-installálja a startup-ban, a FAM-hangolással
|
|
145
|
+
// (overrideDynamoNTSGlobalSettings → DyNTS_global_settings.memoryGuard) — itt nincs külön teendő.
|
|
125
146
|
await embedding_1.FAM_EmbeddingBootstrap_ControlService.getInstance().boot();
|
|
126
147
|
fsm_dynamo_1.DyFM_Log.S_info(`UI is available at: localhost:${environment_1.env.httpPort}/api/app`);
|
|
127
148
|
}
|
|
128
|
-
/** A heap-guard küszöbeinek beolvasása a config-ból + telepítés. A config-hiba NEM buktatja a bootot (default-ok). */
|
|
129
|
-
async installHeapGuard() {
|
|
130
|
-
try {
|
|
131
|
-
const config = config_control_service_1.FAM_Config_ControlService.getInstance();
|
|
132
|
-
const num = async (key) => {
|
|
133
|
-
const resolved = await config.resolve(key, {});
|
|
134
|
-
return typeof resolved.value === 'number' ? resolved.value : undefined;
|
|
135
|
-
};
|
|
136
|
-
const enabledResolved = await config.resolve('memory.guardEnabled', {});
|
|
137
|
-
fam_heap_guard_control_service_1.FAM_HeapGuard_ControlService.getInstance().configure({
|
|
138
|
-
enabled: typeof enabledResolved.value === 'boolean' ? enabledResolved.value : undefined,
|
|
139
|
-
warnRatio: await num('memory.warnRatio'),
|
|
140
|
-
shedRatio: await num('memory.shedRatio'),
|
|
141
|
-
exitRatio: await num('memory.exitRatio'),
|
|
142
|
-
pollMs: await num('memory.pollMs'),
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
catch (error) {
|
|
146
|
-
fsm_dynamo_1.DyFM_Log.warn(`[heap-guard] config-olvasás sikertelen, default küszöbök: ${error?.message}`);
|
|
147
|
-
}
|
|
148
|
-
fam_heap_guard_control_service_1.FAM_HeapGuard_ControlService.getInstance().install();
|
|
149
|
-
}
|
|
150
149
|
getGlobalErrorHandler() {
|
|
151
150
|
return async (err, req, res, issuer) => {
|
|
152
151
|
// No-silent-failure (dsgn-008): minden hibát persistálunk a `fam_errors` tárba a
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@futdevpro/fdp-agent-memory",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.30",
|
|
4
4
|
"description": "Local-first, vector-backed multi-table agent memory exposed as an MCP server (read/write/capabilities). Public, FDP-Templates-free, no auth.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"license": "FDP",
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"@futdevpro/fsm-dynamo": "^1.16.7",
|
|
58
|
-
"@futdevpro/nts-dynamo": "^1.15.
|
|
58
|
+
"@futdevpro/nts-dynamo": "^1.15.74",
|
|
59
59
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
60
60
|
"axios": "~1.8.1",
|
|
61
61
|
"better-sqlite3": "^12.4.1",
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.FAM_HeapGuard_ControlService = void 0;
|
|
4
|
-
const tslib_1 = require("tslib");
|
|
5
|
-
const v8 = tslib_1.__importStar(require("v8"));
|
|
6
|
-
const perf_hooks_1 = require("perf_hooks");
|
|
7
|
-
const fsm_dynamo_1 = require("@futdevpro/fsm-dynamo");
|
|
8
|
-
/**
|
|
9
|
-
* `FAM_HeapGuard_ControlService` (user-FR 2026-06-21) — **OOM-hibakezelés**. A FAM-szerver V8-heapje a nagy korpuszon
|
|
10
|
-
* (126k vektor) + nehéz műveletekkor (scan/import/re-embed) a plafonhoz érhet → „Ineffective mark-compacts near heap
|
|
11
|
-
* limit" → FATAL exit 134 (nem catch-elhető). Ez a guard:
|
|
12
|
-
*
|
|
13
|
-
* 1. **Watchdog**: periodikusan méri a heap-arányt (`v8.getHeapStatistics`) ÉS a GC-hányadot (`perf_hooks` 'gc'
|
|
14
|
-
* observer — a thrash valódi jele: a trace-ben `mu = 0.306`, azaz a futás 70%-a GC). A szint-váltásokat logolja
|
|
15
|
-
* (a `/api/logs`-on az agent IS látja jönni a nyomást).
|
|
16
|
-
* 2. **Load-shed**: `shouldShedLoad()` → a NEHÉZ write-ek (scan/import/re-embed) `critical` nyomásnál 503-mal
|
|
17
|
-
* elutasítva, MIELŐTT egy újabb nagy allokáció átlöki a plafont (a könnyű read/create/update fut tovább).
|
|
18
|
-
* 3. **Graceful exit**: ha a nyomás a `exitRatio` fölött tartós (GC-vel sem csökken), strukturált diagnosztika +
|
|
19
|
-
* tiszta `process.exit` → a `fam serve` ps1-wrapper PREDIKTÍVAN újraindít (a kriptikus exit-134 helyett).
|
|
20
|
-
* 4. **Crash-handler**: `uncaughtException` / `unhandledRejection` → strukturált log + tiszta exit (eddig néma volt).
|
|
21
|
-
*
|
|
22
|
-
* Lokális, single-user (ADR-004) → a load-shed a saját gépet védi, nem multi-tenant fairness. A küszöbök
|
|
23
|
-
* config-/env-kapcsolósak; a tiszta szint-számítás (`computeLevel`) pure + külön tesztelt.
|
|
24
|
-
*/
|
|
25
|
-
class FAM_HeapGuard_ControlService {
|
|
26
|
-
static _instance;
|
|
27
|
-
/** GC-hányad-küszöb a `warn`/`critical` szinthez (a thrash-jelhez; a ratio MELLETT). */
|
|
28
|
-
static GC_WARN_FRACTION = 0.40;
|
|
29
|
-
static GC_CRITICAL_FRACTION = 0.60;
|
|
30
|
-
thresholds = {
|
|
31
|
-
warnRatio: 0.70, shedRatio: 0.82, exitRatio: 0.92, pollMs: 3000, enabled: true,
|
|
32
|
-
};
|
|
33
|
-
installed = false;
|
|
34
|
-
timer;
|
|
35
|
-
gcObserver;
|
|
36
|
-
lastLevel = 'ok';
|
|
37
|
-
/** A GC-ben töltött ms gördülő ablaka (timestamp + duration párok az utolsó ~pollMs-re). */
|
|
38
|
-
gcEvents = [];
|
|
39
|
-
static getInstance() {
|
|
40
|
-
if (!FAM_HeapGuard_ControlService._instance) {
|
|
41
|
-
FAM_HeapGuard_ControlService._instance = new FAM_HeapGuard_ControlService();
|
|
42
|
-
}
|
|
43
|
-
return FAM_HeapGuard_ControlService._instance;
|
|
44
|
-
}
|
|
45
|
-
/** A küszöbök beállítása (install ELŐTT) — clamp `[0..1]` (a pollMs `[500..60000]`). Hiányzó mező → marad a default. */
|
|
46
|
-
configure(partial) {
|
|
47
|
-
const clampRatio = (value, fallback) => (typeof value === 'number' && value > 0 && value <= 1) ? value : fallback;
|
|
48
|
-
this.thresholds = {
|
|
49
|
-
warnRatio: clampRatio(partial.warnRatio, this.thresholds.warnRatio),
|
|
50
|
-
shedRatio: clampRatio(partial.shedRatio, this.thresholds.shedRatio),
|
|
51
|
-
exitRatio: clampRatio(partial.exitRatio, this.thresholds.exitRatio),
|
|
52
|
-
pollMs: typeof partial.pollMs === 'number' && partial.pollMs >= 500
|
|
53
|
-
? Math.min(partial.pollMs, 60000) : this.thresholds.pollMs,
|
|
54
|
-
enabled: typeof partial.enabled === 'boolean' ? partial.enabled : this.thresholds.enabled,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
/** A crash-handlerek + (ha enabled) a watchdog + GC-observer telepítése. Idempotens (egyszer fut). Best-effort. */
|
|
58
|
-
install() {
|
|
59
|
-
if (this.installed) {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
this.installed = true;
|
|
63
|
-
process.on('uncaughtException', (error) => this.onFatal('uncaughtException', error));
|
|
64
|
-
process.on('unhandledRejection', (reason) => this.onFatal('unhandledRejection', reason));
|
|
65
|
-
if (!this.thresholds.enabled) {
|
|
66
|
-
fsm_dynamo_1.DyFM_Log.info('[heap-guard] disabled (memory.guardEnabled=false) — csak a crash-handlerek aktívak.');
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
this.installGcObserver();
|
|
70
|
-
this.timer = setInterval(() => this.tick(), this.thresholds.pollMs);
|
|
71
|
-
// A timer NE tartsa életben a process-t (a graceful shutdownt ne blokkolja).
|
|
72
|
-
this.timer.unref?.();
|
|
73
|
-
fsm_dynamo_1.DyFM_Log.info(`[heap-guard] aktív (poll ${this.thresholds.pollMs}ms; shed@${Math.round(this.thresholds.shedRatio * 100)}%, `
|
|
74
|
-
+ `exit@${Math.round(this.thresholds.exitRatio * 100)}%).`);
|
|
75
|
-
}
|
|
76
|
-
/** Pure szint-számítás (külön tesztelhető): a ratio ÉS a GC-hányad közül a ROSSZABB dönt. */
|
|
77
|
-
static computeLevel(ratio, gcFraction, thresholds) {
|
|
78
|
-
if (ratio >= thresholds.shedRatio || gcFraction >= FAM_HeapGuard_ControlService.GC_CRITICAL_FRACTION) {
|
|
79
|
-
return 'critical';
|
|
80
|
-
}
|
|
81
|
-
if (ratio >= thresholds.warnRatio || gcFraction >= FAM_HeapGuard_ControlService.GC_WARN_FRACTION) {
|
|
82
|
-
return 'warn';
|
|
83
|
-
}
|
|
84
|
-
return 'ok';
|
|
85
|
-
}
|
|
86
|
-
/** A pillanatnyi heap-nyomás (ratio + GC-hányad → szint). Observability + load-shed forrása. */
|
|
87
|
-
pressure() {
|
|
88
|
-
const stats = v8.getHeapStatistics();
|
|
89
|
-
const usedMb = Math.round(stats.used_heap_size / 1048576);
|
|
90
|
-
const limitMb = Math.round(stats.heap_size_limit / 1048576);
|
|
91
|
-
const ratio = stats.heap_size_limit ? stats.used_heap_size / stats.heap_size_limit : 0;
|
|
92
|
-
const gcFraction = this.recentGcFraction();
|
|
93
|
-
const level = FAM_HeapGuard_ControlService.computeLevel(ratio, gcFraction, this.thresholds);
|
|
94
|
-
return { level: level, usedMb: usedMb, limitMb: limitMb, ratio: ratio, gcFraction: gcFraction };
|
|
95
|
-
}
|
|
96
|
-
/** `true` → a NEHÉZ write-eket (scan/import/re-embed) le kell shed-elni (critical nyomás). Disabled → soha. */
|
|
97
|
-
shouldShedLoad() {
|
|
98
|
-
return this.thresholds.enabled && this.pressure().level === 'critical';
|
|
99
|
-
}
|
|
100
|
-
/** A GC-hányad az utolsó `pollMs` ablakban (GC-ms / ablak-ms; clamp [0..1]). */
|
|
101
|
-
recentGcFraction() {
|
|
102
|
-
const windowMs = this.thresholds.pollMs;
|
|
103
|
-
const cutoff = Date.now() - windowMs;
|
|
104
|
-
this.gcEvents = this.gcEvents.filter((event) => event.at >= cutoff);
|
|
105
|
-
const gcMs = this.gcEvents.reduce((sum, event) => sum + event.ms, 0);
|
|
106
|
-
return Math.min(gcMs / windowMs, 1);
|
|
107
|
-
}
|
|
108
|
-
/** A 'gc' perf-entry-k figyelése — a GC-ben töltött időt gyűjti a gördülő ablakhoz. */
|
|
109
|
-
installGcObserver() {
|
|
110
|
-
try {
|
|
111
|
-
this.gcObserver = new perf_hooks_1.PerformanceObserver((list) => {
|
|
112
|
-
const now = Date.now();
|
|
113
|
-
for (const entry of list.getEntries()) {
|
|
114
|
-
this.gcEvents.push({ at: now, ms: entry.duration });
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
this.gcObserver.observe({ entryTypes: ['gc'] });
|
|
118
|
-
}
|
|
119
|
-
catch {
|
|
120
|
-
// A GC-observer nem kritikus (a ratio-jel marad) — best-effort.
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
/** A watchdog-tick: szint-váltás logolása + a tartós veszély-zóna graceful-exit-je. */
|
|
124
|
-
tick() {
|
|
125
|
-
const pressure = this.pressure();
|
|
126
|
-
if (pressure.level !== this.lastLevel) {
|
|
127
|
-
const line = `[heap-guard] ${this.lastLevel}→${pressure.level} `
|
|
128
|
-
+ `(${pressure.usedMb}/${pressure.limitMb} MB ${Math.round(pressure.ratio * 100)}%, GC ${Math.round(pressure.gcFraction * 100)}%)`;
|
|
129
|
-
if (pressure.level === 'critical') {
|
|
130
|
-
fsm_dynamo_1.DyFM_Log.warn(`${line} — a nehéz write-ek (scan/import/re-embed) load-shed (503).`);
|
|
131
|
-
}
|
|
132
|
-
else if (pressure.level === 'warn') {
|
|
133
|
-
fsm_dynamo_1.DyFM_Log.warn(line);
|
|
134
|
-
}
|
|
135
|
-
else {
|
|
136
|
-
fsm_dynamo_1.DyFM_Log.info(`${line} — nyomás enyhült.`);
|
|
137
|
-
}
|
|
138
|
-
this.lastLevel = pressure.level;
|
|
139
|
-
}
|
|
140
|
-
// Tartós veszély-zóna: GC-kísérlet, majd ha nem csökken → tiszta, logolt exit (a ps1 újraindít).
|
|
141
|
-
if (pressure.ratio >= this.thresholds.exitRatio) {
|
|
142
|
-
this.attemptGcReclaim();
|
|
143
|
-
const after = this.pressure();
|
|
144
|
-
if (after.ratio >= this.thresholds.exitRatio) {
|
|
145
|
-
fsm_dynamo_1.DyFM_Log.error(`[heap-guard] HEAP-VÉSZ ${after.usedMb}/${after.limitMb} MB (${Math.round(after.ratio * 100)}%) — `
|
|
146
|
-
+ 'graceful exit, hogy a `fam serve` TISZTÁN újraindítson (a kriptikus OOM exit-134 elkerülése).');
|
|
147
|
-
// Egy tick haladékot adunk a log flush-nek, majd restartolható kóddal kilépünk.
|
|
148
|
-
setTimeout(() => process.exit(137), 250).unref?.();
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
/** Best-effort GC-kényszerítés (csak ha `--expose-gc`). A near-limit utolsó esély a tiszta-exit ELŐTT. */
|
|
153
|
-
attemptGcReclaim() {
|
|
154
|
-
try {
|
|
155
|
-
const maybeGc = global.gc;
|
|
156
|
-
if (maybeGc) {
|
|
157
|
-
maybeGc();
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
catch {
|
|
161
|
-
// nincs --expose-gc → nincs mit tenni; a tiszta-exit a fallback.
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
/** Strukturált crash-log + tiszta exit (a `fam serve` ps1 újraindít). Az OOM-fatal NEM ide jön (nem catch-elhető). */
|
|
165
|
-
onFatal(kind, error) {
|
|
166
|
-
try {
|
|
167
|
-
const message = error instanceof Error ? `${error.message}\n${error.stack ?? ''}` : String(error);
|
|
168
|
-
fsm_dynamo_1.DyFM_Log.error(`[heap-guard] FATAL ${kind}: ${message}`);
|
|
169
|
-
}
|
|
170
|
-
catch {
|
|
171
|
-
// a log se buktasson — best-effort.
|
|
172
|
-
}
|
|
173
|
-
setTimeout(() => process.exit(1), 250).unref?.();
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
exports.FAM_HeapGuard_ControlService = FAM_HeapGuard_ControlService;
|