@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
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FAM_RulePropagation_Util = void 0;
4
+ const fam_rule_scope_type_enum_1 = require("../../../_enums/fam-rule-scope.type-enum");
5
+ const fam_rule_injection_util_1 = require("./fam-rule-injection.util");
6
+ /**
7
+ * `FAM_RulePropagation_Util` (Rules-Management MP-5) — **pure** renderer + file-merger: a FAM-ban tárolt szabályokból
8
+ * egy generált, marker-keretezett blokkot képez a projekt-`CLAUDE.md`-be, hogy a **FAM-NÉLKÜLI** projekt-agentek is
9
+ * lássák a szabályokat. Dedup: `ruleId` (+ `derivedFrom` provenance) → 1 canonical entry/szabály (nem N duplikátum);
10
+ * `ruleId` nélkül content-alapú. A blokk scope szerint csoportosított + `importance` szerint rendezett.
11
+ *
12
+ * A merge IDEMPOTENS: a markerek között CSERÉL (update-if-stale), hiányzó marker → append (create-if-missing). A markeren
13
+ * KÍVÜLI kézi tartalmat sosem érinti.
14
+ */
15
+ class FAM_RulePropagation_Util {
16
+ static MARKER_START = '<!-- FAM-RULES (generated — do not edit by hand) -->';
17
+ static MARKER_END = '<!-- /FAM-RULES -->';
18
+ /** A scope-csoportok megjelenítési sorrendje + címe. */
19
+ static SCOPE_ORDER = [
20
+ { scope: fam_rule_scope_type_enum_1.FAM_RuleScope.global, title: 'Global (every system)' },
21
+ { scope: fam_rule_scope_type_enum_1.FAM_RuleScope.fdpGlobal, title: 'FDP-global (FDP ecosystem)' },
22
+ { scope: fam_rule_scope_type_enum_1.FAM_RuleScope.workspaceRoot, title: 'Workspace-root' },
23
+ { scope: fam_rule_scope_type_enum_1.FAM_RuleScope.projectInternal, title: 'Project-internal' },
24
+ { scope: fam_rule_scope_type_enum_1.FAM_RuleScope.projectType, title: 'Project-type' },
25
+ { scope: fam_rule_scope_type_enum_1.FAM_RuleScope.trigger, title: 'Trigger (context-specific)' },
26
+ ];
27
+ /**
28
+ * Dedup `ruleId` szerint: azonos `ruleId` → a legmagasabb effektív-importance-ű marad (a `derivedFrom`-propagált
29
+ * másolatok beolvadnak). `ruleId` nélküli szabályok content-prefix szerint deduplikálódnak. Új tömb (nem mutál).
30
+ */
31
+ static dedup(rules) {
32
+ const byKey = new Map();
33
+ for (const rule of rules) {
34
+ const key = rule.ruleId && rule.ruleId.trim().length
35
+ ? `id:${rule.ruleId.trim()}`
36
+ : `content:${(rule.content ?? '').trim().slice(0, 120)}`;
37
+ const existing = byKey.get(key);
38
+ if (!existing || fam_rule_injection_util_1.FAM_RuleInjection_Util.effectiveImportance(rule) > fam_rule_injection_util_1.FAM_RuleInjection_Util.effectiveImportance(existing)) {
39
+ byKey.set(key, rule);
40
+ }
41
+ }
42
+ return [...byKey.values()];
43
+ }
44
+ /** A generált blokk BELSŐ szövege (markerek nélkül): scope-csoportok, importance-rendezve. */
45
+ static renderBlock(rules, opts) {
46
+ const deduped = FAM_RulePropagation_Util.dedup(rules);
47
+ const lines = [];
48
+ lines.push(opts?.heading ?? '## FDP Agent Hard-Rules (synced from FAM)');
49
+ lines.push('');
50
+ lines.push('> Generated from the canonical rule set. Edit the rules in `documentations/rules/`, not here.');
51
+ for (const group of FAM_RulePropagation_Util.SCOPE_ORDER) {
52
+ const inScope = deduped
53
+ .filter((rule) => rule.ruleScope === group.scope)
54
+ .sort(fam_rule_injection_util_1.FAM_RuleInjection_Util.compare);
55
+ if (!inScope.length) {
56
+ continue;
57
+ }
58
+ lines.push('');
59
+ lines.push(`### ${group.title}`);
60
+ for (const rule of inScope) {
61
+ lines.push(FAM_RulePropagation_Util.formatRule(rule));
62
+ }
63
+ }
64
+ return lines.join('\n');
65
+ }
66
+ /** Egy szabály egy-soros bullet-je: `- **Title** (importance) — body`. */
67
+ static formatRule(rule) {
68
+ const content = (rule.content ?? '').trim();
69
+ const titleMatch = content.match(/^#{1,6}\s+(.+)$/m);
70
+ const title = titleMatch ? titleMatch[1].trim() : (rule.ruleId ?? 'rule');
71
+ const body = content.replace(/^#{1,6}\s+.+$/m, '').replace(/\s+/g, ' ').trim();
72
+ const importance = fam_rule_injection_util_1.FAM_RuleInjection_Util.effectiveImportance(rule);
73
+ return `- **${title}** (${importance})${body ? ` — ${body}` : ''}`;
74
+ }
75
+ /** A blokk marker-kerettel. */
76
+ static wrap(block) {
77
+ return `${FAM_RulePropagation_Util.MARKER_START}\n${block}\n${FAM_RulePropagation_Util.MARKER_END}`;
78
+ }
79
+ /**
80
+ * A wrapped blokk IDEMPOTENS beillesztése egy fájl-tartalomba: ha a markerek megvannak → a köztük lévő rész CSERÉL
81
+ * (a markeren kívüli kézi tartalom marad); ha nincs → a fájl VÉGÉRE fűzi (egy elválasztó üres sorral). Új string.
82
+ */
83
+ static mergeIntoFile(fileContent, wrappedBlock) {
84
+ const startIdx = fileContent.indexOf(FAM_RulePropagation_Util.MARKER_START);
85
+ const endIdx = fileContent.indexOf(FAM_RulePropagation_Util.MARKER_END);
86
+ if (startIdx >= 0 && endIdx > startIdx) {
87
+ const before = fileContent.slice(0, startIdx);
88
+ const after = fileContent.slice(endIdx + FAM_RulePropagation_Util.MARKER_END.length);
89
+ return `${before}${wrappedBlock}${after}`;
90
+ }
91
+ const base = fileContent.replace(/\s+$/, '');
92
+ return base.length ? `${base}\n\n${wrappedBlock}\n` : `${wrappedBlock}\n`;
93
+ }
94
+ }
95
+ exports.FAM_RulePropagation_Util = FAM_RulePropagation_Util;
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FAM_RuleScopeContext_Util = void 0;
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ const fam_project_discovery_util_1 = require("../../../_collections/fam-project-discovery.util");
7
+ /**
8
+ * `FAM_RuleScopeContext_Util` (MP-2) — a `cwd`-ből **pure** (csak fs-stat/olvasás, nincs hálózat/DB) levezeti, mely
9
+ * rule-TIER-ek érvényesek. Felfelé sétál a `cwd`-ből a legközelebbi projekt-gyökérig (`isProjectRoot`):
10
+ * - **inProject** = találtunk projekt-gyökeret a fán (a multi-projekt workspace-root NEM projekt-gyökér — nincs
11
+ * `package.json`/`.git` rajta —, így sosem téveszt projektnek).
12
+ * - **isFdp** = a legközelebbi projekt `package.json`-ja `@futdevpro` (név VAGY bármely dependency) → FDP-repo.
13
+ *
14
+ * A `projectType` SZÁNDÉKOSAN nem auto-detektált itt (a kanonikus projekt-típus-taxonómiát MP-4/MP-7 rögzíti) —
15
+ * explicit kontextusból jön. A `cwd` hiányában minden `false` (csak a `global` tier injektál → mindig helyes).
16
+ */
17
+ class FAM_RuleScopeContext_Util {
18
+ /** A felfelé-séta maximum mélysége (defenzív korlát; a tipikus projekt-mélység jóval ez alatt van). */
19
+ static MAX_WALK_UP = 12;
20
+ /** A `cwd`-ből levezetett kontextus (pure). */
21
+ static detect(cwd) {
22
+ if (!cwd || !cwd.trim()) {
23
+ return { isFdp: false, inProject: false };
24
+ }
25
+ const projectRoot = FAM_RuleScopeContext_Util.nearestProjectRoot(cwd);
26
+ if (!projectRoot) {
27
+ return { isFdp: false, inProject: false };
28
+ }
29
+ return {
30
+ isFdp: FAM_RuleScopeContext_Util.isFdpProject(projectRoot),
31
+ inProject: true,
32
+ projectName: fam_project_discovery_util_1.FAM_ProjectDiscovery_Util.projectName(projectRoot),
33
+ projectRoot: projectRoot,
34
+ };
35
+ }
36
+ /** A legközelebbi projekt-gyökér a `cwd`-ből felfelé (`isProjectRoot`), vagy `undefined`. */
37
+ static nearestProjectRoot(cwd) {
38
+ let dir = cwd;
39
+ for (let i = 0; i < FAM_RuleScopeContext_Util.MAX_WALK_UP; i++) {
40
+ if (fam_project_discovery_util_1.FAM_ProjectDiscovery_Util.isProjectRoot(dir)) {
41
+ return dir;
42
+ }
43
+ const parent = (0, path_1.dirname)(dir);
44
+ if (parent === dir) {
45
+ break; // elértük a fs-gyökeret
46
+ }
47
+ dir = parent;
48
+ }
49
+ return undefined;
50
+ }
51
+ /** FDP-projekt-e: a `package.json` `name` VAGY bármely dependency-kulcs `@futdevpro/`-val kezdődik. */
52
+ static isFdpProject(projectRoot) {
53
+ try {
54
+ const pkgPath = (0, path_1.join)(projectRoot, 'package.json');
55
+ if (!(0, fs_1.existsSync)(pkgPath)) {
56
+ return false;
57
+ }
58
+ const pkg = JSON.parse((0, fs_1.readFileSync)(pkgPath, 'utf-8'));
59
+ if (typeof pkg.name === 'string' && pkg.name.startsWith('@futdevpro/')) {
60
+ return true;
61
+ }
62
+ return [pkg.dependencies, pkg.devDependencies, pkg.peerDependencies].some((deps) => deps && typeof deps === 'object'
63
+ && Object.keys(deps).some((key) => key.startsWith('@futdevpro/')));
64
+ }
65
+ catch {
66
+ return false;
67
+ }
68
+ }
69
+ /** A projekt bare-neve (a `nearestProjectRoot` basenameje) — segéd a teszteknek/diagnosztikának. */
70
+ static basenameOf(path) {
71
+ return (0, path_1.basename)(path);
72
+ }
73
+ }
74
+ exports.FAM_RuleScopeContext_Util = FAM_RuleScopeContext_Util;
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FAM_GlobalRule_ControlService = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const mongoose_1 = tslib_1.__importDefault(require("mongoose"));
6
+ const fsm_dynamo_1 = require("@futdevpro/fsm-dynamo");
7
+ const error_codes_const_1 = require("../../../_collections/error-codes.const");
8
+ const fam_error_factory_util_1 = require("../../../_collections/fam-error-factory.util");
9
+ const scope_reference_1 = require("../../scope-reference");
10
+ const embedding_1 = require("../../embedding");
11
+ const fam_rules_data_model_1 = require("../../../_models/data-models/fam-rules.data-model");
12
+ /**
13
+ * `FAM_GlobalRule_ControlService` (user-FR 2026-06-22 — a FAM fő feature-je: globális hard-rule öröklés) — a
14
+ * `rules`-bejegyzések `globalRule` flagjének BE-/KIkapcsolása. A `globalRule:true` bejegyzéseket a retrieval
15
+ * MINDEN scope-szűrt `rules`-lekérdezésbe behúzza (scope-tól függetlenül) → a workspace-szintű hard-rules
16
+ * (E2E-hard-rule, always-master, naming/import-konvenciók) öröklődnek minden al-scope-ba.
17
+ *
18
+ * A jelölés a Mongo `fam_rules`-on `updateMany` (a flag NEM érinti a vektor-poolt → nincs re-hidratálás, a
19
+ * következő lekérdezés már a frissített prefilteren megy). Szelektor: `ids` VAGY `scopeFilter` (a scope subtree
20
+ * minden rule-ja) VAGY `tags`. `dryRun` → csak az érintett-szám.
21
+ */
22
+ class FAM_GlobalRule_ControlService {
23
+ static _instance;
24
+ issuer = 'FAM_GlobalRule_ControlService';
25
+ static getInstance() {
26
+ if (!FAM_GlobalRule_ControlService._instance) {
27
+ FAM_GlobalRule_ControlService._instance = new FAM_GlobalRule_ControlService();
28
+ }
29
+ return FAM_GlobalRule_ControlService._instance;
30
+ }
31
+ /** A `globalRule` flag beállítása a szelektornak megfelelő `rules`-bejegyzéseken (`updateMany`). */
32
+ async setGlobalRule(input) {
33
+ const global = input.global !== false;
34
+ const dryRun = input.dryRun === true;
35
+ const filter = { _deleted: null };
36
+ const ors = [];
37
+ if (input.ids && input.ids.length) {
38
+ ors.push({ _id: { $in: input.ids } });
39
+ }
40
+ if (input.tags && input.tags.length) {
41
+ ors.push({ tags: { $in: input.tags } });
42
+ }
43
+ if (input.scopeFilter && input.scopeFilter.length) {
44
+ const expand = await scope_reference_1.FAM_ScopeResolver_ControlService.getInstance().expandForRead(input.scopeFilter);
45
+ if (expand.scopeIdSet && expand.scopeIdSet.length) {
46
+ ors.push({ 'scopePath.scopeId': { $in: expand.scopeIdSet } });
47
+ }
48
+ }
49
+ if (!ors.length) {
50
+ throw fam_error_factory_util_1.FAM_Error_Util.create({
51
+ errorCode: error_codes_const_1.FAM_ERROR_CODES.mcpDispatchUnknownCapability,
52
+ message: 'A `set_global_rule` szelektor KÖTELEZŐ: adj meg `ids` VAGY `scopeFilter` VAGY `tags` mezőt.',
53
+ issuer: this.issuer,
54
+ context: { operation: 'set-global-rule' },
55
+ });
56
+ }
57
+ filter.$or = ors;
58
+ const model = this.rulesModel();
59
+ if (!model) {
60
+ throw fam_error_factory_util_1.FAM_Error_Util.create({
61
+ errorCode: error_codes_const_1.FAM_ERROR_CODES.vecSearch,
62
+ message: 'A `fam_rules` model nincs regisztrálva (boot-előtt?).',
63
+ issuer: this.issuer,
64
+ context: { operation: 'set-global-rule' },
65
+ });
66
+ }
67
+ const matched = await model.countDocuments(filter);
68
+ let modified = 0;
69
+ if (!dryRun && matched > 0) {
70
+ const res = await model.updateMany(filter, { $set: { globalRule: global } });
71
+ modified = res.modifiedCount ?? 0;
72
+ fsm_dynamo_1.DyFM_Log.info(`[FAM globalRule] ${modified} rules-bejegyzés globalRule=${global} (matched ${matched}).`);
73
+ }
74
+ return { operation: 'set_global_rule', dryRun: dryRun, global: global, matched: matched, modified: modified };
75
+ }
76
+ /** A `fam_rules` mongoose-modellje (a DS-példányosítás regisztrálja). */
77
+ rulesModel() {
78
+ new embedding_1.FAM_Entry_DataService({ dataParams: fam_rules_data_model_1.famRules_dataParams, issuer: this.issuer });
79
+ return mongoose_1.default.models[fam_rules_data_model_1.famRules_dataParams.dataName];
80
+ }
81
+ }
82
+ exports.FAM_GlobalRule_ControlService = FAM_GlobalRule_ControlService;
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FAM_MemoryColdSearch_ControlService = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const mongoose_1 = tslib_1.__importDefault(require("mongoose"));
6
+ const fsm_dynamo_1 = require("@futdevpro/fsm-dynamo");
7
+ const embedding_1 = require("../../embedding");
8
+ const fam_memory_data_model_1 = require("../../../_models/data-models/fam-memory.data-model");
9
+ /**
10
+ * `FAM_MemoryColdSearch_ControlService` (dsgn-013 §5.3, MAM F3b) — az ALVÓ (`dormant`) memóriák COLD keresése. A
11
+ * dormant entry-k NINCSENEK a hot LVS-poolban (§5.2), ezért a normál read nem találja őket. Amikor a hot memória-
12
+ * találatok KEVESEK (a query a hot készletből nincs kielégítve), ez a szolgáltatás a dormant készleten Mongo-oldali
13
+ * koszinusz-keresést futtat (lassabb, de teljes), és a megtalált top-K-t **REAKTIVÁLJA** (`dormant=false` → a
14
+ * következő (re)hidratáláskor visszakerül a hot készletbe). „A dormant nem tűnik el, csak nincs a gyors úton."
15
+ *
16
+ * **Korlátozott:** a dormant scan a `maxCandidates`-re vágott (biztonsági memóriakorlát); a `scopeIdSet`-re szűkített.
17
+ */
18
+ class FAM_MemoryColdSearch_ControlService {
19
+ static _instance;
20
+ issuer = 'FAM_MemoryColdSearch_ControlService';
21
+ static getInstance() {
22
+ if (!FAM_MemoryColdSearch_ControlService._instance) {
23
+ FAM_MemoryColdSearch_ControlService._instance = new FAM_MemoryColdSearch_ControlService();
24
+ }
25
+ return FAM_MemoryColdSearch_ControlService._instance;
26
+ }
27
+ /**
28
+ * Cold-keresés a dormant memórián. Betölti a scope-szűrt dormant entry-ket (lean, vektorral, `maxCandidates`-ig),
29
+ * koszinusz-rangsort számol a query-vektorral, a top-K-t REAKTIVÁLJA, és visszaadja a `FAM_ColdHit`-eket. Üres
30
+ * query-vektor / nincs dormant / hiba → üres lista (best-effort, a read-et nem buktatja).
31
+ */
32
+ async search(set) {
33
+ if (!set.queryVector.length || set.topK <= 0) {
34
+ return [];
35
+ }
36
+ try {
37
+ const model = this.memoryModel();
38
+ if (!model) {
39
+ return [];
40
+ }
41
+ const filter = FAM_MemoryColdSearch_ControlService.buildColdFilter(set);
42
+ const dormant = await model.find(filter).limit(Math.max(1, set.maxCandidates)).lean().exec();
43
+ // KRITIKUS (_id-konzisztencia, ld. streamHydratableVectors): a `.lean()` NYERS ObjectId `_id`-t ad, de a
44
+ // hit-id-knak STRING-nek kell lenniük (a hot-hitek pool-/findDataList-id-jával egyezően) — különben a dedup
45
+ // (`seen.has`), a reaktiváció (`typeof id === 'string'` szűrő) és a wire-szerializáció elromlik.
46
+ for (const entry of dormant) {
47
+ if (entry._id) {
48
+ entry._id = String(entry._id);
49
+ }
50
+ }
51
+ // A reaktiváció/promóció NEM itt történik: a hívó (`reactivateReturnedMemoryHits`) a VÉGSŐ, topK-vágott
52
+ // result.hits-re fut → CSAK a ténylegesen visszaadott cold-hit promótálódik (dormant=false) + kap +1 recall,
53
+ // EGYSZER (nincs dupla-számolás, és a topK-ból kieső cold-jelölt NEM promótálódik fölöslegesen).
54
+ return FAM_MemoryColdSearch_ControlService.rankByCosine(set.queryVector, dormant, set.topK);
55
+ }
56
+ catch (error) {
57
+ fsm_dynamo_1.DyFM_Log.warn(`[FAM memoryColdSearch] cold-keresés sikertelen (best-effort): ${error?.message}`);
58
+ return [];
59
+ }
60
+ }
61
+ /**
62
+ * Koszinusz-rangsor (PURE, tesztelhető): a dimenzió-egyező vektorú entry-kre score-t számol, a 0 ≤ score-t kiejti,
63
+ * desc rendez, top-K. (A l2-normalizált vektorokon a koszinusz a valódi similarity — BFR-AM-007.)
64
+ */
65
+ static rankByCosine(queryVector, entries, topK) {
66
+ const hits = [];
67
+ for (const entry of entries) {
68
+ const vector = (entry.contentVector ?? []);
69
+ if (vector.length !== queryVector.length) {
70
+ continue;
71
+ }
72
+ const score = FAM_MemoryColdSearch_ControlService.cosineSimilarity(queryVector, vector);
73
+ if (score > 0) {
74
+ hits.push({ entry: entry, score: score });
75
+ }
76
+ }
77
+ hits.sort((a, b) => b.score - a.score);
78
+ return hits.slice(0, Math.max(0, topK));
79
+ }
80
+ /**
81
+ * A cold-keresés Mongo-filtere (PURE, tesztelhető): dormant + completed + AKTÍV (`_deleted: null`) + scope/tag/kind
82
+ * prefilter (UGYANAZ, mint a hot-path `findActiveCandidates`/`loadCandidates`). **KRITIKUS:** `_deleted: null` (NEM
83
+ * `{$ne: true}`) — a `_deleted` DATE-mező; a mongoose a boolean `true`-t nem tudja Date-re castolni ("Cast to date
84
+ * failed") → a teljes cold-keresés elhalt (live-lelet 2026-06-24). A tag/kind nélkül a cold-supplement a query által
85
+ * EXPLICIT kiszűrt entry-ket is visszahozná.
86
+ */
87
+ static buildColdFilter(set) {
88
+ const filter = { dormant: true, embeddingStatus: 'completed', _deleted: null };
89
+ if (set.scopeIdSet.length) {
90
+ filter['scopePath.scopeId'] = { $in: set.scopeIdSet };
91
+ }
92
+ if (set.tagFilter && set.tagFilter.length) {
93
+ filter.tags = { $in: set.tagFilter };
94
+ }
95
+ if (set.kindFilter) {
96
+ filter.kind = set.kindFilter;
97
+ }
98
+ return filter;
99
+ }
100
+ /** Koszinusz-similarity két azonos-dimenziójú vektor között (0..1). */
101
+ static cosineSimilarity(a, b) {
102
+ let dot = 0;
103
+ let normA = 0;
104
+ let normB = 0;
105
+ for (let i = 0; i < a.length; i++) {
106
+ dot += a[i] * b[i];
107
+ normA += a[i] * a[i];
108
+ normB += b[i] * b[i];
109
+ }
110
+ if (!normA || !normB) {
111
+ return 0;
112
+ }
113
+ return dot / (Math.sqrt(normA) * Math.sqrt(normB));
114
+ }
115
+ /** A `fam_memory` mongoose-modellje (a DS-példányosítás regisztrálja). */
116
+ memoryModel() {
117
+ new embedding_1.FAM_Entry_DataService({ dataParams: fam_memory_data_model_1.famMemory_dataParams, issuer: this.issuer });
118
+ return mongoose_1.default.models[fam_memory_data_model_1.famMemory_dataParams.dataName];
119
+ }
120
+ }
121
+ exports.FAM_MemoryColdSearch_ControlService = FAM_MemoryColdSearch_ControlService;
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FAM_MemoryDormancy_ControlService = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const mongoose_1 = tslib_1.__importDefault(require("mongoose"));
6
+ const fsm_dynamo_1 = require("@futdevpro/fsm-dynamo");
7
+ const config_control_service_1 = require("../../../_routes/server/config/config.control-service");
8
+ const embedding_1 = require("../../embedding");
9
+ const fam_memory_data_model_1 = require("../../../_models/data-models/fam-memory.data-model");
10
+ /**
11
+ * `FAM_MemoryDormancy_ControlService` (dsgn-013 §5, MAM F3) — a memória-DORMANCY sweep-je: a régóta-nem-felidézett,
12
+ * ritkán-használt `memory`-bejegyzéseket ALVÓ-vá (`dormant=true`) jelöli. Az alvó memória NEM hidratálódik a hot
13
+ * LVS-poolba (RAM-/boot-spórolás, dsgn-013 §5.2), de Mongo-oldali COLD kereséssel elérhető (§5.3) + megtaláláskor
14
+ * reaktiválódik (visszakerül a hot készletbe). **SOHA nem töröl** — a `dormant` csak hidratálás-kihagyás.
15
+ *
16
+ * **Mongo-kifejezhető szabály (== a pure `FAM_MemoryActivation_Util.isDormant` age+recall kapuja):** `lastRecalledAt`
17
+ * (hiányában `__created`) régebbi mint `minAgeDays`, ÉS `recallCount < maxRecallCount`. A sokszor-felidézett memória
18
+ * SOHA nem dormant. A sweep `updateMany` — nem érinti a vektor-poolt (a hatás a következő (re)hidratáláskor jön).
19
+ */
20
+ class FAM_MemoryDormancy_ControlService {
21
+ static _instance;
22
+ issuer = 'FAM_MemoryDormancy_ControlService';
23
+ static MS_PER_DAY = 86_400_000;
24
+ static getInstance() {
25
+ if (!FAM_MemoryDormancy_ControlService._instance) {
26
+ FAM_MemoryDormancy_ControlService._instance = new FAM_MemoryDormancy_ControlService();
27
+ }
28
+ return FAM_MemoryDormancy_ControlService._instance;
29
+ }
30
+ /**
31
+ * A dormancy-sweep: a kvalifikáló `memory`-bejegyzések `dormant=true`-vá jelölése. Best-effort — a hiba logolódik és
32
+ * 0-val tér vissza (a boot-ot/működést nem buktatja). A `nowMs` injektálható (teszt-determinizmus). `dormancyEnabled
33
+ * = false` → no-op.
34
+ */
35
+ async sweep(nowMs) {
36
+ try {
37
+ const config = config_control_service_1.FAM_Config_ControlService.getInstance();
38
+ const enabledValue = (await config.resolve('memory.dormancyEnabled', {})).value;
39
+ if (enabledValue === false) {
40
+ return { enabled: false, markedDormant: 0 };
41
+ }
42
+ const minAgeDays = await this.resolveNumber(config, 'memory.dormantMinAgeDays', 180);
43
+ const maxRecallCount = await this.resolveNumber(config, 'memory.dormantMaxRecallCount', 2);
44
+ const model = this.memoryModel();
45
+ if (!model) {
46
+ return { enabled: true, markedDormant: 0 };
47
+ }
48
+ const filter = FAM_MemoryDormancy_ControlService.buildSweepFilter({
49
+ nowMs: nowMs, minAgeDays: minAgeDays, maxRecallCount: maxRecallCount,
50
+ });
51
+ const res = await model.updateMany(filter, { $set: { dormant: true } });
52
+ const marked = res.modifiedCount ?? 0;
53
+ if (marked > 0) {
54
+ fsm_dynamo_1.DyFM_Log.info(`[FAM memoryDormancy] ${marked} memória-bejegyzés ALVÓ-vá jelölve (age>${minAgeDays}d, recall<${maxRecallCount}).`);
55
+ }
56
+ return { enabled: true, markedDormant: marked };
57
+ }
58
+ catch (error) {
59
+ fsm_dynamo_1.DyFM_Log.warn(`[FAM memoryDormancy] sweep sikertelen (best-effort): ${error?.message}`);
60
+ return { enabled: true, markedDormant: 0 };
61
+ }
62
+ }
63
+ /**
64
+ * A sweep Mongo-filtere (PURE, tesztelhető): `completed` + még-nem-dormant + age-gát (`lastRecalledAt` VAGY a
65
+ * fallback `__created` a `minAgeDays`-cutoff előtt) + recall-gát (`recallCount < maxRecallCount`, a hiányzót is
66
+ * beleértve). A flag-csak frissítés → a vektor-poolt nem érinti.
67
+ */
68
+ static buildSweepFilter(set) {
69
+ const cutoffMs = set.nowMs - set.minAgeDays * FAM_MemoryDormancy_ControlService.MS_PER_DAY;
70
+ const cutoffDate = new Date(cutoffMs);
71
+ // age-gát: explicit lastRecalledAt a cutoff előtt, VAGY nincs lastRecalledAt és a __created a cutoff előtt.
72
+ const ageGate = {
73
+ $or: [
74
+ { lastRecalledAt: { $lt: cutoffMs } },
75
+ { lastRecalledAt: { $exists: false }, __created: { $lt: cutoffDate } },
76
+ ],
77
+ };
78
+ // recall-gát: recallCount < max (a hiányzó/0 is kvalifikál).
79
+ const recallGate = {
80
+ $or: [{ recallCount: { $lt: set.maxRecallCount } }, { recallCount: { $exists: false } }],
81
+ };
82
+ return {
83
+ embeddingStatus: 'completed',
84
+ dormant: { $ne: true },
85
+ $and: [ageGate, recallGate],
86
+ };
87
+ }
88
+ /** Egy szám-config feloldása a default-tal (a `resolve.value` lehet más típus → fallback). */
89
+ async resolveNumber(config, key, fallback) {
90
+ const value = (await config.resolve(key, {})).value;
91
+ return typeof value === 'number' ? value : fallback;
92
+ }
93
+ /** A `fam_memory` mongoose-modellje (a DS-példányosítás regisztrálja). */
94
+ memoryModel() {
95
+ new embedding_1.FAM_Entry_DataService({ dataParams: fam_memory_data_model_1.famMemory_dataParams, issuer: this.issuer });
96
+ return mongoose_1.default.models[fam_memory_data_model_1.famMemory_dataParams.dataName];
97
+ }
98
+ }
99
+ exports.FAM_MemoryDormancy_ControlService = FAM_MemoryDormancy_ControlService;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FAM_MemoryReactivation_ControlService = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const mongoose_1 = tslib_1.__importDefault(require("mongoose"));
6
+ const fsm_dynamo_1 = require("@futdevpro/fsm-dynamo");
7
+ const fam_table_type_enum_1 = require("../../../_enums/fam-table.type-enum");
8
+ const embedding_1 = require("../../embedding");
9
+ const fam_memory_data_model_1 = require("../../../_models/data-models/fam-memory.data-model");
10
+ /**
11
+ * `FAM_MemoryReactivation_ControlService` (dsgn-013 §4, MAM F2) — a memória FELIDÉZÉS-megerősítése. Amikor a `read`
12
+ * ténylegesen VISSZAAD egy `memory`-hitet (a felhasználó/agent felidézte), az „új energiát kap": `lastRecalledAt = now`,
13
+ * `recallCount += 1`, `dormant = false`. Ez az emberi memória retrieval-practice-e — a felidézett emlék megerősödik,
14
+ * feljebb rangsorol (a dsgn-013 §2 frequencyBoost + a friss recency), és ha alvó volt, visszakerül a hot készletbe.
15
+ *
16
+ * **Fire-and-forget:** a `read`-ből async, NEM blokkolja a választ; batch-elt `updateMany` (egy DB-hívás a top-K-ra).
17
+ * CSAK a ténylegesen visszaadott (használt) hitre fut → nincs „gyakran-keresett-de-irreleváns" felhízás. A hiba
18
+ * NÉMA-best-effort (a read már sikerült; a felidézés-frissítés elvesztése nem hiba). A flag NEM érinti a vektor-poolt.
19
+ */
20
+ class FAM_MemoryReactivation_ControlService {
21
+ static _instance;
22
+ issuer = 'FAM_MemoryReactivation_ControlService';
23
+ static getInstance() {
24
+ if (!FAM_MemoryReactivation_ControlService._instance) {
25
+ FAM_MemoryReactivation_ControlService._instance = new FAM_MemoryReactivation_ControlService();
26
+ }
27
+ return FAM_MemoryReactivation_ControlService._instance;
28
+ }
29
+ /**
30
+ * A megadott `memory`-entry-k FELIDÉZÉS-frissítése (`updateMany`): `lastRecalledAt = nowMs`, `recallCount += 1`,
31
+ * `dormant = false`. Üres id-lista → no-op (0). Visszaadja a módosított darabszámot. **Nem dob** — a hiba logolódik
32
+ * és `0`-val tér vissza (best-effort, a read-et nem buktatja).
33
+ */
34
+ async reactivate(ids, nowMs) {
35
+ const uniqueIds = Array.from(new Set(ids.filter((id) => typeof id === 'string' && id.length)));
36
+ if (!uniqueIds.length) {
37
+ return 0;
38
+ }
39
+ try {
40
+ const model = this.memoryModel();
41
+ if (!model) {
42
+ return 0;
43
+ }
44
+ const res = await model.updateMany({ _id: { $in: uniqueIds } }, { $set: { lastRecalledAt: nowMs, dormant: false }, $inc: { recallCount: 1 } });
45
+ return res.modifiedCount ?? 0;
46
+ }
47
+ catch (error) {
48
+ fsm_dynamo_1.DyFM_Log.warn(`[FAM memoryReactivation] felidézés-frissítés sikertelen (best-effort): ${error?.message}`);
49
+ return 0;
50
+ }
51
+ }
52
+ /**
53
+ * A reaktiválandó `memory`-hit `_id`-k kiválasztása a read-eredmény hitjeiből (PURE, tesztelhető): CSAK a
54
+ * `memory`-táras, `_id`-vel bíró hitek, deduplikálva. A NEM-memory hitek (codebase/documents/…) kimaradnak.
55
+ */
56
+ static selectMemoryIds(hits) {
57
+ const ids = hits
58
+ .filter((hit) => hit.table === fam_table_type_enum_1.FAM_Table.memory && typeof hit.id === 'string' && hit.id.length > 0)
59
+ .map((hit) => hit.id);
60
+ return Array.from(new Set(ids));
61
+ }
62
+ /** A `fam_memory` mongoose-modellje (a DS-példányosítás regisztrálja). */
63
+ memoryModel() {
64
+ new embedding_1.FAM_Entry_DataService({ dataParams: fam_memory_data_model_1.famMemory_dataParams, issuer: this.issuer });
65
+ return mongoose_1.default.models[fam_memory_data_model_1.famMemory_dataParams.dataName];
66
+ }
67
+ }
68
+ exports.FAM_MemoryReactivation_ControlService = FAM_MemoryReactivation_ControlService;
@@ -117,8 +117,21 @@ class FAM_RetrievalCandidate_DataService {
117
117
  const filter = {};
118
118
  // ① scope-prefilter (subtree scopeId-halmaz). null → nincs scope-szűrés (teljes tár).
119
119
  if (input.scopeIdSet) {
120
- // A scopePath denormalizált scope-ref tömb; a scopeId-ra szűrünk (dot-path a Mixed tömbön).
121
- filter['scopePath.scopeId'] = { $in: input.scopeIdSet };
120
+ const isGlobalRulesTable = registryEntry.table === fam_table_type_enum_1.FAM_Table.rules && input.includeGlobalRules !== false;
121
+ if (isGlobalRulesTable) {
122
+ // GLOBÁLIS HARD-RULE öröklés (user-FR 2026-06-22 — a FAM fő feature-je): a scope-szűrt `rules`-lekérdezés
123
+ // a subtree-scope rule-jai MELLÉ a `globalRule:true` (workspace-szintű hard-rules) bejegyzéseket is
124
+ // behúzza — `(scope subtree-ben) VAGY (globális hard-rule)`. Így egy al-scope-ban is megkapjuk a
125
+ // mindenhol érvényes hard-rules-t. (A `$or` a tag/kind/isActive AND-feltételekkel kombinálódik.)
126
+ filter.$or = [
127
+ { 'scopePath.scopeId': { $in: input.scopeIdSet } },
128
+ { globalRule: true },
129
+ ];
130
+ }
131
+ else {
132
+ // A scopePath denormalizált scope-ref tömb; a scopeId-ra szűrünk (dot-path a Mixed tömbön).
133
+ filter['scopePath.scopeId'] = { $in: input.scopeIdSet };
134
+ }
122
135
  }
123
136
  // ② tag-prefilter (OR a tagek között): bármely tag-match bekerül.
124
137
  if (input.tagFilter && input.tagFilter.length) {