@futdevpro/fdp-agent-memory 1.1.21 → 1.1.112
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/find-duplicates.command.js +41 -16
- package/build/src/_cli/_commands/scan-projects.command.js +113 -113
- package/build/src/_cli/_commands/scan.command.js +2 -0
- package/build/src/_cli/_commands/serve.command.js +110 -10
- package/build/src/_cli/_services/fam-client.service.js +25 -1
- package/build/src/_cli/register-commands.js +28 -0
- package/build/src/_collections/config-catalog.const.js +152 -33
- package/build/src/_collections/error-codes.const.js +2 -0
- package/build/src/_collections/fam-console.util.js +38 -260
- package/build/src/_collections/fam-db-models.const.js +2 -0
- package/build/src/_collections/fam-entry-bootstrap.util.js +20 -6
- package/build/src/_collections/fam-error-context.util.js +1 -0
- package/build/src/_collections/fam-mcp-bridge.util.js +15 -1
- package/build/src/_collections/fam-operation-queue.service.js +85 -0
- package/build/src/_collections/fam-project-discovery.util.js +148 -0
- package/build/src/_collections/fam-request-origin.util.js +41 -0
- package/build/src/_collections/fam-retry.util.js +48 -0
- package/build/src/_collections/fam-scan-progress-sink.service.js +26 -0
- package/build/src/_collections/title.const.js +26 -26
- package/build/src/_enums/fam-rule-scope.type-enum.js +25 -0
- package/build/src/_models/data-models/fam-entry.data-model.js +29 -0
- package/build/src/_models/data-models/fam-memory.data-model.js +5 -0
- package/build/src/_models/data-models/fam-rules.data-model.js +24 -0
- package/build/src/_models/data-models/fam-scan-job.data-model.js +73 -0
- package/build/src/_models/data-models/fam-scope.data-model.js +9 -0
- package/build/src/_modules/capture/_services/fam-auto-capture.control-service.js +1 -1
- package/build/src/_modules/embedding/_services/fam-dedup-warn.control-service.js +40 -0
- package/build/src/_modules/embedding/_services/fam-duplicate-scan.control-service.js +74 -16
- package/build/src/_modules/embedding/_services/fam-embedding-bootstrap.control-service.js +50 -0
- package/build/src/_modules/embedding/_services/fam-embedding-pipeline.control-service.js +32 -6
- package/build/src/_modules/embedding/_services/fam-embedding.control-service.js +142 -9
- package/build/src/_modules/embedding/_services/fam-entry.data-service.js +32 -1
- package/build/src/_modules/embedding/_services/fam-hydration-coordinator.control-service.js +159 -0
- package/build/src/_modules/embedding/_services/fam-lmstudio-embedding.provider.js +41 -4
- package/build/src/_modules/embedding/_services/fam-vector-search.control-service.js +67 -12
- package/build/src/_modules/embedding/index.js +5 -1
- package/build/src/_modules/ingest/_collections/fam-file-routing.util.js +10 -0
- package/build/src/_modules/ingest/_collections/fam-frontmatter.util.js +134 -0
- package/build/src/_modules/ingest/_collections/fam-project-identity.util.js +30 -0
- package/build/src/_modules/ingest/_collections/fam-scan-progress.util.js +5 -2
- package/build/src/_modules/ingest/_collections/fam-scan-summary.util.js +55 -29
- package/build/src/_modules/ingest/_collections/fam-split-chunker.util.js +139 -0
- package/build/src/_modules/ingest/_models/interfaces/fam-scan-job.interface.js +2 -0
- package/build/src/_modules/ingest/_services/fam-chunker.control-service.js +10 -0
- package/build/src/_modules/ingest/_services/fam-delta-compare.util.js +68 -1
- package/build/src/_modules/ingest/_services/fam-ingest-run.data-service.js +1 -1
- package/build/src/_modules/ingest/_services/fam-ingest.control-service.js +177 -37
- package/build/src/_modules/ingest/_services/fam-scan-job.control-service.js +429 -0
- package/build/src/_modules/ingest/_services/fam-scan-job.data-service.js +34 -0
- package/build/src/_modules/ingest/_services/fam-scan.control-service.js +6 -4
- package/build/src/_modules/ingest/index.js +6 -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 +264 -41
- package/build/src/_modules/mcp/_services/fam-read-tool.service.js +71 -1
- package/build/src/_modules/mcp/_services/fam-write-tool.service.js +36 -0
- package/build/src/_modules/migration/_services/fam-claude-mem-import.control-service.js +1 -1
- package/build/src/_modules/retrieval/_collections/fam-lexical-match.util.js +69 -0
- package/build/src/_modules/retrieval/_collections/fam-memory-activation.util.js +66 -0
- package/build/src/_modules/retrieval/_collections/fam-rule-injection.util.js +125 -0
- package/build/src/_modules/retrieval/_collections/fam-rule-propagation.util.js +95 -0
- package/build/src/_modules/retrieval/_collections/fam-rule-scope-context.util.js +74 -0
- package/build/src/_modules/retrieval/_services/fam-global-rule.control-service.js +82 -0
- package/build/src/_modules/retrieval/_services/fam-memory-cold-search.control-service.js +121 -0
- package/build/src/_modules/retrieval/_services/fam-memory-dormancy.control-service.js +99 -0
- package/build/src/_modules/retrieval/_services/fam-memory-reactivation.control-service.js +68 -0
- package/build/src/_modules/retrieval/_services/fam-retrieval-candidate.data-service.js +15 -2
- package/build/src/_modules/retrieval/_services/fam-retrieval.control-service.js +197 -5
- package/build/src/_modules/retrieval/_services/fam-rule-injection.control-service.js +102 -0
- package/build/src/_modules/retrieval/index.js +12 -1
- package/build/src/_modules/scope-reference/_collections/fam-scope-maintenance.util.js +134 -0
- package/build/src/_modules/scope-reference/_services/fam-scope-maintenance.control-service.js +423 -0
- package/build/src/_modules/scope-reference/_services/fam-scope-resolver.control-service.js +2 -2
- package/build/src/_modules/scope-reference/_services/fam-scope.data-service.js +25 -0
- package/build/src/_modules/scope-reference/index.js +4 -1
- package/build/src/_routes/server/api/api.controller.js +324 -23
- package/build/src/_routes/server/api/fam-doctor.control-service.js +156 -0
- package/build/src/_routes/server/config/config.control-service.js +2 -2
- package/build/src/app.server.js +29 -27
- package/client-dist/{chunk-YXHWCJ5O.js → chunk-GF3FJP7E.js} +71 -71
- package/client-dist/chunk-NMUCMQY6.js +1 -0
- package/client-dist/favicon.ico +0 -0
- package/client-dist/index.html +3 -3
- package/client-dist/main-YK4YIVAQ.js +2 -0
- package/package.json +2 -2
- package/build/src/_cli/_collections/fam-project-discovery.util.js +0 -98
- package/build/src/_collections/fam-heap-guard.control-service.js +0 -176
- package/client-dist/chunk-I77GXVAQ.js +0 -1
- package/client-dist/main-PJPEDVJT.js +0 -2
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FAM_ProjectDiscovery_Util = void 0;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const fam_famignore_util_1 = require("../_modules/ingest/_collections/fam-famignore.util");
|
|
7
|
+
const fam_glob_match_util_1 = require("../_modules/ingest/_collections/fam-glob-match.util");
|
|
8
|
+
/**
|
|
9
|
+
* `FAM_ProjectDiscovery_Util` (FEAT-008: a szerver-oldali scan-orchestráció magja, áthelyezve `_cli`→`_collections`)
|
|
10
|
+
* — projekt-gyökerek **auto-felderítése** egy mappa alatt + a config JSON-lista parse-je. PURE (fs-olvasás, nincs
|
|
11
|
+
* hálózat). A scope-nevet a `package.json` `name`-jéből deriválja, fallback a dir-név.
|
|
12
|
+
*
|
|
13
|
+
* **Felderítés-szabály:** egy könyvtár PROJEKT-GYÖKÉR, ha van `package.json` VAGY `.git`. Ott MEGÁLLUNK — EGY SCOPE
|
|
14
|
+
* PER REPO (user-decision 2026-06-24): a monorepo (`server`/`client`/`cli`/`e2e`) MIND egy közös repo-scope-ba kerül,
|
|
15
|
+
* NEM bontjuk al-csomag-scope-okra. A scope NEVE: monorepónál (van saját-markerű al-csomag) a REPO-DIR neve (a root
|
|
16
|
+
* `package.json` neve félrevezető lehet — pl. `@futdevpro/ccap` a `ccap-revisioned` monorepóra); egy-csomagos LEAF
|
|
17
|
+
* projektnél a `package.json` `name` bare alakja (a természetes identitás). A `node_modules` + rejtett mappák +
|
|
18
|
+
* `SKIP_DIRS` + `.fdpfamignore` kihagyva.
|
|
19
|
+
*/
|
|
20
|
+
class FAM_ProjectDiscovery_Util {
|
|
21
|
+
/** A projekt-gyökér markerek (bármelyik megléte → projekt). */
|
|
22
|
+
static MARKERS = ['package.json', '.git'];
|
|
23
|
+
/**
|
|
24
|
+
* A felderítésből MINDIG KIHAGYOTT könyvtárnevek (user-direktíva 2026-06-24, KRITIKUS — „SEMMIT nem akarunk
|
|
25
|
+
* szkennelni, ami a Stale-ben van!!!"). A `.fdpfamignore`-tól FÜGGETLEN, GARANTÁLT hard-skip: a `STALE-projects/`
|
|
26
|
+
* (archív/elavult projektek, pl. `fury` — 18757 fájl, „mind új" → halálra terheli az embedding-providert) +
|
|
27
|
+
* a `TEMPLATE-projects/` (`{{PROJECT_NAME}}` sablon-junk) + a `logs/` SOHA nem felderítendők. A `.`-mappák +
|
|
28
|
+
* `node_modules` külön (a startsWith('.') feltétel). Case-SENSITIVE (a workspace mappanevek pontos alakja).
|
|
29
|
+
*/
|
|
30
|
+
static SKIP_DIRS = new Set([
|
|
31
|
+
'node_modules', 'STALE-projects', 'TEMPLATE-projects', 'logs',
|
|
32
|
+
]);
|
|
33
|
+
/** Projekt-gyökér-e a könyvtár (van `package.json` vagy `.git`). */
|
|
34
|
+
static isProjectRoot(dir) {
|
|
35
|
+
return FAM_ProjectDiscovery_Util.MARKERS.some((marker) => (0, fs_1.existsSync)((0, path_1.join)(dir, marker)));
|
|
36
|
+
}
|
|
37
|
+
/** A scope leaf-név: a `package.json` `name` bare alakja (`@scope/x` → `x`), vagy a dir-név. */
|
|
38
|
+
static projectName(dir) {
|
|
39
|
+
try {
|
|
40
|
+
const pkgPath = (0, path_1.join)(dir, 'package.json');
|
|
41
|
+
if ((0, fs_1.existsSync)(pkgPath)) {
|
|
42
|
+
const name = JSON.parse((0, fs_1.readFileSync)(pkgPath, 'utf-8')).name;
|
|
43
|
+
if (typeof name === 'string' && name.trim()) {
|
|
44
|
+
return name.trim().replace(/^@[^/]+\//, '');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// fallback a dir-névre
|
|
50
|
+
}
|
|
51
|
+
return (0, path_1.basename)(dir);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Projekt-gyökerek felderítése `root` alatt `maxDepth`-ig. Ha a `root` maga projekt → csak az. Egyébként
|
|
55
|
+
* rekurzívan a nem-rejtett alkönyvtárakban (egy projekt-gyökérnél MEGÁLL). Determinisztikus (név szerint rendezett).
|
|
56
|
+
*/
|
|
57
|
+
static discover(root, maxDepth, defaults) {
|
|
58
|
+
const result = [];
|
|
59
|
+
// A `.fdpfamignore` a FELDERÍTÉSRE is érvényes (user-FR 2026-06-22 — KRITIKUS bugfix): a famignore-olt mappákba
|
|
60
|
+
// (pl. a `Stale`-archív) NEM megyünk be, így a bennük lévő projekteket (pl. `fury`) NEM is fedezzük fel. A globok
|
|
61
|
+
// ROOT-relatívak (a `loadFromDir` baseDir-prefixe); a root + a nested famignore akkumulálódik a leszálláskor.
|
|
62
|
+
const relRoot = (dir) => (0, path_1.relative)(root, dir).replace(/\\/g, '/');
|
|
63
|
+
// A `dir` szűrt (nem-rejtett, nem-SKIP_DIRS, nem-famignore-olt) közvetlen al-KÖNYVTÁRAI (nevek). A
|
|
64
|
+
// monorepo-al-csomag-kereséshez ÉS a nem-projekt-leszálláshoz közös.
|
|
65
|
+
const filteredChildDirs = (dir, allGlobs) => {
|
|
66
|
+
let entries;
|
|
67
|
+
try {
|
|
68
|
+
entries = (0, fs_1.readdirSync)(dir).sort();
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
const dirs = [];
|
|
74
|
+
for (const name of entries) {
|
|
75
|
+
if (name.startsWith('.') || FAM_ProjectDiscovery_Util.SKIP_DIRS.has(name)) {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const child = (0, path_1.join)(dir, name);
|
|
79
|
+
try {
|
|
80
|
+
if (!(0, fs_1.statSync)(child).isDirectory()) {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
continue; // olvashatatlan bejegyzés → kihagyjuk
|
|
86
|
+
}
|
|
87
|
+
// FAMIGNORE-szűrés: a root-relatív útvonal VAGY a puszta mappanév egy ignore-globra illeszkedik → kihagyjuk.
|
|
88
|
+
const childRel = relRoot(child);
|
|
89
|
+
if (fam_glob_match_util_1.FAM_GlobMatch_Util.matchesAny(childRel, allGlobs) || fam_glob_match_util_1.FAM_GlobMatch_Util.matchesAny(name, allGlobs)) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
dirs.push(name);
|
|
93
|
+
}
|
|
94
|
+
return dirs;
|
|
95
|
+
};
|
|
96
|
+
const visit = (dir, depth, ignoreGlobs) => {
|
|
97
|
+
if (depth > maxDepth) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
// E könyvtár saját `.fdpfamignore`-ja (a root-relatív base-szel) → akkumulálva a leszármazottakra (a
|
|
101
|
+
// projekt-gyökérre IS, hogy a monorepo-leszálláskor + az al-csomag-keresésnél is érvényesüljön).
|
|
102
|
+
const localGlobs = fam_famignore_util_1.FAM_FamIgnore_Util.loadFromDir(dir, depth === 0 ? '' : relRoot(dir));
|
|
103
|
+
const allGlobs = localGlobs.length ? [...ignoreGlobs, ...localGlobs] : ignoreGlobs;
|
|
104
|
+
const childDirs = filteredChildDirs(dir, allGlobs);
|
|
105
|
+
if (FAM_ProjectDiscovery_Util.isProjectRoot(dir)) {
|
|
106
|
+
// ONE SCOPE PER REPO (user-decision 2026-06-24): a projekt-gyökeret EGY scope-ként vesszük fel, és NEM
|
|
107
|
+
// megyünk beljebb — a monorepo (server/client/cli/e2e) MIND egy közös repo-scope-ba kerül (nincs
|
|
108
|
+
// al-csomag-split, nincs lumpolás-aggály: szándékosan EGY repo = EGY scope). A scope NEVE: ha a gyökér
|
|
109
|
+
// alatt VAN saját-markerű al-csomag (FDP-monorepo), akkor a REPO-DIR neve — mert a root `package.json`
|
|
110
|
+
// neve félrevezető lehet (pl. `@futdevpro/ccap` a `ccap-revisioned` monorepóra → bare `ccap` → a téves
|
|
111
|
+
// `ccap-client` scope-ra oldódott). Egy-csomagos LEAF projektnél a `package.json` `name` (a természetes
|
|
112
|
+
// identitás → az NPM-package-ek scope-jai NEM neveződnek át). A monorepo-detekció depth-független
|
|
113
|
+
// (egy szinttel lejjebb nézünk; nem descend-elünk, ezért a maxDepth itt nem korlát).
|
|
114
|
+
const hasSubPackage = childDirs.some((name) => FAM_ProjectDiscovery_Util.isProjectRoot((0, path_1.join)(dir, name)));
|
|
115
|
+
result.push({
|
|
116
|
+
path: dir,
|
|
117
|
+
project: hasSubPackage ? (0, path_1.basename)(dir) : FAM_ProjectDiscovery_Util.projectName(dir),
|
|
118
|
+
org: defaults?.org, table: defaults?.table,
|
|
119
|
+
});
|
|
120
|
+
return; // one scope per repo: nem megyünk beljebb
|
|
121
|
+
}
|
|
122
|
+
for (const name of childDirs) {
|
|
123
|
+
visit((0, path_1.join)(dir, name), depth + 1, allGlobs);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
visit(root, 0, []);
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
/** A config JSON-lista → projekt-spec-ek (validálva: `path` + `project` kötelező soronként). */
|
|
130
|
+
static parseConfig(content) {
|
|
131
|
+
const parsed = JSON.parse(content);
|
|
132
|
+
if (!Array.isArray(parsed)) {
|
|
133
|
+
throw new Error('A scan-projects config egy JSON-tömb legyen: [{ "path": "...", "project": "..." }, ...]');
|
|
134
|
+
}
|
|
135
|
+
return parsed.map((raw, index) => {
|
|
136
|
+
const entry = (raw && typeof raw === 'object') ? raw : {};
|
|
137
|
+
if (typeof entry.path !== 'string' || typeof entry.project !== 'string') {
|
|
138
|
+
throw new Error(`A config[${index}] hiányos: a 'path' és a 'project' (string) kötelező.`);
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
path: entry.path, project: entry.project,
|
|
142
|
+
org: typeof entry.org === 'string' ? entry.org : undefined,
|
|
143
|
+
table: typeof entry.table === 'string' ? entry.table : undefined,
|
|
144
|
+
};
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
exports.FAM_ProjectDiscovery_Util = FAM_ProjectDiscovery_Util;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FAM_RequestOrigin_Util = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* `FAM_RequestOrigin_Util` (user-FR 2026-06-25) — egy FAM-esemény INDÍTÓJÁNAK aláírását (`FAM_AgentOrigin`) oldja fel a
|
|
6
|
+
* HTTP-kérésből: az `X-FAM-Session` / `X-FAM-Agent` / `X-FAM-Workspace` / `X-FAM-Label` headerekből VAGY a body `origin`
|
|
7
|
+
* mezőjéből (a body PER-MEZŐ felülír — explicit > header). Így a hívó (hook / `fam` CLI / agent) ALÁÍRJA az általa
|
|
8
|
+
* indított eseményt → több, EGYIDEJŰ workspace-session is megkülönböztethető. Aláírás teljes hiányában `agent:'unknown'`.
|
|
9
|
+
*
|
|
10
|
+
* A szerver NEM tudja kitalálni az identitást (a lokál portot minden session azonos forrásból éri el) — ezért a
|
|
11
|
+
* SZERZŐDÉS: a hívó adja meg. A FDP-agentek konvenciója: a CC `session_id`-t küldik az `X-FAM-Session` headerben.
|
|
12
|
+
*/
|
|
13
|
+
class FAM_RequestOrigin_Util {
|
|
14
|
+
/** A támogatott aláírás-headerek (kisbetűs — az Express így adja a `req.headers`-t). */
|
|
15
|
+
static HEADERS = [
|
|
16
|
+
{ key: 'sessionId', header: 'x-fam-session' },
|
|
17
|
+
{ key: 'agent', header: 'x-fam-agent' },
|
|
18
|
+
{ key: 'workspace', header: 'x-fam-workspace' },
|
|
19
|
+
{ key: 'label', header: 'x-fam-label' },
|
|
20
|
+
];
|
|
21
|
+
/** A kérés (header + opcionális body-`origin`) → feloldott `FAM_AgentOrigin` (body felülír; üresnél `agent:'unknown'`). */
|
|
22
|
+
static fromRequest(req, bodyOrigin) {
|
|
23
|
+
const merged = {};
|
|
24
|
+
for (const entry of FAM_RequestOrigin_Util.HEADERS) {
|
|
25
|
+
const fromBody = bodyOrigin ? bodyOrigin[entry.key] : undefined;
|
|
26
|
+
const value = (fromBody && fromBody.trim()) ? fromBody.trim() : FAM_RequestOrigin_Util.header(req, entry.header);
|
|
27
|
+
if (value) {
|
|
28
|
+
merged[entry.key] = value;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const hasAny = Object.values(merged).some((value) => !!value);
|
|
32
|
+
return hasAny ? merged : { agent: 'unknown' };
|
|
33
|
+
}
|
|
34
|
+
/** Egy header string-értéke (trimmelve), vagy `undefined`. Tömb-header (ritka) első eleme. */
|
|
35
|
+
static header(req, name) {
|
|
36
|
+
const raw = req.headers[name];
|
|
37
|
+
const value = Array.isArray(raw) ? raw[0] : raw;
|
|
38
|
+
return value && value.trim() ? value.trim() : undefined;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.FAM_RequestOrigin_Util = FAM_RequestOrigin_Util;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FAM_Retry_Util = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* `FAM_Retry_Util` — általános, ÚJRAHASZNÁLHATÓ retry-exponenciális-backoff helper a TRANZIENS hibákra
|
|
6
|
+
* (stresszteszt-lelet 2026-06-22: konkurrens terhelés alatt a remote embedding-provider ritkán megakadt → a
|
|
7
|
+
* teljes read elbukott egyetlen tranziens hibán). Pure-vezérelt (a `fn` async művelet + opciók), így a backoff
|
|
8
|
+
* `baseBackoffMs:0`-val determinisztikusan + gyorsan unit-tesztelhető.
|
|
9
|
+
*
|
|
10
|
+
* Az `isRetryable` predikátum dönti el, mi tranziens: a determinisztikus hibák (pl. validáció, dimenzió-mismatch)
|
|
11
|
+
* NEM retry-zhetők (azonnal dobnak), csak a hálózati/provider-átmenetiek. Az `onRetry` callback observability-re
|
|
12
|
+
* (warn-log) — a util maga NEM logol (függőség-mentes).
|
|
13
|
+
*/
|
|
14
|
+
class FAM_Retry_Util {
|
|
15
|
+
/**
|
|
16
|
+
* A `fn` futtatása `maxAttempts`-szor, exponenciális backoff-fal (`baseBackoffMs × 2^(n-1)`, `maxBackoffMs`-re
|
|
17
|
+
* kapozva) a próbálkozások KÖZÖTT. Siker → az eredmény. Ha az `isRetryable` `false`-t ad egy hibára → AZONNAL
|
|
18
|
+
* dobja (nincs további próba). A próbálkozások kimerülése → az UTOLSÓ hibát dobja (no-silent-failure).
|
|
19
|
+
*/
|
|
20
|
+
static async withRetry(fn, options) {
|
|
21
|
+
const maxAttempts = Math.max(1, Math.floor(options.maxAttempts));
|
|
22
|
+
const base = options.baseBackoffMs ?? 250;
|
|
23
|
+
const cap = options.maxBackoffMs ?? 1000;
|
|
24
|
+
let lastError;
|
|
25
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
26
|
+
try {
|
|
27
|
+
return await fn();
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
lastError = error;
|
|
31
|
+
if (options.isRetryable && !options.isRetryable(error)) {
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
if (attempt < maxAttempts) {
|
|
35
|
+
const backoffMs = Math.min(cap, base * 2 ** (attempt - 1));
|
|
36
|
+
if (options.onRetry) {
|
|
37
|
+
options.onRetry(attempt, backoffMs, error);
|
|
38
|
+
}
|
|
39
|
+
if (backoffMs > 0) {
|
|
40
|
+
await new Promise((resolveSleep) => setTimeout(resolveSleep, backoffMs));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
throw lastError;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.FAM_Retry_Util = FAM_Retry_Util;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FAM_ScanProgress_Sink = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* `FAM_ScanProgress_Sink` (FEAT-008 sub-progress, user-FR 2026-06-22) — a LEGUTÓBBI projekten-belüli haladás
|
|
6
|
+
* process-szintű holder-e. Mivel az operation-queue szerializál (egyszerre EGY projekt ingestál), egyetlen „latest"
|
|
7
|
+
* elég. Az ingest a throttle-olt progress-emitkor frissíti; a scan-job a `getStatus`-ban beolvassa → a thin-CLI
|
|
8
|
+
* poll is látja a within-project haladást (nem csak a per-projekt befejezést). Statikus, állapot-minimal.
|
|
9
|
+
*/
|
|
10
|
+
class FAM_ScanProgress_Sink {
|
|
11
|
+
/** A legutóbbi haladás-pillanatkép (vagy undefined, ha még nem volt / törölve). */
|
|
12
|
+
static latest;
|
|
13
|
+
/** A legutóbbi haladás beállítása (az ingest hívja a progress-emitnél). */
|
|
14
|
+
static set(progress) {
|
|
15
|
+
FAM_ScanProgress_Sink.latest = progress;
|
|
16
|
+
}
|
|
17
|
+
/** A legutóbbi haladás (a scan-job status-hoz). */
|
|
18
|
+
static get() {
|
|
19
|
+
return FAM_ScanProgress_Sink.latest;
|
|
20
|
+
}
|
|
21
|
+
/** A holder ürítése (teszt / projekt-váltás). */
|
|
22
|
+
static clear() {
|
|
23
|
+
FAM_ScanProgress_Sink.latest = undefined;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.FAM_ScanProgress_Sink = FAM_ScanProgress_Sink;
|
|
@@ -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.`;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FAM_RuleScope = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* `FAM_RuleScope` (Rules-Management System, MP-1) — egy szabály ALKALMAZHATÓSÁGI osztálya: HOL érvényes + injektálódik.
|
|
6
|
+
* (Megkülönböztetve a `scopePath`-tól, ami a scope-ENTITÁS-lánc — itt a rule always-apply TIER-jéről van szó.)
|
|
7
|
+
*
|
|
8
|
+
* - `global` — univerzális; MINDEN rendszerre (FDP + nem-FDP), root ÉS in-project. Mindig injektált.
|
|
9
|
+
* - `fdpGlobal` — FDP-ökoszisztéma-specifikus (FDP CLI, Keystore, bedrock, Dynamo/NTS, FAM, @futdevpro). Csak FDP-repóban.
|
|
10
|
+
* - `workspaceRoot` — CSAK a multi-projekt workspace root-szinten (orchestráció / cross-project / devops).
|
|
11
|
+
* - `projectInternal` — minden projektben, amikor az agent a projekt-workspace-en belül dolgozik.
|
|
12
|
+
* - `projectType` — projekt-TÍPUS szerint (a `ruleScopeTopic` adja a típust, pl. `ngx`/`nts`/`server`/`client`).
|
|
13
|
+
* - `trigger` — on-demand, kontextus/téma szerint (a `ruleScopeTopic` a téma; a rule-fetch-reflex hozza elő).
|
|
14
|
+
*
|
|
15
|
+
* Konvenció: `keyNameIsCamelCase = 'value-is-kebab-case'`.
|
|
16
|
+
*/
|
|
17
|
+
var FAM_RuleScope;
|
|
18
|
+
(function (FAM_RuleScope) {
|
|
19
|
+
FAM_RuleScope["global"] = "global";
|
|
20
|
+
FAM_RuleScope["fdpGlobal"] = "fdp-global";
|
|
21
|
+
FAM_RuleScope["workspaceRoot"] = "workspace-root";
|
|
22
|
+
FAM_RuleScope["projectInternal"] = "project-internal";
|
|
23
|
+
FAM_RuleScope["projectType"] = "project-type";
|
|
24
|
+
FAM_RuleScope["trigger"] = "trigger";
|
|
25
|
+
})(FAM_RuleScope || (exports.FAM_RuleScope = FAM_RuleScope = {}));
|
|
@@ -22,6 +22,14 @@ class FAM_Entry extends fsm_dynamo_1.DyFM_Metadata {
|
|
|
22
22
|
kind;
|
|
23
23
|
/** Többes kategorizálás (dsgn-001 §3); per-tár ajánlott vokabulár, de szabadon bővül. */
|
|
24
24
|
tags = [];
|
|
25
|
+
/**
|
|
26
|
+
* **GLOBÁLIS HARD-RULE** jelölő (user-FR 2026-06-22 — a FAM egyik fő feature-je): ha `true`, ez a (jellemzően
|
|
27
|
+
* `rules`-táras) bejegyzés MINDEN scope-szűrt lekérdezésbe BEKERÜL, scope-tól FÜGGETLENÜL — a workspace-szintű
|
|
28
|
+
* hard-rules (pl. E2E-hard-rule, always-master, naming/import-konvenciók) így öröklődnek MINDEN al-scope rule-
|
|
29
|
+
* lekérdezésébe. A retrieval candidate-prefilter `$or`-ral húzza be (`scopePath.scopeId $in subtree` VAGY
|
|
30
|
+
* `globalRule:true`). Default `false` (a legtöbb rule scope-specifikus); a hard-rules EXPLICIT jelölést kapnak.
|
|
31
|
+
*/
|
|
32
|
+
globalRule;
|
|
25
33
|
// --- tartalom + embedding ---
|
|
26
34
|
/** A vektorizált, ember-olvasható szöveg (kanonikus mező — dsgn-001 §2). */
|
|
27
35
|
content;
|
|
@@ -49,6 +57,27 @@ class FAM_Entry extends fsm_dynamo_1.DyFM_Metadata {
|
|
|
49
57
|
weight = 1;
|
|
50
58
|
/** Opcionális kvalitatív hangsúly (0..1). */
|
|
51
59
|
importance;
|
|
60
|
+
// --- Memory Activation Model (dsgn-013, CSAK a `memory` táron értelmezett) ---
|
|
61
|
+
/**
|
|
62
|
+
* Az utolsó FELIDÉZÉS (recall) epoch-ms-e — a lazy recency-decay bemenete (dsgn-013 §2). A `read` által
|
|
63
|
+
* visszaadott top-K memory-hitre frissül (F2). Default a létrehozás ideje (cold-start kegyelmi idő: egy
|
|
64
|
+
* frissen-szkennelt RÉGI memória ne tűnjön azonnal dormant-nak). NEM-memory táron értelmetlen (decayFactor=1).
|
|
65
|
+
*/
|
|
66
|
+
lastRecalledAt;
|
|
67
|
+
/** Hányszor lett FELIDÉZVE (dsgn-013 §2) — a telítődő frequency-megerősítés bemenete (`1 + log2(1+recallCount)`). */
|
|
68
|
+
recallCount = 0;
|
|
69
|
+
/**
|
|
70
|
+
* ALVÓ-e (dsgn-013 §5): a degradáció-küszöb alá esett, régóta nem-felidézett memória — NEM hidratálódik a hot
|
|
71
|
+
* LVS-poolba (RAM-/boot-spórolás), de Mongo-oldali COLD kereséssel elérhető (+ megtalálásakor reaktiválódik).
|
|
72
|
+
* SOHA nem jelent törlést (a tartalom megmarad). Indexelt a `memory` data-paramsban (a hidratálás-szűrőhöz).
|
|
73
|
+
*/
|
|
74
|
+
dormant;
|
|
75
|
+
/**
|
|
76
|
+
* LEVÁLTOTT-e (superseded, dsgn-013 §6, MAM F4): egy additív (`memory`) fájl RÉGI tartalma, amit egy újabb
|
|
77
|
+
* szken átírt/kivett (pl. kompaktálás). NEM törlés — megőrizve + cold-kereshető, de a ranking penalizálja
|
|
78
|
+
* (`memory.supersededPenalty`), így a friss verzió fölébe kerül. A content-addressed additív delta jelöli.
|
|
79
|
+
*/
|
|
80
|
+
superseded;
|
|
52
81
|
// --- forrás / pozíció (scan/import esetén) ---
|
|
53
82
|
/** Provenance (dsgn-001 §6). */
|
|
54
83
|
source;
|
|
@@ -51,5 +51,10 @@ exports.famMemory_dataParams = new fsm_dynamo_1.DyFM_DataModel_Params({
|
|
|
51
51
|
runId: { type: 'string', index: true },
|
|
52
52
|
instanceId: { type: 'string', index: true },
|
|
53
53
|
summarizedEntryIds: { type: 'string[]', default: [] },
|
|
54
|
+
// Memory Activation Model (dsgn-013) — CSAK a memory táron perzisztált. `dormant` indexelt a hidratálás-szűrőhöz.
|
|
55
|
+
lastRecalledAt: { type: 'number' },
|
|
56
|
+
recallCount: { type: 'number', default: 0 },
|
|
57
|
+
dormant: { type: 'boolean', index: true },
|
|
58
|
+
superseded: { type: 'boolean', index: true },
|
|
54
59
|
},
|
|
55
60
|
});
|
|
@@ -10,6 +10,14 @@ const fam_entry_data_model_1 = require("./fam-entry.data-model");
|
|
|
10
10
|
* A `FAM_Entry` bázisra épül; per-tár mezők: `category` (== `kind`), `isActive` (inaktív szabály
|
|
11
11
|
* NEM retrieve-elődik), `applicableScopes` (üres = globális), `dimensions`.
|
|
12
12
|
* `kind` default `'rule'`, `isActive` default `true` (dsgn-001 §4).
|
|
13
|
+
*
|
|
14
|
+
* **Rules-Management System (MP-1)** — injektálás-aware mezők:
|
|
15
|
+
* - `ruleScope`/`ruleScopeTopic` — always-apply TIER (lásd `FAM_RuleScope`); a `<topic>` suffix a
|
|
16
|
+
* `projectType`/`trigger` esetén külön (`ruleScopeTopic`, pl. `'ngx'` / `'docker'`).
|
|
17
|
+
* - `userApproved` — a user EGYESÉVEL jóváhagyta-e (a curated-core hard-rule gate-je).
|
|
18
|
+
* - `ruleId` — stabil, ember-olvasható azonosító (pl. `'G-trace'`, `'PI3'`) — propagáció/dedup kulcs.
|
|
19
|
+
* - `derivedFrom` — ha propagált/származtatott: a kanonikus forrás-`ruleId` (dedup → 1 canonical + N provenance).
|
|
20
|
+
* - `importance` (a bázison) a rules-rendszerben **0–100** skálán rangsorol injektáláskor (MP-2).
|
|
13
21
|
*/
|
|
14
22
|
class FAM_Rules_DataModel extends fam_entry_data_model_1.FAM_Entry {
|
|
15
23
|
/** Kategória (== `kind`; pl. `'coding'` / `'project'` / `'naming'`). */
|
|
@@ -20,6 +28,16 @@ class FAM_Rules_DataModel extends fam_entry_data_model_1.FAM_Entry {
|
|
|
20
28
|
applicableScopes = [];
|
|
21
29
|
/** Dimenziók (`server`|`client`|`fullstack`|`devops`|`testing`). */
|
|
22
30
|
dimensions = [];
|
|
31
|
+
/** Always-apply TIER osztály (MP-1; `FAM_RuleScope`). Üres = nincs besorolva (legacy / nem-tier). */
|
|
32
|
+
ruleScope;
|
|
33
|
+
/** A `projectType`/`trigger` TIER témája (pl. `'ngx'` / `'docker'`); más tier-eknél üres. */
|
|
34
|
+
ruleScopeTopic;
|
|
35
|
+
/** A user EGYESÉVEL approve-olta-e (curated-core hard-rule gate). */
|
|
36
|
+
userApproved = false;
|
|
37
|
+
/** Stabil, ember-olvasható azonosító (pl. `'G-trace'`, `'PI3'`) — propagáció/dedup kulcs. */
|
|
38
|
+
ruleId;
|
|
39
|
+
/** Propagált/származtatott szabálynál a kanonikus forrás-`ruleId` (dedup-provenance). */
|
|
40
|
+
derivedFrom;
|
|
23
41
|
constructor(set) {
|
|
24
42
|
super(set);
|
|
25
43
|
if (set) {
|
|
@@ -45,7 +63,13 @@ exports.famRules_dataParams = new fsm_dynamo_1.DyFM_DataModel_Params({
|
|
|
45
63
|
},
|
|
46
64
|
category: { type: 'string', index: true },
|
|
47
65
|
isActive: { type: 'boolean', index: true, default: true },
|
|
66
|
+
globalRule: { type: 'boolean', index: true },
|
|
48
67
|
applicableScopes: { type: 'array', default: [] },
|
|
49
68
|
dimensions: { type: 'string[]', index: true, default: [] },
|
|
69
|
+
ruleScope: { type: 'string', index: true },
|
|
70
|
+
ruleScopeTopic: { type: 'string', index: true },
|
|
71
|
+
userApproved: { type: 'boolean', index: true, default: false },
|
|
72
|
+
ruleId: { type: 'string', index: true },
|
|
73
|
+
derivedFrom: { type: 'string', index: true },
|
|
50
74
|
},
|
|
51
75
|
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.famScanJob_dataParams = exports.FAM_ScanJob_DataModel = void 0;
|
|
4
|
+
const fsm_dynamo_1 = require("@futdevpro/fsm-dynamo");
|
|
5
|
+
/**
|
|
6
|
+
* `FAM_ScanJob_DataModel` (FEAT-008 Phase 4, user-FR 2026-06-22) — egy szerver-oldali scan-job DURABLE pillanatképe
|
|
7
|
+
* a `fam_scan_jobs` collection-ben. Cél: a job (per-projekt állapottal) TÚLÉLJE a server-restart/crash/close-t, és a
|
|
8
|
+
* szerver **AUTO-RESUME-olja a megszakítási ponttól** (a már `ok` projekteket átugorva). A `projects[]` a per-projekt
|
|
9
|
+
* checkpoint (státusz + eredmény); a `specs[]` az újrafuttatáshoz kell (path/project/org/table). `extends DyFM_Metadata`.
|
|
10
|
+
*
|
|
11
|
+
* A `jobId` a UUID (mező, indexelt) — NEM a `_id` (a Dynamo a `_id`-t ObjectId-ra cast-olja, a client-UUID nem fér).
|
|
12
|
+
*/
|
|
13
|
+
class FAM_ScanJob_DataModel extends fsm_dynamo_1.DyFM_Metadata {
|
|
14
|
+
/** A job UUID-ja (a getStatus/resume ezen keres). */
|
|
15
|
+
jobId;
|
|
16
|
+
/** A globális státusz. */
|
|
17
|
+
status;
|
|
18
|
+
/** A projektek száma. */
|
|
19
|
+
total;
|
|
20
|
+
/** A jelenleg feldolgozott projekt indexe. */
|
|
21
|
+
currentIndex;
|
|
22
|
+
/** A sikeres projektek száma. */
|
|
23
|
+
okCount;
|
|
24
|
+
/** A projektek közti cooldown (ms). */
|
|
25
|
+
cooldownMs;
|
|
26
|
+
/** A feltételes cooldown küszöbe. */
|
|
27
|
+
cooldownMinChunks;
|
|
28
|
+
/** Indítás epoch-ms. */
|
|
29
|
+
startedAt;
|
|
30
|
+
/** Befejezés epoch-ms (ha kész). */
|
|
31
|
+
finishedAt;
|
|
32
|
+
/** Per-projekt checkpoint (státusz + eredmény) — a resume ebből tudja, mi van már kész. */
|
|
33
|
+
projects = [];
|
|
34
|
+
/** A scan-specifikációk (a resume újrafuttatásához: path/project/org/table). */
|
|
35
|
+
specs = [];
|
|
36
|
+
/** A hívó (CLI) start/source location-je — a resume-kori path-feloldáshoz. */
|
|
37
|
+
sourceLocation;
|
|
38
|
+
/** Hányszor lett már auto-resume-olva (FEAT-008 Phase 5) — a resume-loop elleni cap (`scan.maxResumeCount`) bemenete. */
|
|
39
|
+
resumeCount;
|
|
40
|
+
/** Terminál ok-szöveg (pl. „resume-cap túllépve") — `failed`/`cancelled` job-on, a diagnózishoz. */
|
|
41
|
+
failureReason;
|
|
42
|
+
constructor(set) {
|
|
43
|
+
super(set);
|
|
44
|
+
if (set) {
|
|
45
|
+
fsm_dynamo_1.DyFM_Object.cleanAssign(this, set);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.FAM_ScanJob_DataModel = FAM_ScanJob_DataModel;
|
|
50
|
+
/**
|
|
51
|
+
* MongoDB data-params a `fam_scan_jobs` collection-höz (FEAT-008 Phase 4). Nincs vektor-mező (a job nem retrieve-elhető
|
|
52
|
+
* tartalom). A `jobId` + `status` indexelt (a getStatus + a resume-lekérdezés gyors útja).
|
|
53
|
+
*/
|
|
54
|
+
exports.famScanJob_dataParams = new fsm_dynamo_1.DyFM_DataModel_Params({
|
|
55
|
+
dataName: 'fam_scan_jobs',
|
|
56
|
+
addArchive: true,
|
|
57
|
+
properties: {
|
|
58
|
+
jobId: { type: 'string', index: true },
|
|
59
|
+
status: { type: 'string', index: true },
|
|
60
|
+
total: { type: 'number' },
|
|
61
|
+
currentIndex: { type: 'number' },
|
|
62
|
+
okCount: { type: 'number' },
|
|
63
|
+
cooldownMs: { type: 'number' },
|
|
64
|
+
cooldownMinChunks: { type: 'number' },
|
|
65
|
+
startedAt: { type: 'number' },
|
|
66
|
+
finishedAt: { type: 'number' },
|
|
67
|
+
projects: { type: 'array', default: [] },
|
|
68
|
+
specs: { type: 'array', default: [] },
|
|
69
|
+
sourceLocation: { type: 'string' },
|
|
70
|
+
resumeCount: { type: 'number' },
|
|
71
|
+
failureReason: { type: 'string' },
|
|
72
|
+
},
|
|
73
|
+
});
|
|
@@ -21,6 +21,14 @@ class FAM_Scope_DataModel extends fsm_dynamo_1.DyFM_Metadata {
|
|
|
21
21
|
parentScopeId;
|
|
22
22
|
/** Denormalizált gyors-alias gyorsítótár (forrás: `fam_reference`; szinkron MP-3). */
|
|
23
23
|
aliases = [];
|
|
24
|
+
/**
|
|
25
|
+
* Kanonikus repo-kulcs (scope-prevenció, user-FR 2026-06-22) — a stabil projekt-identitás a git-remote-ból
|
|
26
|
+
* (`org/repo`, lowercase) vagy fallback a package-névből. A SCAN stamp-eli a project-layer scope-ra a felismert
|
|
27
|
+
* git/package identitásból (`FAM_ProjectIdentity_Util.canonicalRepoKey`). Erre épül a repo-kulcs alapú
|
|
28
|
+
* duplikátum-detekció (`detectIssues.repoKeyDuplicates`), ami a KÜLÖNBÖZŐ nevű, de AZONOS repo eseteket
|
|
29
|
+
* (pl. import-`10.3.3` vs scan-`ccap-revisioned`) is elkapja — amit a puszta név-egyezés kihagy.
|
|
30
|
+
*/
|
|
31
|
+
canonicalRepoKey;
|
|
24
32
|
/** Leírás. */
|
|
25
33
|
description;
|
|
26
34
|
/** Ki hozta létre (szűkített: `manual` | `agent-write` | `import`). */
|
|
@@ -46,6 +54,7 @@ exports.famScope_dataParams = new fsm_dynamo_1.DyFM_DataModel_Params({
|
|
|
46
54
|
canonicalName: { type: 'string', index: true, required: true },
|
|
47
55
|
parentScopeId: { type: 'string', index: true },
|
|
48
56
|
aliases: { type: 'string[]', index: true, default: [] },
|
|
57
|
+
canonicalRepoKey: { type: 'string', index: true },
|
|
49
58
|
description: { type: 'string' },
|
|
50
59
|
createdBy: { type: 'string', index: true },
|
|
51
60
|
},
|
|
@@ -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
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FAM_DedupWarn_ControlService = void 0;
|
|
4
|
+
const fam_vector_search_control_service_1 = require("./fam-vector-search.control-service");
|
|
5
|
+
/**
|
|
6
|
+
* `FAM_DedupWarn_ControlService` (user-FR 2026-06-25) — **near-duplikátum-figyelmeztetés** íráskor: egy ÚJ entry
|
|
7
|
+
* vektorával megkeresi a tár legközelebbi szomszédait, és ha valamelyik a küszöb (`threshold`) FÖLÖTT van, jelzi
|
|
8
|
+
* (NEM blokkol — csak warning: „ez majdnem ugyanaz, mint X; érdemes lehet UPDATE-elni a meglévőt új entry helyett").
|
|
9
|
+
* A meglévő `FAM_DuplicateScan` az EGÉSZ tár O(n²) cluster-scanje (utólagos takarítás); EZ a KÖNNYŰ, ÍRÁS-IDEJŰ
|
|
10
|
+
* változat (1 vektor-keresés a friss vektorral) — a duplikáció-KELETKEZÉS megelőzésére.
|
|
11
|
+
*/
|
|
12
|
+
class FAM_DedupWarn_ControlService {
|
|
13
|
+
static _instance;
|
|
14
|
+
static getInstance() {
|
|
15
|
+
if (!FAM_DedupWarn_ControlService._instance) {
|
|
16
|
+
FAM_DedupWarn_ControlService._instance = new FAM_DedupWarn_ControlService();
|
|
17
|
+
}
|
|
18
|
+
return FAM_DedupWarn_ControlService._instance;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Az új tartalom (már kiszámolt `vector`) near-duplikátumai a `table` hidratált poolján: a legközelebbi `topK`
|
|
22
|
+
* szomszéd, ÖNMAGÁT (`excludeId`) kizárva, a `threshold` fölött. Üres vektor / nincs jelölt → üres lista.
|
|
23
|
+
*/
|
|
24
|
+
async check(set) {
|
|
25
|
+
if (!set.vector.length) {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
// +1 a topK-hoz: az önmagát (excludeId) is visszaadja score ~1.0-val, azt kiszűrjük.
|
|
29
|
+
const hits = await fam_vector_search_control_service_1.FAM_VectorSearch_ControlService.getInstance().search({
|
|
30
|
+
table: set.table,
|
|
31
|
+
queryVector: set.vector,
|
|
32
|
+
topK: (set.topK ?? 5) + 1,
|
|
33
|
+
});
|
|
34
|
+
return hits
|
|
35
|
+
.filter((hit) => hit.id !== set.excludeId && hit.score >= set.threshold)
|
|
36
|
+
.slice(0, set.topK ?? 5)
|
|
37
|
+
.map((hit) => ({ id: hit.id, score: hit.score }));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.FAM_DedupWarn_ControlService = FAM_DedupWarn_ControlService;
|