@futdevpro/fdp-agent-memory 1.1.30 → 1.1.113

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 (81) hide show
  1. package/build/package.json +1 -1
  2. package/build/src/_cli/_commands/find-duplicates.command.js +41 -16
  3. package/build/src/_cli/_commands/scan-projects.command.js +113 -113
  4. package/build/src/_cli/_commands/scan.command.js +2 -0
  5. package/build/src/_cli/_commands/serve.command.js +110 -10
  6. package/build/src/_cli/_services/fam-client.service.js +25 -1
  7. package/build/src/_cli/register-commands.js +28 -0
  8. package/build/src/_collections/config-catalog.const.js +160 -6
  9. package/build/src/_collections/error-codes.const.js +2 -0
  10. package/build/src/_collections/fam-console.util.js +38 -260
  11. package/build/src/_collections/fam-db-models.const.js +2 -0
  12. package/build/src/_collections/fam-entry-bootstrap.util.js +20 -6
  13. package/build/src/_collections/fam-error-context.util.js +1 -0
  14. package/build/src/_collections/fam-mcp-bridge.util.js +15 -1
  15. package/build/src/_collections/fam-operation-queue.service.js +85 -0
  16. package/build/src/_collections/fam-project-discovery.util.js +148 -0
  17. package/build/src/_collections/fam-request-origin.util.js +41 -0
  18. package/build/src/_collections/fam-retry.util.js +48 -0
  19. package/build/src/_collections/fam-scan-progress-sink.service.js +26 -0
  20. package/build/src/_enums/fam-rule-scope.type-enum.js +25 -0
  21. package/build/src/_models/data-models/fam-entry.data-model.js +29 -0
  22. package/build/src/_models/data-models/fam-memory.data-model.js +5 -0
  23. package/build/src/_models/data-models/fam-rules.data-model.js +24 -0
  24. package/build/src/_models/data-models/fam-scan-job.data-model.js +73 -0
  25. package/build/src/_models/data-models/fam-scope.data-model.js +9 -0
  26. package/build/src/_modules/embedding/_services/fam-dedup-warn.control-service.js +40 -0
  27. package/build/src/_modules/embedding/_services/fam-duplicate-scan.control-service.js +74 -16
  28. package/build/src/_modules/embedding/_services/fam-embedding-bootstrap.control-service.js +50 -0
  29. package/build/src/_modules/embedding/_services/fam-embedding-pipeline.control-service.js +31 -5
  30. package/build/src/_modules/embedding/_services/fam-embedding.control-service.js +142 -9
  31. package/build/src/_modules/embedding/_services/fam-entry.data-service.js +9 -0
  32. package/build/src/_modules/embedding/_services/fam-hydration-coordinator.control-service.js +159 -0
  33. package/build/src/_modules/embedding/_services/fam-lmstudio-embedding.provider.js +41 -4
  34. package/build/src/_modules/embedding/_services/fam-vector-search.control-service.js +67 -12
  35. package/build/src/_modules/embedding/index.js +5 -1
  36. package/build/src/_modules/ingest/_collections/fam-file-routing.util.js +10 -0
  37. package/build/src/_modules/ingest/_collections/fam-frontmatter.util.js +134 -0
  38. package/build/src/_modules/ingest/_collections/fam-project-identity.util.js +30 -0
  39. package/build/src/_modules/ingest/_collections/fam-scan-progress.util.js +5 -2
  40. package/build/src/_modules/ingest/_collections/fam-scan-summary.util.js +55 -29
  41. package/build/src/_modules/ingest/_collections/fam-split-chunker.util.js +139 -0
  42. package/build/src/_modules/ingest/_models/interfaces/fam-scan-job.interface.js +2 -0
  43. package/build/src/_modules/ingest/_services/fam-chunker.control-service.js +10 -0
  44. package/build/src/_modules/ingest/_services/fam-delta-compare.util.js +68 -1
  45. package/build/src/_modules/ingest/_services/fam-ingest-run.data-service.js +1 -1
  46. package/build/src/_modules/ingest/_services/fam-ingest.control-service.js +173 -37
  47. package/build/src/_modules/ingest/_services/fam-scan-job.control-service.js +429 -0
  48. package/build/src/_modules/ingest/_services/fam-scan-job.data-service.js +34 -0
  49. package/build/src/_modules/ingest/_services/fam-scan.control-service.js +6 -4
  50. package/build/src/_modules/ingest/index.js +6 -1
  51. package/build/src/_modules/mcp/_services/fam-capability-registry.service.js +151 -8
  52. package/build/src/_modules/mcp/_services/fam-read-tool.service.js +70 -0
  53. package/build/src/_modules/mcp/_services/fam-write-tool.service.js +36 -0
  54. package/build/src/_modules/retrieval/_collections/fam-lexical-match.util.js +69 -0
  55. package/build/src/_modules/retrieval/_collections/fam-memory-activation.util.js +66 -0
  56. package/build/src/_modules/retrieval/_collections/fam-rule-injection.util.js +125 -0
  57. package/build/src/_modules/retrieval/_collections/fam-rule-propagation.util.js +95 -0
  58. package/build/src/_modules/retrieval/_collections/fam-rule-scope-context.util.js +74 -0
  59. package/build/src/_modules/retrieval/_services/fam-global-rule.control-service.js +82 -0
  60. package/build/src/_modules/retrieval/_services/fam-memory-cold-search.control-service.js +121 -0
  61. package/build/src/_modules/retrieval/_services/fam-memory-dormancy.control-service.js +99 -0
  62. package/build/src/_modules/retrieval/_services/fam-memory-reactivation.control-service.js +68 -0
  63. package/build/src/_modules/retrieval/_services/fam-retrieval-candidate.data-service.js +15 -2
  64. package/build/src/_modules/retrieval/_services/fam-retrieval.control-service.js +216 -5
  65. package/build/src/_modules/retrieval/_services/fam-rule-injection.control-service.js +102 -0
  66. package/build/src/_modules/retrieval/index.js +12 -1
  67. package/build/src/_modules/scope-reference/_collections/fam-scope-maintenance.util.js +76 -0
  68. package/build/src/_modules/scope-reference/_services/fam-scope-maintenance.control-service.js +233 -0
  69. package/build/src/_modules/scope-reference/_services/fam-scope.data-service.js +25 -0
  70. package/build/src/_routes/server/api/api.controller.js +270 -0
  71. package/build/src/_routes/server/api/fam-doctor.control-service.js +156 -0
  72. package/build/src/app.server.js +13 -10
  73. package/client-dist/{chunk-YXHWCJ5O.js → chunk-GF3FJP7E.js} +71 -71
  74. package/client-dist/chunk-NMUCMQY6.js +1 -0
  75. package/client-dist/favicon.ico +0 -0
  76. package/client-dist/index.html +3 -3
  77. package/client-dist/main-YK4YIVAQ.js +2 -0
  78. package/package.json +1 -1
  79. package/build/src/_cli/_collections/fam-project-discovery.util.js +0 -98
  80. package/client-dist/chunk-I77GXVAQ.js +0 -1
  81. package/client-dist/main-PJPEDVJT.js +0 -2
@@ -66,9 +66,37 @@ function registerFAMCommands(program, version) {
66
66
  ];
67
67
  for (const command of famCommands) {
68
68
  applyExitMapping(command);
69
+ if (SERVER_DEPENDENT_COMMANDS.has(command.name())) {
70
+ attachEnsureServerHook(command);
71
+ }
69
72
  program.addCommand(command);
70
73
  }
71
74
  }
75
+ /**
76
+ * A szerver-függő (REST-et hívó) data-parancsok — ezekre rákötjük az auto-ensure pre-action hook-ot (user-FR
77
+ * 2026-06-22: „minden fam funkciónak gondoskodnia kell a fam elindításáról"). A környezet-szintű parancsok
78
+ * (`init`/`doctor`/`validate-env`/…) É a szerver-életciklust kezelő `start`/`serve` KIMARADNAK (azok nem a
79
+ * futó primary-t hívják, vagy MAGUK a szerver).
80
+ */
81
+ const SERVER_DEPENDENT_COMMANDS = new Set([
82
+ 'read', 'write', 'scan', 'scan-projects', 'import', 'export', 'capture',
83
+ 'config', 'stats', 'find-duplicates', 'errors', 'migrate', 'seed-example',
84
+ ]);
85
+ /**
86
+ * Az auto-ensure pre-action hook rákötése egy szerver-függő parancsra (a commander a hook-ot a parancs ÉS
87
+ * az al-parancsai action-jére is meghívja, így a `config get`/`import claude-mem` is fedett). Best-effort: ha
88
+ * az ensure elhasal, a parancs a SAJÁT `unreachable` hibáját adja (no-silent-failure, dsgn-008) — nem dob itt.
89
+ */
90
+ function attachEnsureServerHook(command) {
91
+ command.hook('preAction', async () => {
92
+ try {
93
+ await serve_command_1.FAM_Serve_Util.ensureRunning();
94
+ }
95
+ catch {
96
+ /* best-effort auto-ensure — a tényleges elérhetetlenséget a parancs HTTP-rétege jelzi tiszta hibával */
97
+ }
98
+ });
99
+ }
72
100
  /**
73
101
  * A dsgn-010 §3 exit-kód-mapping rákötése egy FAM parancsra ÉS minden al-parancsára (rekurzívan): a
74
102
  * commander használati/argument-hibái `2`-vel lépnek ki (a parancs-handler-ek `0`/`1`/`3` kódjait a
@@ -26,6 +26,11 @@ const GLOBAL_ONLY = [fam_config_level_type_enum_1.FAM_ConfigLevel.global];
26
26
  */
27
27
  exports.SCAN_IGNORE_PATTERNS_DEFAULT = [
28
28
  '**/node_modules/**', '**/.git/**', '**/build/**', '**/dist/**', '**/coverage/**',
29
+ // Workspace-szintű HARD-kizárás (user-direktíva 2026-06-24, KRITIKUS — „SEMMIT nem akarunk szkennelni, ami a
30
+ // Stale-ben van!!!"). Az archív/elavult projektek (`STALE-projects/`, pl. `fury`) + a sablon-junk
31
+ // (`TEMPLATE-projects/`, `{{PROJECT_NAME}}`) SOHA nem korpusz-anyag — még explicit cél-útvonalon sem. (A
32
+ // felderítés külön, garantáltan kihagyja őket: FAM_ProjectDiscovery_Util.SKIP_DIRS.)
33
+ '**/STALE-projects/**', '**/TEMPLATE-projects/**',
29
34
  // Framework build-/futás-cache + generált kimenet (FAM-REV-056) — sosem forrás, gyakran minifikált/bináris-
30
35
  // szerű (pl. az Angular `.angular/cache/.../babel-webpack` ezreket számláló, embed-timeout-ot okozó junk-ja).
31
36
  '**/.angular/**', '**/.nx/**', '**/.cache/**', '**/.turbo/**', '**/.next/**', '**/.nuxt/**', '**/.svelte-kit/**',
@@ -45,6 +50,11 @@ exports.SCAN_IGNORE_PATTERNS_DEFAULT = [
45
50
  // lowercase NEM matchelné a Unity `Logs/`-át).
46
51
  '**/Library/**', '**/Temp/**', '**/Obj/**', '**/obj/**', '**/bin/**', '**/Logs/**', '**/Build/**', '**/Builds/**',
47
52
  '**/.vs/**', '**/*.csproj', '**/*.sln', '**/*.meta', '**/*.unity', '**/*.prefab', '**/*.asset',
53
+ // Agent-/böngésző-state (lokális futás-melléktermék, SOHA nem korpusz-anyag): `.local-state` (pl. agent
54
+ // browser-automation profil) + Chromium-profil/cache — lockolt SQLite + bináris blobok (Login Data /
55
+ // Cookies / Web Data = SZEMÉLYES adat), olvashatatlanok + junk. (Lelet 2026-06-22: fdp-assistant/__agent/
56
+ // .local-state = 403 MB / 2452 fájl → 2208 `FAM-SCAN-READ-001`. Az `__agent` MAGA MARAD — csak a state junk.)
57
+ '**/.local-state/**', '**/browser-profiles/**', '**/Cache_Data/**',
48
58
  // A `.fdpfamignore` SAJÁT MAGÁT kizárja: ignore-spec-ként OLVASSUK (FAM_FamIgnore_Util.loadFromDir,
49
59
  // a walk/routing megkerülésével), de SOHA nem ingesteljük content-ként (a router amúgy is skip-elné).
50
60
  '**/.fdpfamignore',
@@ -69,13 +79,35 @@ exports.CONFIG_CATALOG = {
69
79
  description: 'A topK effektív felső korlátja (CCAP DEFAULT_PAGE_SIZE=50, cap 500).',
70
80
  },
71
81
  'read.maxTokens': {
72
- type: 'number', default: 6000, min: 256, max: 32000, integer: true, levels: ALL_LEVELS,
73
- description: 'Az agent context védelme (max token a read-válaszban).',
82
+ type: 'number', default: 4000, min: 256, max: 32000, integer: true, levels: ALL_LEVELS,
83
+ description: 'Az agent context védelme + a read-válasz INLINE-tartása (max token; ~4 char/token). A read-tool '
84
+ + 'ÉRVÉNYESÍTI (FAM-REV: korábban dead-config volt → korlátlan hit-tartalom → a host fájlba mentette a választ). '
85
+ + 'A budget felett a hit-tartalom levágódik + a túlcsorduló hit-ek elhagyva (`truncated:true`).',
86
+ },
87
+ 'write.dedupWarnEnabled': {
88
+ type: 'boolean', default: true, levels: ALL_LEVELS,
89
+ description: 'Near-duplikátum-FIGYELMEZTETÉS íráskor (user-FR 2026-06-25): a `create` UTÁN a friss vektorral '
90
+ + 'megkeresi a tár legközelebbi szomszédait; ha valamelyik a `write.dedupWarnThreshold` FÖLÖTT van, a write-'
91
+ + 'válasz `warnings`-ába kerül („majdnem ugyanaz, mint X — érdemes UPDATE-elni"). NEM blokkol (a duplikáció-'
92
+ + 'keletkezés megelőzése). `false` → nincs ellenőrzés (nulla extra munka, mert a friss vektor úgyis kész).',
93
+ },
94
+ 'write.dedupWarnThreshold': {
95
+ type: 'number', default: 0.93, min: 0.5, max: 1.0, levels: ALL_LEVELS,
96
+ description: 'A near-duplikátum-warning koszinusz-küszöbe (0..1; default 0.93): e FÖLÖTT egy meglévő entry '
97
+ + '„majdnem azonosnak" számít az új tartalommal → figyelmeztetés. Magasabb = csak a szinte-teljes egyezésre '
98
+ + 'jelez; alacsonyabb = agresszívebb (több figyelmeztetés). CSAK jelzés, sosem blokkol.',
74
99
  },
75
100
  'read.minScore': {
76
101
  type: 'number', default: 0.0, min: 0.0, max: 1.0, levels: ALL_LEVELS,
77
102
  description: 'Minimum similarity-score küszöb (CCAP default 0 = nincs vágás).',
78
103
  },
104
+ 'read.hydrationWaitMs': {
105
+ type: 'number', default: 180000, min: 0, max: 600000, integer: true, levels: ALL_LEVELS,
106
+ description: 'A readiness-coordinator hidratálás-bevárás felső korlátja ms-ban: a `read`/`search` MEGVÁRJA a '
107
+ + 'tár pool-jának (kezdő VAGY újra-) hidratálását ahelyett, hogy `FAM-VEC-SEARCH-001 pool ÜRES`-t dobna. '
108
+ + '0 = nincs várakozás (azonnali hiba, ha még nem kész). Boot-kor a ~290k-vektor hidratálás percekig tarthat → '
109
+ + 'a default 180s ezt fedi; time-out után tiszta „még hidratál, próbáld újra" hiba (a következő hívás már kész).',
110
+ },
79
111
  'read.relevanceFloor': {
80
112
  type: 'number', default: 0.5, min: 0.0, max: 1.0, levels: ALL_LEVELS,
81
113
  description: 'Confidence-padló a nyers cosine score-on: a relevancia-JELZÉSEK (totalRelevant / '
@@ -92,6 +124,34 @@ exports.CONFIG_CATALOG = {
92
124
  + 'importChunkWeight` a definíció-chunkok ALÁ rangsorolja őket. READ-time (nincs re-embed); a nyers '
93
125
  + 'cosine score (relevancia-jelzések) NEM változik, csak a ranking. 1 = nincs csökkentés.',
94
126
  },
127
+ 'read.importSourceWeight': {
128
+ type: 'number', default: 0.85, min: 0.0, max: 1.0, levels: ALL_LEVELS,
129
+ description: 'Az import-FORRÁSÚ (`source.type:"import"`, pl. claude-mem-import) entry-k read-idejű rangsor-'
130
+ + 'szorzója (R2, 2026-06-22 eval-lelet). A `memory`/`knowledge` táron a 33k import ZAJA elnyomta a kurált '
131
+ + '(`manual`/`agent`/scan) tanulságokat → e szorzóval (`finalScore × importSourceWeight`) a kurált FÖLÉ '
132
+ + 'rangsorol. READ-time (nincs re-embed); a nyers cosine NEM változik, csak a ranking. 1 = kikapcsolva.',
133
+ },
134
+ 'read.keywordWeight': {
135
+ type: 'number', default: 0.6, min: 0.0, max: 5.0, levels: ALL_LEVELS,
136
+ description: 'Hibrid LEXIKAI (keyword/term-overlap) boost súlya (Improvement 2, eval-lelet 2026-06-25): a tiszta-'
137
+ + 'vektor `read` ELVÉTI a literal-keyword választ (a koszinusz alacsony a query-megfogalmazásra; a `grep` '
138
+ + 'azonnal hozza). A finalScore-t `× (1 + keywordWeight × lexicalScore)` szorzóval BOOST-only emeli, ahol a '
139
+ + 'lexicalScore (0..1) a query disztinktív term-jeinek átfedése az entry szövegével (content+heading+forrás-út). '
140
+ + 'A tiszta-szemantikus találat (lexical 0) SOHA nem süllyed (×1). READ-time (nincs re-embed). `0` = pure-vektor '
141
+ + '(régi viselkedés). Default 0.6.',
142
+ },
143
+ 'read.scanSummaryWeight': {
144
+ type: 'number', default: 1.0, min: 0.0, max: 10.0, levels: ALL_LEVELS,
145
+ description: 'A `source.type:"scan-summary"` (auto-generált projekt-összefoglaló: `project-identity` + '
146
+ + '`fs-summary`) chunkok EFFEKTÍV statikus weight-SAPKÁJA read-időben (2026-07-26 dispatch-eval): e '
147
+ + 'generikus entry-k `weight:3` (project-identity) / `weight:2` (fs-summary) → `finalScore ~2.2` még '
148
+ + 'lexicalScore 0-0.2 mellett is elnyomta a szemantikailag+lexikailag relevánsabb doksikat. E cap a '
149
+ + 'scan-summary effektív weight-jét `min(weight, scanSummaryWeight)`-re szorítja (factor = cap/weight), '
150
+ + 'hogy a STATIKUS weight NE tudja legyőzni a relevanciát. `1.0` = tiszta relevancia-rangsor (nincs statikus '
151
+ + 'boost); az identity-jellegű query-ken a summary a MAGAS cosine-ja miatt így is felszínre kerül (nincs '
152
+ + 'regresszió). READ-time (nincs re-embed); a nyers cosine + a `weight` DTO-mező NEM változik, csak a ranking. '
153
+ + 'A stored weight ALÁ csökkenteni is lehet (<1) az agresszívabb leszorításhoz; ≥ stored weight = kikapcsolva.',
154
+ },
95
155
  'read.denseResultRatio': {
96
156
  type: 'number', default: 2.0, min: 1.0, max: 50.0, levels: ALL_LEVELS,
97
157
  description: 'Dense-jelzés ratio-kapuja: totalRelevant >= ratio*topK → denseResults (dsgn-005 §4.2).',
@@ -104,6 +164,77 @@ exports.CONFIG_CATALOG = {
104
164
  type: 'string', default: 'weighted', enumValues: ['weighted', 'raw', 'recency'], levels: ALL_LEVELS,
105
165
  description: 'Score-rendezés módja (súlyozott / nyers / recency).',
106
166
  },
167
+ // --- 4.1b. Memory Activation Model — decay/reactivation/dormancy (fogyasztó: dsgn-013) ---
168
+ 'memory.activationEnabled': {
169
+ type: 'boolean', default: true, levels: ALL_LEVELS,
170
+ description: 'Memory Activation Model (dsgn-013): a felidézés-alapú lazy-decay rangsor-szorzó a `memory` táron. '
171
+ + 'A frissen/gyakran FELIDÉZETT memória felül-, a régóta érintetlen alul-rangsorol (de sosem tűnik el, '
172
+ + 'csak kisebb a súlya). `false` → a decay-mentes (mai) viselkedés. CSAK a memory tárra hat.',
173
+ },
174
+ 'memory.decayHalfLifeDays': {
175
+ type: 'number', default: 90, min: 1, max: 3650, integer: true, levels: ALL_LEVELS,
176
+ description: 'A recency-decay FELEZŐDÉSI ideje napban (Ebbinghaus-görbe, dsgn-013 §2): ennyi nap inaktivitás '
177
+ + '(nem-felidézés) után a recency-faktor a felére csökken. Nagyobb = lassabb felejtés. Default 90.',
178
+ },
179
+ 'memory.floorWeight': {
180
+ type: 'number', default: 0.05, min: 0, max: 1, levels: ALL_LEVELS,
181
+ description: 'A memória-decay ALSÓ KORLÁTJA (dsgn-013 §2): az effektív szorzó sosem esik ez alá → a régi memória '
182
+ + 'lesüllyed a rangsorban, de a vektor-keresésben MÉG MEGTALÁLHATÓ (`> 0` → a `weight ≤ 0 → kizárt` szabályt '
183
+ + 'nem lépi át). `0` = a degradáció akár teljesen elnyomhat (de a tartalom akkor is megmarad Mongo-ban).',
184
+ },
185
+ 'memory.maxFrequencyBoost': {
186
+ type: 'number', default: 2.5, min: 1, max: 100, levels: ALL_LEVELS,
187
+ description: 'A felidézés-gyakoriság (frequency) rangsor-boost FELSŐ KORLÁTJA (eval-lelet 2026-06-25): a nyers '
188
+ + '`1 + log2(1+recallCount)` KORLÁTLAN (gyakran-felidézett entry-re ~5×), ami multiplikatívan elnyomja a '
189
+ + 'koszinusz-relevanciát (a „forró" de alig-releváns memória eltemeti a releváns „hideget"). Itt plafonozzuk. '
190
+ + 'Default 2.5. Nagyobb = a gyakoriság erősebben rangsorol (a régi korlátlan viselkedéshez állítsd magasra).',
191
+ },
192
+ 'memory.activationWeight': {
193
+ type: 'number', default: 0.35, min: 0, max: 1, levels: ALL_LEVELS,
194
+ description: 'Az aktiváció-szorzó TEMPER-súlya (α, eval-lelet 2026-06-25): a nyers decay-szorzót az 1 felé húzza '
195
+ + '(`1 + α·(raw−1)`) → az aktiváció FINOMAN modulál, a koszinusz-RELEVANCIA dominál (de a recent/frequent még '
196
+ + 'kissé előrébb). MINDKÉT véglet kompresszálódik: a „forró" entry nem boostolódik túl, ÉS a releváns „hideg" '
197
+ + 'entry nem zuhan a floor-ra. `1` = a régi (temper-mentes) viselkedés. Default 0.35.',
198
+ },
199
+ 'memory.reactivateOnRead': {
200
+ type: 'boolean', default: true, levels: ALL_LEVELS,
201
+ description: 'Reaktiváció-on-read (dsgn-013 §4): a `read` által VISSZAADOTT memory-hitek „új energiát kapnak" '
202
+ + '(lastRecalledAt=most, recallCount++, dormant=false) — a felidézés megerősíti az emléket (feljebb rangsorol, '
203
+ + 'és ha alvó volt, visszakerül a hot készletbe). Async, NEM blokkolja a read-et. `false` → nincs frissítés.',
204
+ },
205
+ 'memory.dormancyEnabled': {
206
+ type: 'boolean', default: true, levels: GLOBAL_ONLY,
207
+ description: 'Dormancy (dsgn-013 §5): a régóta-nem-felidézett, ritkán-használt memóriák ALVÓ-vá jelölése — NEM '
208
+ + 'hidratálódnak a hot LVS-poolba (RAM-/boot-spórolás), de COLD (Mongo) kereséssel elérhetők + megtaláláskor '
209
+ + 'reaktiválódnak. SOHA nem törlés. `false` → minden memória hidratálódik (a mai viselkedés).',
210
+ },
211
+ 'memory.dormantMinAgeDays': {
212
+ type: 'number', default: 180, min: 1, max: 3650, integer: true, levels: GLOBAL_ONLY,
213
+ description: 'A DORMANT-küszöb minimális inaktivitása napban (dsgn-013 §5.1): egy memória CSAK akkor lehet alvó, '
214
+ + 'ha legalább ennyi napja nem idéztük fel (biztonsági alsó kor — a friss memória sosem alvó). Default 180.',
215
+ },
216
+ 'memory.dormantMaxRecallCount': {
217
+ type: 'number', default: 2, min: 0, max: 1000, integer: true, levels: GLOBAL_ONLY,
218
+ description: 'A DORMANT-küszöb recall-gátja (dsgn-013 §5.1): a memória CSAK akkor lehet alvó, ha ennél kevesebbszer '
219
+ + 'idéztük fel — a gyakran-használt (≥ ennyi recall) memória SOHA nem alvó. Default 2.',
220
+ },
221
+ 'memory.coldSearchEnabled': {
222
+ type: 'boolean', default: true, levels: ALL_LEVELS,
223
+ description: 'Cold-search (dsgn-013 §5.3): ha egy memória-read a HOT poolból kevés találatot ad (a dormant entry-k '
224
+ + 'nincsenek ott), a dormant készleten Mongo-oldali koszinusz-keresés fut (lassabb, de teljes), és a talált '
225
+ + 'entry-ket REAKTIVÁLJA (visszakerülnek a hot készletbe). `false` → a dormant entry-k csak re-scan/aktiváláskor jönnek vissza.',
226
+ },
227
+ 'memory.coldSearchMaxCandidates': {
228
+ type: 'number', default: 5000, min: 100, max: 100000, integer: true, levels: ALL_LEVELS,
229
+ description: 'A cold-search dormant-scan FELSŐ KORLÁTJA (biztonsági memóriakorlát, dsgn-013 §5.3): a Mongo-koszinusz '
230
+ + 'legfeljebb ennyi dormant entry vektorát tölti be egy keresésnél. Default 5000.',
231
+ },
232
+ 'memory.supersededPenalty': {
233
+ type: 'number', default: 0.25, min: 0, max: 1, levels: ALL_LEVELS,
234
+ description: 'A LEVÁLTOTT (superseded) memória ranking-PENALTYje (dsgn-013 §6): egy additív memory-fájl régi, már '
235
+ + 'átírt tartalma (pl. kompaktált MEMORY.md korábbi bejegyzései) ezzel a szorzóval süllyed a rangsorban — a friss '
236
+ + 'verzió fölébe kerül, de a régi MEGMARAD (cold-kereshető). `1` = nincs penalty; `0` = teljesen elnyomott (de megőrzött).',
237
+ },
107
238
  // --- 4.2. Chunking — fájl-típusonként (fogyasztó: dsgn-004) ---
108
239
  'chunk.ts.maxSize': {
109
240
  type: 'number', default: 1500, min: 200, max: 8000, integer: true, levels: ALL_LEVELS,
@@ -236,10 +367,19 @@ exports.CONFIG_CATALOG = {
236
367
  + 'KÖZÖTT vár (az utolsó után nem), és a `dryRun` NEM vár (nincs valódi embed-munka).',
237
368
  },
238
369
  'scan.projectCooldownMs': {
239
- type: 'number', default: 0, min: 0, max: 60000, integer: true, levels: ALL_LEVELS,
240
- description: 'Kikapcsolható cooldown (ms) a PROJEKTEK KÖZÉ a `scan-projects` soros multi-scan-jében — a sok-'
241
- + 'projektes (L2/L6) beolvasás ne thrash-elje a gépet. Default `0` = KIKAPCSOLVA; nagy futásra pl. 2000–'
242
- + '5000. A CLI `--cooldown <ms>` felülírja ezt a config-default-ot (a projektek KÖZÖTT vár, az utolsó után nem).',
370
+ type: 'number', default: 10000, min: 0, max: 60000, integer: true, levels: ALL_LEVELS,
371
+ description: 'Cooldown (ms) a PROJEKTEK KÖZÉ a `scan-projects` soros multi-scan-jében — a sok-projektes '
372
+ + '(L2/L6) beolvasás ne thrash-elje a gépet. Default `10000` (10s). FELTÉTELES (user-FR 2026-06-22): CSAK '
373
+ + 'akkor vár, ha az ELŐZŐ projekt VALÓDI embed-munkát végzett ( `scan.projectCooldownMinChunks` új+módosult '
374
+ + 'chunk) — ha minden `equal` volt (0 embed), nincs gép-terhelés → NINCS cooldown. `0` = teljesen KI. A CLI '
375
+ + '`--cooldown <ms>` felülírja. (A projektek KÖZÖTT vár, az utolsó után nem.)',
376
+ },
377
+ 'scan.projectCooldownMinChunks': {
378
+ type: 'number', default: 100, min: 0, max: 100000, integer: true, levels: ALL_LEVELS,
379
+ description: 'A feltételes projekt-cooldown küszöbe (user-FR 2026-06-22): a `scan-projects` CSAK akkor vár a '
380
+ + 'projektek között, ha az előző projekt ennyi VAGY több új+módosult (ténylegesen embeddelt) chunkot termelt. '
381
+ + 'Default `100` — a kis/`equal`-csak projektek után nincs felesleges szünet (a cooldown a helyi embed-terhelés '
382
+ + 'tehermentesítésére van). `0` = mindig vár (ha a cooldownMs > 0).',
243
383
  },
244
384
  'scan.estimateMsPerChunk': {
245
385
  type: 'number', default: 250, min: 1, max: 10000, integer: true, levels: ALL_LEVELS,
@@ -251,6 +391,20 @@ exports.CONFIG_CATALOG = {
251
391
  description: 'A futás-közbeni scan-progress log min. időköze (ms) — a nagy scan haladását ennyente jelzi '
252
392
  + 'a konzolon (`[famIngest]` activity-sor). A végső 100% mindig kibocsátódik.',
253
393
  },
394
+ 'scan.stallThresholdMs': {
395
+ type: 'number', default: 120000, min: 5000, max: 1800000, integer: true, levels: ALL_LEVELS,
396
+ description: 'STALL-küszöb (ms, FEAT-008 Phase 5 — „jobb job kezelés"): ha egy FUTÓ scan-job ennyi ideje nem '
397
+ + 'mutat haladást (sem per-target befejezés, sem within-target progress), a `status`-ban `stalled:true` jelenik '
398
+ + 'meg. CSAK JELZÉS (nem szakítja meg automatikusan) — a user/CLI dönt (poll tovább / `cancel`). Az LM Studio '
399
+ + 'crash/hang determinisztikus felismeréséhez. Default `120000` (2 perc).',
400
+ },
401
+ 'scan.maxResumeCount': {
402
+ type: 'number', default: 5, min: 0, max: 100, integer: true, levels: ALL_LEVELS,
403
+ description: 'AUTO-RESUME cap (FEAT-008 Phase 5): egy megszakadt job-ot a szerver legfeljebb ennyiszer indít újra '
404
+ + 'a startup-resume-on. Efölött a job `failed` (failureReason="resume-cap túllépve") — így egy folyton-elakadó '
405
+ + '(pl. mindig ugyanazon a tartalmon összeomló) job nem indul újra a végtelenségig minden restartkor. `0` = '
406
+ + 'soha ne resume-olj automatikusan.',
407
+ },
254
408
  // --- 4.6. Reference / scope (fogyasztó: dsgn-002) ---
255
409
  'reference.cacheTtlMs': {
256
410
  type: 'number', default: 60000, min: 0, max: 3600000, integer: true, levels: GLOBAL_ONLY,
@@ -111,6 +111,8 @@ exports.FAM_ERROR_CODES = {
111
111
  // --- Vector (FAM-VEC-*) ---
112
112
  /** Mongo→in-memory pool hidratálás sikertelen boot-on (BFR-AM-001). */
113
113
  vecHydrate: 'FAM-VEC-HYDRATE-001',
114
+ /** A hidratálás-bevárás (readiness-coordinator) hibája: a tár hidratálása MEGBUKOTT vagy a várakozás time-outolt. */
115
+ vecHydrateWait: 'FAM-VEC-HYDRATE-002',
114
116
  /** vectorSearch hiba (dimenzió / üres pool / searchMode). */
115
117
  vecSearch: 'FAM-VEC-SEARCH-001',
116
118
  /** contentVector Mongo-persist sikertelen (BFR-AM-005). */
@@ -8,55 +8,25 @@ const os = tslib_1.__importStar(require("os"));
8
8
  const path = tslib_1.__importStar(require("path"));
9
9
  const fsm_dynamo_1 = require("@futdevpro/fsm-dynamo");
10
10
  /**
11
- * `FAM_Console_Util` (FEAT-001) `fam start`-kor (a PRIMARY példánynál) megnyit egy KÜLÖN, vizuálisan
12
- * látható terminál-ablakot, ami a szerver folyó tevékenységét mutatja.
11
+ * `FAM_Console_Util` — a leválasztott (detached) FAM REST-szerver indítása a SAJÁT, LÁTHATÓ konzol-ablakában.
13
12
  *
14
- * **Miért nem a process saját konzolja?** MCP-stdio módban a process stdout-ja a JSON-RPC csatorna —
15
- * azt NEM szabad konzol-ablakká tenni. Ezért a látható ablak egy KÜLÖN monitor-process, ami a szerver
16
- * **activity-logját tail-eli**: a `tee`-vel a `console.*` kimenetet egy log-fájlba is írjuk (a meglévő
17
- * stderr/stdout mellé), és egy platform-natív terminál-ablakot indítunk, ami azt a fájlt követi.
13
+ * **A szerver MAGA fut egy látható terminál-ablakban** (user-direktíva 2026-06-22: a FAM-szervernek MINDIG
14
+ * saját látható ablakban kell megnyílnia"): a `spawnServerWindow` egy platform-natív terminál-ablakot nyit, ami
15
+ * KÖZVETLENÜL a `node`-ot futtatja → az ablak a szerver élő stdout/stderr-jét mutatja. Az ablak `detached` +
16
+ * `unref()` → TELJESEN FÜGGETLEN a hívótól (CLI / agent / subagent): amikor a hívó végez, az ablak NEM záródik be.
18
17
  *
19
- * **Default ON.** Kikapcsolható: `FAM_SHOW_CONSOLE=false|0|off|no` (a docker-compose ezt állítja, mert
20
- * a konténerben nincs GUI). Minden lépés **fail-safe** (try/catch) az ablak hibája SOHA nem töri a bootot.
18
+ * **Nincs „activity-monitor" ablak.** A régi FEAT-001 tail-monitor (log-fájlt tail-elő külön ablak) **VÉGLEG
19
+ * ELTÁVOLÍTVA** (user-direktíva 2026-06-22: „a monitor-ablakot soha többé sehol; minden kapcsolódó kódot törölj").
20
+ * Egy szerver = EGY ablak (a szerver konzolja), semmi más.
21
21
  *
22
- * **Részletesség (`isVerbose`, `FAM_CONSOLE_VERBOSE`):** default VERBOSE → MINDEN `console.*` log-sor a
23
- * monitorba kerül (user-direktíva). `FAM_CONSOLE_VERBOSE=false` kurált allowlist (`isActivityRelevant`).
22
+ * **File-log (`installFileLog`):** opcionálisan a `console.*` kimenet egy fájlba is rácsatolható (`FAM_LOG_FILE`),
23
+ * hogy a logok a látható ablak MELLETT fájlból is tail-elhetők legyenek (a `fam serve` ezt állítja be a detached szerverre).
24
24
  */
25
25
  class FAM_Console_Util {
26
26
  /**
27
- * A monitor-lock **frissesség-küszöbe** (ms): ha a lockfile mtime-ja ennél régebbi, a monitor-ablakot
28
- * HALOTTNAK tekintjük (bezárták) új nyitható. A nyitott ablak `MONITOR_HEARTBEAT_MS`-enként frissíti a
29
- * lockot (a PID-alapú check helyett az törékeny: async PID-write-race a `start`-detach miatt + Windows
30
- * PID-reuse → FELESLEGES dupla ablakok szerver-restartkor; a frissesség mindkettőt megoldja).
31
- */
32
- static MONITOR_STALE_MS = 10_000;
33
- /** A monitor-ablak heartbeat-periódusa (ms): ennyente frissíti a lockfile mtime-ját, amíg él. */
34
- static MONITOR_HEARTBEAT_MS = 3_000;
35
- /**
36
- * Az activity-monitor-ablak engedélyezett-e. **Default OFF** (opt-in) — user-direktíva 2026-06-21: a külön
37
- * FEAT-001 tail-monitort kivettük, mert a `fam serve` saját LÁTHATÓ konzol-ablakot ad (az a szerver-aktivitás),
38
- * így a külön monitor redundáns. Csak EXPLICIT `FAM_SHOW_CONSOLE=true|1|on|yes` esetén nyílik (ritka:
39
- * pl. ha valaki a stdio-MCP `fam start`-ot primary-ként futtatja és mégis tail-ablakot szeretne).
40
- */
41
- static isEnabled() {
42
- const raw = (process.env.FAM_SHOW_CONSOLE ?? '').trim().toLowerCase();
43
- return raw === 'true' || raw === '1' || raw === 'on' || raw === 'yes';
44
- }
45
- /**
46
- * Az activity-LOG **verbose**-e (`FAM_CONSOLE_VERBOSE`). **Default ON** (user-direktíva: a monitor MINDEN
47
- * log-sort írjon ki) → a `console.*` minden nem-üres sora a monitor-ablakba kerül. `false|0|off|no` → a
48
- * **kurált** allowlist (csak `[fam…]` + mongo-connect + hiba/figyelmeztetés; a boot-zaj kimarad). A kettő
49
- * trade-off: VERBOSE = teljes átláthatóság (de zajosabb), kurált = csak a lényeg.
50
- */
51
- static isVerbose() {
52
- const raw = (process.env.FAM_CONSOLE_VERBOSE ?? '').trim().toLowerCase();
53
- return raw !== 'false' && raw !== '0' && raw !== 'off' && raw !== 'no';
54
- }
55
- /**
56
- * **File-log (`FAM_LOG_FILE`)** — ha az env be van állítva, a `console.*` MINDEN sorát (SZŰRETLEN) rácsatolja
57
- * arra a fájlra, FÜGGETLENÜL a monitor-ablaktól (`FAM_SHOW_CONSOLE`). A `fam serve` ezt állítja be a detached
58
- * szerverre → a szerver logjai (köztük az import-progress) **FÁJLBÓL OLVASHATÓK** (tail-elhetők), nem csak a
59
- * GUI-ablakból. Append-mód (boot-szeparátorral), fail-safe (a log-write SOHA nem dönti meg a szervert).
27
+ * **File-log (`FAM_LOG_FILE`)** ha az env be van állítva, a `console.*` MINDEN sorát rácsatolja arra a fájlra
28
+ * (a látható ablak MELLETT). A `fam serve` ezt állítja a detached szerverre a logok (köztük az import-progress)
29
+ * fájlból is OLVASHATÓK/tail-elhetők. Append-mód (boot-szeparátorral), fail-safe (a log-write SOHA nem dönti meg a szervert).
60
30
  */
61
31
  static installFileLog() {
62
32
  const logPath = (process.env.FAM_LOG_FILE ?? '').trim();
@@ -100,223 +70,19 @@ class FAM_Console_Util {
100
70
  console.error = wrap(console.error);
101
71
  }
102
72
  /**
103
- * A PRIMARY szerver activity-konzol-ablakának megnyitása: log-fájl-tee + platform-natív monitor-ablak.
104
- * Disabled (`FAM_SHOW_CONSOLE` off) vagy bármilyen hiba esetén CSENDES no-op (a boot folytatódik).
105
- */
106
- static openActivityConsole(set) {
107
- if (!FAM_Console_Util.isEnabled()) {
108
- return;
109
- }
110
- try {
111
- const logPath = path.join(os.tmpdir(), `fam-activity-${set.port}.log`);
112
- const lockPath = path.join(os.tmpdir(), `fam-activity-${set.port}.lock`);
113
- // A log-fájl PERSISTÁL a (reconnect miatti) szerver-újraindulások között: header CSAK ha új,
114
- // különben separator. A folyó monitor-ablak UGYANAZT a fájlt tail-eli, így a tevékenység
115
- // akkumulálódik (nem csonkol minden bootkor — ez okozta a „nincs érdemi info"-t).
116
- const mode = FAM_Console_Util.isVerbose() ? 'VERBOSE (minden log)' : 'kurált (allowlist)';
117
- if (!fs.existsSync(logPath)) {
118
- fs.writeFileSync(logPath, `# FAM activity log — port ${set.port} — ${new Date().toISOString()} — mód: ${mode}\n`);
119
- }
120
- else {
121
- fs.appendFileSync(logPath, `# --- szerver-process (újra)indult — ${new Date().toISOString()} — mód: ${mode} ---\n`);
122
- }
123
- FAM_Console_Util.teeConsoleToFile(logPath);
124
- // EGYETLEN monitor-ablak: ha már fut egy (a lockfile-beli PID él), NEM nyitunk újat — csak tee-zünk
125
- // a közös logba (ezt mutatja a meglévő ablak). Így a reconnect-churn NEM spamel új terminálokat.
126
- if (FAM_Console_Util.isMonitorAlive(lockPath)) {
127
- return;
128
- }
129
- FAM_Console_Util.spawnMonitorWindow(logPath, lockPath, set.port);
130
- }
131
- catch (error) {
132
- fsm_dynamo_1.DyFM_Log.warn(`[FAM console] activity-ablak megnyitása sikertelen (a szerver fut tovább): ${error?.message}`);
133
- }
134
- }
135
- /**
136
- * Fut-e már egy ÉLŐ monitor-ablak ehhez a porthoz? **Frissesség-alapú** (a régi PID-check helyett): a nyitott
137
- * ablak `MONITOR_HEARTBEAT_MS`-enként frissíti a lockfile-t (mtime), így ha az mtime `MONITOR_STALE_MS`-nél
138
- * frissebb → él (reuse, NEM nyitunk újat); ha régebbi → az ablakot bezárták → új nyitható. Ez immun a Windows
139
- * `start`-detach okozta async PID-write-race-re ÉS a PID-reuse-ra (a két ok, ami szerver-restartkor dupla
140
- * ablakot nyitott). Hiányzó/olvashatatlan lock → nincs élő ablak.
141
- */
142
- static isMonitorAlive(lockPath) {
143
- try {
144
- if (!fs.existsSync(lockPath)) {
145
- return false;
146
- }
147
- const ageMs = Date.now() - fs.statSync(lockPath).mtimeMs;
148
- return ageMs < FAM_Console_Util.MONITOR_STALE_MS;
149
- }
150
- catch {
151
- return false;
152
- }
153
- }
154
- /** A `console.*` kimenet rácsatolása a log-fájlra (a meglévő stderr/stdout MELLÉ — nem helyette). */
155
- static teeConsoleToFile(logPath) {
156
- const append = (line) => {
157
- try {
158
- fs.appendFileSync(logPath, `${line}\n`);
159
- }
160
- catch {
161
- /* a fájl-append hibája soha nem dönti meg a szervert */
162
- }
163
- };
164
- const stringify = (value) => {
165
- if (typeof value === 'string') {
166
- return value;
167
- }
168
- try {
169
- return JSON.stringify(value);
170
- }
171
- catch {
172
- return String(value);
173
- }
174
- };
175
- const wrap = (original) => {
176
- return (...args) => {
177
- original(...args);
178
- // A stderr/stdout a TELJES kimenetet kapja (változatlan); az activity-LOGba viszont CSAK a
179
- // tevékenység-releváns sorok kerülnek — a boot-banner (ASCII-art, endpoint-mount, mongoose-
180
- // warning) így nem szemeteli az ablakot, és a [fam*] activity nem süllyed el (a user-panasz).
181
- const line = args.map(stringify).join(' ');
182
- if (FAM_Console_Util.isActivityRelevant(line)) {
183
- append(line);
184
- }
185
- };
186
- };
187
- console.log = wrap(console.log.bind(console));
188
- console.info = wrap(console.info.bind(console));
189
- console.warn = wrap(console.warn.bind(console));
190
- console.error = wrap(console.error.bind(console));
191
- console.debug = wrap(console.debug.bind(console));
192
- }
193
- /**
194
- * Activity-releváns-e egy log-sor → bekerül-e a monitor-ablakba. **VERBOSE módban** (default, `isVerbose`)
195
- * MINDEN nem-üres sor átmegy (a user „minden logot" direktívája). **Kurált módban** (`FAM_CONSOLE_VERBOSE=
196
- * false`) allowlist: minden `[fam…]`/`[FAM …]` sor (famRead/famWrite/famIngest + `[FAM hydrate]`/`[FAM MCP]`
197
- * boot-státusz), a `Connected to MongoDB`, a `…successfully`, és MINDEN hiba/figyelmeztetés (hogy a problémák
198
- * SOHA ne tűnjenek el); minden más (a teljes Express/Dynamo boot-banner) kimarad (stderr-en marad).
199
- */
200
- static isActivityRelevant(line) {
201
- const trimmed = line.trim();
202
- if (!trimmed) {
203
- return false;
204
- }
205
- // VERBOSE (default): minden nem-üres sor a monitorba — teljes átláthatóság (user-direktíva).
206
- if (FAM_Console_Util.isVerbose()) {
207
- return true;
208
- }
209
- if (/^\[fam/i.test(trimmed)) {
210
- return true;
211
- }
212
- if (/connected to mongodb|successfully/i.test(trimmed)) {
213
- return true;
214
- }
215
- return /\b(error|warn|warning|fail|failed|exception|critical)\b/i.test(trimmed);
216
- }
217
- /**
218
- * Platform-natív terminál-ablak indítása, ami a log-fájlt tail-eli (detached, fail-safe). Az ablak
219
- * `MONITOR_HEARTBEAT_MS`-enként **frissíti a lockot** (heartbeat), így a következő (reconnect/restart miatti)
220
- * boot az mtime-frissességből látja, hogy él → NEM nyit újat (`isMonitorAlive`). A spawn ELŐTT egy
221
- * provisional friss lockot is írunk (a közel-egyidejű boot-race ellen).
222
- */
223
- static spawnMonitorWindow(logPath, lockPath, port) {
224
- try {
225
- FAM_Console_Util.touchLock(lockPath); // provisional friss mtime — a spawn alatti race-dupla ellen
226
- if (process.platform === 'win32') {
227
- // A monitor-logikát egy temp `.ps1`-be írjuk + `-File`-lal futtatjuk → NINCS `-Command`-escaping-
228
- // rémálom (a sok `()[]{}|::`). A script HEARTBEAT-eli a lockot (a régi async `$PID`-write helyett —
229
- // az okozta a PID-race/reuse dupla-ablakokat), és StreamReader-rel STREAMING-tail-el (UTF-8, a
230
- // `Get-Content -Wait` helyett, hogy a heartbeat a [fam*]-zaj alatt is frissen tartsa a lockot).
231
- const scriptPath = path.join(os.tmpdir(), `fam-activity-monitor-${port}.ps1`);
232
- fs.writeFileSync(scriptPath, FAM_Console_Util.windowsMonitorScript(logPath, lockPath, port));
233
- const child = (0, child_process_1.spawn)(process.env.ComSpec ?? 'cmd.exe', ['/c', 'start', '', 'powershell', '-NoExit', '-ExecutionPolicy', 'Bypass', '-File', scriptPath], { detached: true, stdio: 'ignore', windowsHide: false });
234
- child.unref();
235
- return;
236
- }
237
- // mac/linux: a tail MELLETT egy heartbeat-loop (a lockot frissíti, amíg a tail — így az ablak — él);
238
- // a `kill -0 $tailPid` figyeli a tail-t, így az ablak bezárásakor a loop is kilép (nincs árva heartbeat).
239
- const heartbeatSec = Math.max(1, Math.round(FAM_Console_Util.MONITOR_HEARTBEAT_MS / 1000));
240
- const shellCmd = `tail -n 200 -f '${logPath}' & tp=$!; `
241
- + `while kill -0 $tp 2>/dev/null; do : > '${lockPath}'; sleep ${heartbeatSec}; done`;
242
- if (process.platform === 'darwin') {
243
- const script = `tell application "Terminal" to do script "${shellCmd.replace(/"/g, '\\"')}"`;
244
- const child = (0, child_process_1.spawn)('osascript', ['-e', script], { detached: true, stdio: 'ignore' });
245
- child.unref();
246
- return;
247
- }
248
- const terminal = FAM_Console_Util.findLinuxTerminal();
249
- if (!terminal) {
250
- fsm_dynamo_1.DyFM_Log.warn('[FAM console] nincs ismert Linux terminál-emulátor — az activity-ablak kihagyva '
251
- + `(a log a fájlban: ${logPath}; tail-eld kézzel, vagy FAM_SHOW_CONSOLE=false).`);
252
- return;
253
- }
254
- const child = (0, child_process_1.spawn)(terminal, ['-e', 'sh', '-c', shellCmd], { detached: true, stdio: 'ignore' });
255
- child.unref();
256
- }
257
- catch (error) {
258
- fsm_dynamo_1.DyFM_Log.warn(`[FAM console] a monitor-ablak indítása sikertelen: ${error?.message}`);
259
- }
260
- }
261
- /**
262
- * A Windows monitor-`.ps1` tartalma: UTF-8 streaming-tail (StreamReader; a `Get-Content -Wait` helyett, hogy
263
- * a heartbeat akkor is frissüljön, ha sok a log-sor) + `MONITOR_HEARTBEAT_MS`-enkénti lock-frissítés (a
264
- * heartbeat). Az ablak bezárásakor a PowerShell-process megszűnik → a lock mtime elöregszik → `isMonitorAlive`
265
- * false → a következő boot új ablakot nyit (helyesen). A path-okban a `'`-eket megdupláljuk (PS escape, defenzív).
266
- */
267
- static windowsMonitorScript(logPath, lockPath, port) {
268
- const log = logPath.replace(/'/g, "''");
269
- const lock = lockPath.replace(/'/g, "''");
270
- return [
271
- 'try { [Console]::OutputEncoding=[System.Text.Encoding]::UTF8 } catch {}',
272
- `try { $host.UI.RawUI.WindowTitle='FAM activity (${port})' } catch {}`,
273
- `$log='${log}'`,
274
- `$lock='${lock}'`,
275
- 'if (Test-Path -LiteralPath $log) { Get-Content -LiteralPath $log -Tail 200 -Encoding UTF8 | Write-Host }',
276
- "$fs=[System.IO.File]::Open($log,'Open','Read','ReadWrite')",
277
- '$sr=New-Object System.IO.StreamReader($fs,[System.Text.Encoding]::UTF8)',
278
- '$sr.BaseStream.Seek(0,[System.IO.SeekOrigin]::End) | Out-Null',
279
- '$beat=0',
280
- 'while ($true) {',
281
- ' $line=$sr.ReadLine()',
282
- ' if ($null -ne $line) { Write-Host $line } else { Start-Sleep -Milliseconds 400 }',
283
- ' $now=[DateTimeOffset]::Now.ToUnixTimeMilliseconds()',
284
- ` if (($now - $beat) -ge ${FAM_Console_Util.MONITOR_HEARTBEAT_MS}) { [System.IO.File]::WriteAllText($lock,[string]$now); $beat=$now }`,
285
- '}',
286
- ].join('\r\n');
287
- }
288
- /** A lockfile mtime-jának frissítése (a monitor „élő" jelzése, heartbeat). Fail-safe. */
289
- static touchLock(lockPath) {
290
- try {
291
- fs.writeFileSync(lockPath, String(Date.now()));
292
- }
293
- catch {
294
- /* a lock-write hibája soha nem dönti meg a szervert */
295
- }
296
- }
297
- /** Az első elérhető Linux terminál-emulátor (PATH-on), vagy null. */
298
- static findLinuxTerminal() {
299
- const candidates = ['x-terminal-emulator', 'gnome-terminal', 'konsole', 'xfce4-terminal', 'xterm'];
300
- for (const candidate of candidates) {
301
- for (const dir of (process.env.PATH ?? '').split(path.delimiter)) {
302
- if (dir && fs.existsSync(path.join(dir, candidate))) {
303
- return candidate;
304
- }
305
- }
306
- }
307
- return null;
308
- }
309
- /**
310
- * Egy VISIBLE, OLVASHATÓ terminál-ablak nyitása, amiben a megadott parancs (a FAM REST-szerver) FUT. A
311
- * `fam serve` (FEAT-003) ezt használja: a `start`/terminál TELJESEN LEVÁLASZTJA a szervert a hívóról (saját
312
- * konzol-ablak), így túléli a CLI-t, és az ablak a szerver **élő stdout/stderr-jét** mutatja közvetlenül.
313
- * Ezért a szervert `FAM_SHOW_CONSOLE=false`-szal indítjuk (a serve env-override-ja) — a FEAT-001 külön
314
- * tail-monitor-ablak ehhez **redundáns** (nem nyílik 2. ablak). Fail-safe (a hívó `--foreground`-ra eshet vissza).
73
+ * A FAM REST-szerver indítása EGY LÁTHATÓ, DETACHED terminál-ablakban, ami KÖZVETLENÜL a `node`-ot futtatja
74
+ * (az ablak a szerver konzolja). A `start`/terminál TELJESEN LEVÁLASZTJA a szervert a hívóról → **túléli a
75
+ * hívó (CLI / agent / subagent) kilépését** (amikor a hívó végez, az ablak NEM záródik be). Az ablak a kilépés
76
+ * UTÁN is nyitva marad (`-NoExit` / `exec $SHELL`), hogy a boot-hiba látható maradjon. Fail-safe (`--foreground`
77
+ * fallback, ha nincs terminál).
78
+ *
79
+ * `command` = `process.execPath` (node), `args` = `[--max-old-space-size=…, build/src/index.js]`. Az env-override-ok
80
+ * a futtató shellbe kerülnek (PS `$env:` / POSIX `export`).
315
81
  */
316
82
  static spawnServerWindow(set) {
317
83
  if (process.platform === 'win32') {
318
- // Launcher-`.ps1` (mint a FEAT-001 monitor) → NINCS `-Command`-escaping-rémálom; `-NoExit` → az ablak
319
- // a szerver kilépése (vagy crash) UTÁN is nyitva marad (a hiba látható).
84
+ // Launcher-`.ps1` → NINCS `-Command`-escaping-rémálom; `-NoExit` → az ablak a szerver kilépése/crash UTÁN is
85
+ // nyitva marad. A `cmd /c start` ÚJ, látható ablakot nyit; a `detached`+`unref` leválasztja a hívóról (túléli).
320
86
  const scriptPath = path.join(os.tmpdir(), `fam-server-${set.port}.ps1`);
321
87
  fs.writeFileSync(scriptPath, FAM_Console_Util.windowsServerScript(set));
322
88
  const child = (0, child_process_1.spawn)(process.env.ComSpec ?? 'cmd.exe', ['/c', 'start', '', 'powershell', '-NoExit', '-ExecutionPolicy', 'Bypass', '-File', scriptPath], { detached: true, stdio: 'ignore', windowsHide: false });
@@ -344,9 +110,9 @@ class FAM_Console_Util {
344
110
  child.unref();
345
111
  }
346
112
  /**
347
- * A Windows szerver-launcher `.ps1` tartalma: UTF-8 konzol + ablakcím, az env-override-ok (`$env:X='...'`),
348
- * majd a szerver futtatása (`& command args`). A `'`-eket megdupláljuk (PS string-escape). A `-NoExit`
349
- * (a spawn-oldalon) tartja nyitva az ablakot kilépés után.
113
+ * A Windows szerver-launcher `.ps1` tartalma (PURE — unit-tesztelhető): UTF-8 konzol + ablakcím, az env-override-ok
114
+ * (`$env:X='...'`), majd a szerver futtatása (`& command args`). A `'`-eket megdupláljuk (PS string-escape). A
115
+ * `-NoExit` (a spawn-oldalon) tartja nyitva az ablakot kilépés után.
350
116
  */
351
117
  static windowsServerScript(set) {
352
118
  const esc = (value) => value.replace(/'/g, "''");
@@ -363,5 +129,17 @@ class FAM_Console_Util {
363
129
  lines.push("Write-Host \"[FAM szerver kilépett — exit $LASTEXITCODE — 'fam serve --restart' újraindítja]\"");
364
130
  return lines.join('\r\n');
365
131
  }
132
+ /** Az első elérhető Linux terminál-emulátor (PATH-on), vagy null. */
133
+ static findLinuxTerminal() {
134
+ const candidates = ['x-terminal-emulator', 'gnome-terminal', 'konsole', 'xfce4-terminal', 'xterm'];
135
+ for (const candidate of candidates) {
136
+ for (const dir of (process.env.PATH ?? '').split(path.delimiter)) {
137
+ if (dir && fs.existsSync(path.join(dir, candidate))) {
138
+ return candidate;
139
+ }
140
+ }
141
+ }
142
+ return null;
143
+ }
366
144
  }
367
145
  exports.FAM_Console_Util = FAM_Console_Util;
@@ -11,6 +11,7 @@ const fam_knowledge_data_model_1 = require("../_models/data-models/fam-knowledge
11
11
  const fam_memory_data_model_1 = require("../_models/data-models/fam-memory.data-model");
12
12
  const fam_reference_data_model_1 = require("../_models/data-models/fam-reference.data-model");
13
13
  const fam_rules_data_model_1 = require("../_models/data-models/fam-rules.data-model");
14
+ const fam_scan_job_data_model_1 = require("../_models/data-models/fam-scan-job.data-model");
14
15
  const fam_scope_data_model_1 = require("../_models/data-models/fam-scope.data-model");
15
16
  /**
16
17
  * A FAM perzisztens collection-jeinek `dataParams`-listája (MP-1, dsgn-001) — **SSOT**.
@@ -32,6 +33,7 @@ exports.FAM_DB_MODELS = [
32
33
  fam_reference_data_model_1.famReference_dataParams, // fam_reference
33
34
  fam_scope_data_model_1.famScope_dataParams, // fam_scopes
34
35
  fam_ingest_run_data_model_1.famIngestRun_dataParams, // fam_ingest_runs
36
+ fam_scan_job_data_model_1.famScanJob_dataParams, // fam_scan_jobs (FEAT-008 Phase 4 — durable scan-job + auto-resume)
35
37
  // Config-réteg (MP-7, dsgn-007): DB-backed scoped config (global<table<scope).
36
38
  fam_config_data_model_1.famConfig_dataParams, // fam_config
37
39
  ];