@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
|
@@ -19,9 +19,11 @@ const fam_content_hash_util_1 = require("../_collections/fam-content-hash.util")
|
|
|
19
19
|
const fam_git_repo_util_1 = require("../_collections/fam-git-repo.util");
|
|
20
20
|
const fam_scan_reconcile_util_1 = require("../_collections/fam-scan-reconcile.util");
|
|
21
21
|
const fam_secret_content_util_1 = require("../_collections/fam-secret-content.util");
|
|
22
|
+
const fam_frontmatter_util_1 = require("../_collections/fam-frontmatter.util");
|
|
22
23
|
const fam_project_identity_util_1 = require("../_collections/fam-project-identity.util");
|
|
23
24
|
const fam_scan_path_util_1 = require("../_collections/fam-scan-path.util");
|
|
24
25
|
const fam_scan_progress_util_1 = require("../_collections/fam-scan-progress.util");
|
|
26
|
+
const fam_scan_progress_sink_service_1 = require("../../../_collections/fam-scan-progress-sink.service");
|
|
25
27
|
const fam_scan_summary_util_1 = require("../_collections/fam-scan-summary.util");
|
|
26
28
|
const scope_reference_2 = require("../../scope-reference");
|
|
27
29
|
const fam_reference_data_model_1 = require("../../../_models/data-models/fam-reference.data-model");
|
|
@@ -59,6 +61,16 @@ class FAM_Ingest_ControlService {
|
|
|
59
61
|
const sample = content.length > 8192 ? content.slice(0, 8192) : content;
|
|
60
62
|
return sample.indexOf(String.fromCharCode(0)) !== -1;
|
|
61
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Tábla-vezérelt ADDITÍV (accumulate-only) szabály (user-FR 2026-06-24): a `memory` tár MINDIG content-addressed
|
|
66
|
+
* additív — a memória-bejegyzések sosem „íródnak felül azonos location-nél", így a kompaktálás-előtti állapot
|
|
67
|
+
* a forrás zsugorodása UTÁN is megmarad a FAM-ban (vesztességmentes superset). A többi tár (codebase/documents/
|
|
68
|
+
* rules/...) a szokásos `(fájl, chunkIndex)` delta-t használja; rájuk a `preserveHistory` request-flag kérhet
|
|
69
|
+
* explicit additívot. Pure + statikus (tesztelhető).
|
|
70
|
+
*/
|
|
71
|
+
static isAccumulateOnly(table) {
|
|
72
|
+
return table === fam_table_type_enum_1.FAM_Table.memory;
|
|
73
|
+
}
|
|
62
74
|
/** Default issuer az ingest-műveletekhez. */
|
|
63
75
|
issuer = 'FAM_Ingest_ControlService';
|
|
64
76
|
static getInstance() {
|
|
@@ -180,7 +192,7 @@ class FAM_Ingest_ControlService {
|
|
|
180
192
|
});
|
|
181
193
|
runId = openedRun._id ?? runId;
|
|
182
194
|
// (4) Per-fájl feldolgozás (flow-limit kötegekben; a per-fájl hiba NEM buktatja a run-t).
|
|
183
|
-
const verdicts = { new: 0, modified: 0, equal: 0, deleted: 0 };
|
|
195
|
+
const verdicts = { new: 0, modified: 0, equal: 0, deleted: 0, superseded: 0 };
|
|
184
196
|
const errors = [];
|
|
185
197
|
let chunkCount = 0;
|
|
186
198
|
const flowLimit = await this.resolveFlowLimit(canonicalScope);
|
|
@@ -195,12 +207,21 @@ class FAM_Ingest_ControlService {
|
|
|
195
207
|
? 0
|
|
196
208
|
: fam_cooldown_util_1.FAM_Cooldown_Util.resolveMs((await config_control_service_1.FAM_Config_ControlService.getInstance().resolve('scan.sectionCooldownMs', { scopePath: canonicalScope })).value);
|
|
197
209
|
let lastProgressAt = startedAt;
|
|
210
|
+
// Sub-progress (user-FR 2026-06-22): a felderítés→embedding közti csend kitöltése — mit gyűjtöttünk be +
|
|
211
|
+
// hogyan route-oltuk (mely tárakba), MIELŐTT a throttle-olt feldolgozás-sorok elindulnak.
|
|
212
|
+
if (!isDryRun && total > 0) {
|
|
213
|
+
const scopeLabel = canonicalScope.map((ref) => ref.canonicalName).join(' > ') || '(globális)';
|
|
214
|
+
fsm_dynamo_1.DyFM_Log.info(`[famIngest] [${scopeLabel}] felderítés kész: ${total} fájl begyűjtve `
|
|
215
|
+
+ `(${discovery.skippedFiles.length} kihagyva, tárak: ${discovery.tables.map((table) => String(table)).join('+') || '—'}) `
|
|
216
|
+
+ '→ embedding indul');
|
|
217
|
+
}
|
|
198
218
|
for (let i = 0; i < total; i += flowLimit) {
|
|
199
219
|
const batch = discovery.routedFiles.slice(i, i + flowLimit);
|
|
200
220
|
const results = await Promise.all(batch.map((file) => this.processFile({
|
|
201
221
|
file: file, runId: runId, scopePath: canonicalScope,
|
|
202
222
|
resolvedRoot: discovery.resolvedRoot,
|
|
203
223
|
dryRun: isDryRun, issuer: issuer, errors: errors,
|
|
224
|
+
preserveHistory: request.preserveHistory === true,
|
|
204
225
|
})));
|
|
205
226
|
for (const result of results) {
|
|
206
227
|
if (!result) {
|
|
@@ -215,10 +236,17 @@ class FAM_Ingest_ControlService {
|
|
|
215
236
|
const now = Date.now();
|
|
216
237
|
if (!isDryRun && (processed >= total || now - lastProgressAt >= progressIntervalMs)) {
|
|
217
238
|
lastProgressAt = now;
|
|
239
|
+
const scopeLeaf = canonicalScope.length ? canonicalScope[canonicalScope.length - 1].canonicalName : '';
|
|
218
240
|
fsm_dynamo_1.DyFM_Log.info(fam_scan_progress_util_1.FAM_ScanProgress_Util.progressLine({
|
|
241
|
+
scopeLabel: scopeLeaf || undefined,
|
|
219
242
|
processed: processed, total: total, chunkCount: chunkCount,
|
|
220
243
|
verdicts: verdicts, elapsedSeconds: (now - startedAt) / 1000,
|
|
221
244
|
}));
|
|
245
|
+
// Sub-progress sink (user-FR): a within-project haladás a scan-job status-ába → a thin-CLI poll is látja.
|
|
246
|
+
fam_scan_progress_sink_service_1.FAM_ScanProgress_Sink.set({
|
|
247
|
+
scopeLabel: scopeLeaf, processed: processed, total: total, chunkCount: chunkCount,
|
|
248
|
+
pct: total ? Math.round((processed / total) * 100) : 100, updatedAt: now,
|
|
249
|
+
});
|
|
222
250
|
}
|
|
223
251
|
// Szekció-cooldown: a batch-ek KÖZÉ (NEM az utolsó után) — opcionális gép-tehermentesítő szünet.
|
|
224
252
|
if (sectionCooldownMs > 0 && processed < total) {
|
|
@@ -228,7 +256,11 @@ class FAM_Ingest_ControlService {
|
|
|
228
256
|
// (4b) Törölt-fájl reconciliation (whole-file orphan) — CSAK folder/project scan (a `scan-file`
|
|
229
257
|
// egyetlen fájlt céloz, nem reconciliál). A lemezről eltűnt fájlok chunkjai soft-delete-elődnek; a
|
|
230
258
|
// számuk a `verdicts.deleted`-be (a per-fájl, fájlon-belüli `deleted`-ek mellé).
|
|
231
|
-
|
|
259
|
+
// ADDITÍV mód (user-FR 2026-06-22/24): `preserveHistory` request-flag VAGY a `memory` tár (tábla-vezérelt) →
|
|
260
|
+
// a reconcile-törlés KIHAGYVA (a FAM superset-ként megtartja a forrásból eltűnt — pl. kompaktálódó memóriám —
|
|
261
|
+
// bejegyzéseit is; nincs destruktív felülírás). A `memory` ezért SOHA nem reconciliál, flag nélkül sem.
|
|
262
|
+
if (request.operation !== 'scan-file' && !request.preserveHistory
|
|
263
|
+
&& !(request.tableOverride !== undefined && FAM_Ingest_ControlService.isAccumulateOnly(request.tableOverride))) {
|
|
232
264
|
const reconcileIgnored = await this.resolveReconcileIgnored(canonicalScope);
|
|
233
265
|
verdicts.deleted += await this.reconcileDeletedFiles({
|
|
234
266
|
discovery: discovery, scopePath: canonicalScope, issuer: issuer, dryRun: request.dryRun ?? false,
|
|
@@ -284,7 +316,7 @@ class FAM_Ingest_ControlService {
|
|
|
284
316
|
* soft-delete). A per-fájl hiba `errors[]`-be (no-silent-failure) + `null` visszatérés (a run nem bukik).
|
|
285
317
|
*/
|
|
286
318
|
async processFile(set) {
|
|
287
|
-
|
|
319
|
+
let table = set.file.route;
|
|
288
320
|
try {
|
|
289
321
|
const rawContent = fs.readFileSync(set.file.absolutePath, 'utf-8');
|
|
290
322
|
// Tartalmi secret-redakció (user-FR): a forrásba hardcode-olt API-kulcs (sk-/AKIA/ghp_/PEM/JWT) a tárolt
|
|
@@ -294,7 +326,16 @@ class FAM_Ingest_ControlService {
|
|
|
294
326
|
if (redaction.count > 0) {
|
|
295
327
|
fsm_dynamo_1.DyFM_Log.warn(`[famIngest] secret-redakció: ${redaction.count} token redaktálva (${set.file.relativePath})`);
|
|
296
328
|
}
|
|
297
|
-
|
|
329
|
+
// MP-3 / F-A: fájl-fej `---`-frontmatter → ingest-irányítók + fejléc-mentes TÖRZS. Frontmatter nélkül NO-OP
|
|
330
|
+
// (`body===content`). A `table`-override (pl. `table: rules`) + `splitMode` + a per-entry rule-mezők innen jönnek.
|
|
331
|
+
const front = fam_frontmatter_util_1.FAM_Frontmatter_Util.parse(redaction.text);
|
|
332
|
+
const directives = front.directives;
|
|
333
|
+
const overrideTable = this.resolveDirectiveTable(directives.table);
|
|
334
|
+
if (overrideTable) {
|
|
335
|
+
table = overrideTable;
|
|
336
|
+
}
|
|
337
|
+
const splitMode = directives.splitMode === true ? 'heading' : (directives.splitMode || undefined);
|
|
338
|
+
const content = front.body;
|
|
298
339
|
// FAM-REV-064 bináris-tartalom guard: a denylist-en átcsúszott, ismeretlen-kiterjesztésű bináris
|
|
299
340
|
// (null-byte az első ~8KB-ban) NE kerüljön a generic chunkerbe (szemét-chunk ellen). Skip → 0 chunk.
|
|
300
341
|
if (FAM_Ingest_ControlService.isLikelyBinary(content)) {
|
|
@@ -302,6 +343,7 @@ class FAM_Ingest_ControlService {
|
|
|
302
343
|
}
|
|
303
344
|
const chunks = await fam_chunker_control_service_1.FAM_Chunker_ControlService.getInstance().chunkFile({
|
|
304
345
|
content: content, relativePath: set.file.relativePath, table: table, scopePath: set.scopePath,
|
|
346
|
+
splitMode: splitMode,
|
|
305
347
|
});
|
|
306
348
|
const registryEntry = embedding_1.FAM_StoreRegistry_Util.getEntry(table);
|
|
307
349
|
if (!registryEntry) {
|
|
@@ -311,11 +353,17 @@ class FAM_Ingest_ControlService {
|
|
|
311
353
|
// Existing-by-key betöltés a KANONIKUS abszolút útvonalon (FAM-REV-050) — a scan-root-független
|
|
312
354
|
// fájl-identitás, így a delta bármely scan-gyökérről ugyanazt a fájlt matcheli (nincs duplikáció).
|
|
313
355
|
const existing = await this.loadExistingChunks(dataService, set.file.absolutePath, set.scopePath);
|
|
314
|
-
|
|
356
|
+
// ADDITÍV (content-addressed) delta → a régi (kompaktálás-előtti) tartalom MEGMARAD, sose `deleted`/`modified`
|
|
357
|
+
// (a FAM a memória minden valaha-látott állapotának vesztességmentes uniója). TÁBLA-VEZÉRELT (user-FR
|
|
358
|
+
// 2026-06-24): a `memory` tár MINDIG additív (a „felülírás azonos location-nél" csak kódbázis/dok/rules);
|
|
359
|
+
// a `preserveHistory` request-flag pedig bármely tárra explicit additív szkent kérhet.
|
|
360
|
+
const accumulateOnly = set.preserveHistory === true || FAM_Ingest_ControlService.isAccumulateOnly(table);
|
|
361
|
+
const deltaItems = fam_delta_compare_util_1.FAM_DeltaCompare_Util.compare(chunks, existing, accumulateOnly);
|
|
315
362
|
if (!set.dryRun) {
|
|
316
363
|
await this.applyVerdicts({
|
|
317
364
|
deltaItems: deltaItems, file: set.file, table: table, runId: set.runId,
|
|
318
365
|
scopePath: set.scopePath, root: set.resolvedRoot, dataService: dataService,
|
|
366
|
+
directives: directives,
|
|
319
367
|
});
|
|
320
368
|
}
|
|
321
369
|
return {
|
|
@@ -373,6 +421,10 @@ class FAM_Ingest_ControlService {
|
|
|
373
421
|
// re-embed nélkül (a friss chunk charStart/charEnd/sor-pozíciója felülír, ha eltér).
|
|
374
422
|
await set.dataService.backfillPosition(item.existingId, item.chunk.position);
|
|
375
423
|
}
|
|
424
|
+
// Git-provenance backfill (user-FR 2026-06-22): a CSAK-metaadat frissítés — a git-provenance
|
|
425
|
+
// feature ELŐTT ingestelt korpusz `equal`-chunkjai is megkapják a repoUrl/repoRelativePath/
|
|
426
|
+
// repoBranch-et RE-EMBED NÉLKÜL (a control-service számolja, így nincs data-service↔ingest ciklus).
|
|
427
|
+
await set.dataService.backfillGitProvenance(item.existingId, fam_git_repo_util_1.FAM_GitRepo_Util.sourceFieldsFor(set.file.absolutePath));
|
|
376
428
|
}
|
|
377
429
|
catch {
|
|
378
430
|
// best-effort metaadat-frissítés — a content már naprakész (equal), nem eszkaláljuk.
|
|
@@ -384,7 +436,7 @@ class FAM_Ingest_ControlService {
|
|
|
384
436
|
await this.persistAndEmbed({
|
|
385
437
|
chunk: item.chunk, existingId: item.existingId, contentHash: item.contentHash,
|
|
386
438
|
file: set.file, table: set.table, runId: set.runId, scopePath: set.scopePath,
|
|
387
|
-
root: set.root, dataService: set.dataService,
|
|
439
|
+
root: set.root, dataService: set.dataService, directives: set.directives,
|
|
388
440
|
});
|
|
389
441
|
}
|
|
390
442
|
}
|
|
@@ -398,12 +450,21 @@ class FAM_Ingest_ControlService {
|
|
|
398
450
|
}
|
|
399
451
|
continue;
|
|
400
452
|
}
|
|
453
|
+
if (item.verdict === 'superseded') {
|
|
454
|
+
// dsgn-013 §6 (MAM F4): a fájl régi, már átírt tartalma — NEM törlés (a content + vektor MEGMARAD a
|
|
455
|
+
// poolban is, cold-kereshető), csak `superseded=true` jelölés → a memory-ranking penalizálja (a friss
|
|
456
|
+
// verzió fölébe kerül). A pool-ból NEM vesszük ki (a penalty süllyeszti; a dormancy idővel de-hidratálja).
|
|
457
|
+
if (item.existingId) {
|
|
458
|
+
await set.dataService.setSuperseded(item.existingId, true);
|
|
459
|
+
}
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
401
462
|
// new / modified → upsert + embed (a `chunk` itt sosem null).
|
|
402
463
|
if (item.chunk) {
|
|
403
464
|
await this.persistAndEmbed({
|
|
404
465
|
chunk: item.chunk, existingId: item.existingId, contentHash: item.contentHash,
|
|
405
466
|
file: set.file, table: set.table, runId: set.runId, scopePath: set.scopePath,
|
|
406
|
-
root: set.root, dataService: set.dataService,
|
|
467
|
+
root: set.root, dataService: set.dataService, directives: set.directives,
|
|
407
468
|
});
|
|
408
469
|
}
|
|
409
470
|
}
|
|
@@ -437,6 +498,9 @@ class FAM_Ingest_ControlService {
|
|
|
437
498
|
addedBy: 'scan',
|
|
438
499
|
ingestRunId: set.runId,
|
|
439
500
|
});
|
|
501
|
+
// MP-3 / F-A: a frontmatter per-entry rule-mezőit (importance/userApproved/ruleScope/…) + tageket rákötjük.
|
|
502
|
+
// CSAK definiált értékek (Object.assign-nal) — a nem-rules táron a nem-deklarált mezők úgyis nem persistálnak.
|
|
503
|
+
this.applyDirectiveFields(entry, set.directives);
|
|
440
504
|
// Persist (saveData = upsert: meglévő `_id` → modify, hiányzó → insert).
|
|
441
505
|
const saved = await set.dataService.saveData(entry);
|
|
442
506
|
// Embed CSAK a new/modified-re (MP-2). Az embed-hiba az entry-t `error`-státuszba teszi +
|
|
@@ -445,6 +509,51 @@ class FAM_Ingest_ControlService {
|
|
|
445
509
|
table: set.table, entry: saved, callType: 'embed-write',
|
|
446
510
|
});
|
|
447
511
|
}
|
|
512
|
+
/** A frontmatter `table:` direktíva → érvényes `FAM_Table` (ismeretlen → undefined, a file-routing marad). */
|
|
513
|
+
resolveDirectiveTable(raw) {
|
|
514
|
+
if (!raw) {
|
|
515
|
+
return undefined;
|
|
516
|
+
}
|
|
517
|
+
return Object.values(fam_table_type_enum_1.FAM_Table).find((value) => value === raw);
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* MP-3 / F-A: a frontmatter per-entry rule-mezőit + tageket az `FAM_Entry`-re köti (CSAK definiált értékek). A
|
|
521
|
+
* nem-`rules` táron a nem-deklarált mezők úgyis nem persistálnak (a dataParams.properties dönt), így ártalmatlan.
|
|
522
|
+
*/
|
|
523
|
+
applyDirectiveFields(entry, directives) {
|
|
524
|
+
if (!directives) {
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
const target = entry;
|
|
528
|
+
if (typeof directives.importance === 'number') {
|
|
529
|
+
target.importance = directives.importance;
|
|
530
|
+
}
|
|
531
|
+
if (typeof directives.userApproved === 'boolean') {
|
|
532
|
+
target.userApproved = directives.userApproved;
|
|
533
|
+
}
|
|
534
|
+
if (typeof directives.globalRule === 'boolean') {
|
|
535
|
+
target.globalRule = directives.globalRule;
|
|
536
|
+
}
|
|
537
|
+
if (directives.ruleScope) {
|
|
538
|
+
target.ruleScope = directives.ruleScope;
|
|
539
|
+
}
|
|
540
|
+
if (directives.ruleScopeTopic) {
|
|
541
|
+
target.ruleScopeTopic = directives.ruleScopeTopic;
|
|
542
|
+
}
|
|
543
|
+
if (directives.ruleId) {
|
|
544
|
+
target.ruleId = directives.ruleId;
|
|
545
|
+
}
|
|
546
|
+
if (directives.derivedFrom) {
|
|
547
|
+
target.derivedFrom = directives.derivedFrom;
|
|
548
|
+
}
|
|
549
|
+
if (directives.category) {
|
|
550
|
+
target.category = directives.category;
|
|
551
|
+
}
|
|
552
|
+
if (directives.tags && directives.tags.length) {
|
|
553
|
+
const existing = Array.isArray(entry.tags) ? entry.tags : [];
|
|
554
|
+
target.tags = [...new Set([...existing, ...directives.tags])];
|
|
555
|
+
}
|
|
556
|
+
}
|
|
448
557
|
/**
|
|
449
558
|
* A file-rendszer-összefoglaló (`knowledge`, kind `fs-summary`) generálása + idempotens upsert-je a scan után.
|
|
450
559
|
* A `FAM_ScanSummary_Util` a mappastruktúrából építi a kereshető „mi van a projektben" szöveget. **Identitás:**
|
|
@@ -459,15 +568,16 @@ class FAM_Ingest_ControlService {
|
|
|
459
568
|
}
|
|
460
569
|
const dataService = new embedding_1.FAM_Entry_DataService({ dataParams: registryEntry.dataParams, issuer: set.issuer });
|
|
461
570
|
const scopeLabel = set.scopePath.map((ref) => ref.canonicalName).join(' > ') || '(globális)';
|
|
462
|
-
|
|
571
|
+
// NO-LOSS split (user-FR 2026-06-22): nagy fánál TÖBB, egyenlő részre bontva — minden fájl szerepel,
|
|
572
|
+
// semmit nem nyelünk le. Részenként EGY-EGY `knowledge`-entry (`chunkIndex` = rész-index).
|
|
573
|
+
const parts = fam_scan_summary_util_1.FAM_ScanSummary_Util.buildParts({
|
|
463
574
|
scopeLabel: scopeLabel,
|
|
464
575
|
root: set.discovery.resolvedRoot,
|
|
465
576
|
relativePaths: set.discovery.routedFiles.map((file) => file.relativePath),
|
|
466
577
|
skippedCount: set.discovery.skippedFiles.length,
|
|
467
578
|
tables: set.discovery.tables.map((table) => String(table)),
|
|
468
579
|
});
|
|
469
|
-
|
|
470
|
-
// Meglévő összefoglaló a (source-type + scan-gyökér + levél-scope) identitáson.
|
|
580
|
+
// A meglévő összefoglaló-részek a (source-type + scan-gyökér + levél-scope) identitáson, rész-index szerint.
|
|
471
581
|
const leafScopeId = set.scopePath.length ? set.scopePath[set.scopePath.length - 1].scopeId : undefined;
|
|
472
582
|
const filter = {};
|
|
473
583
|
filter['source.type'] = fam_scan_summary_util_1.FAM_ScanSummary_Util.SOURCE_TYPE;
|
|
@@ -477,33 +587,54 @@ class FAM_Ingest_ControlService {
|
|
|
477
587
|
filter['scopePath.scopeId'] = leafScopeId;
|
|
478
588
|
}
|
|
479
589
|
const existing = await dataService.findHydratableList(filter);
|
|
480
|
-
const
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
590
|
+
const existingByIndex = new Map();
|
|
591
|
+
for (const item of existing) {
|
|
592
|
+
existingByIndex.set(item.chunkIndex ?? 0, item); // régi egy-részes entry: chunkIndex undefined → 0
|
|
593
|
+
}
|
|
594
|
+
let changed = 0;
|
|
595
|
+
for (let index = 0; index < parts.length; index++) {
|
|
596
|
+
const partContent = parts[index];
|
|
597
|
+
const partHash = fam_content_hash_util_1.FAM_ContentHash_Util.hash(partContent);
|
|
598
|
+
const existingPart = existingByIndex.get(index);
|
|
599
|
+
if (existingPart?.contentHash === partHash) {
|
|
600
|
+
continue; // változatlan rész → NO-OP (nincs fölös re-embed)
|
|
601
|
+
}
|
|
602
|
+
const entry = new fam_entry_data_model_1.FAM_Entry({
|
|
603
|
+
_id: existingPart?._id,
|
|
604
|
+
table: fam_table_type_enum_1.FAM_Table.knowledge,
|
|
605
|
+
content: partContent,
|
|
606
|
+
contentHash: partHash,
|
|
607
|
+
embeddingStatus: 'pending',
|
|
608
|
+
scopePath: set.scopePath,
|
|
609
|
+
chunkIndex: index,
|
|
610
|
+
kind: fam_scan_summary_util_1.FAM_ScanSummary_Util.KIND,
|
|
611
|
+
tags: ['fs-summary', 'filesystem', 'scan-summary'],
|
|
612
|
+
weight: 2,
|
|
613
|
+
source: {
|
|
614
|
+
type: 'scan-summary', root: set.discovery.resolvedRoot, absolutePath: set.discovery.resolvedRoot,
|
|
615
|
+
},
|
|
616
|
+
addedBy: 'scan',
|
|
617
|
+
ingestRunId: set.runId,
|
|
618
|
+
});
|
|
619
|
+
const saved = await dataService.saveData(entry);
|
|
620
|
+
await embedding_1.FAM_EmbeddingPipeline_ControlService.getInstance().embedAndPersist({
|
|
621
|
+
table: fam_table_type_enum_1.FAM_Table.knowledge, entry: saved, callType: 'embed-write',
|
|
622
|
+
});
|
|
623
|
+
changed++;
|
|
624
|
+
}
|
|
625
|
+
// Fölös részek (egy korábbi, NAGYOBB összefoglaló most-már-nem-létező részei) → soft-delete (no-orphan).
|
|
626
|
+
let removed = 0;
|
|
627
|
+
for (const item of existing) {
|
|
628
|
+
if ((item.chunkIndex ?? 0) >= parts.length) {
|
|
629
|
+
await dataService.deleteData(item._id);
|
|
630
|
+
removed++;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
if (changed > 0 || removed > 0) {
|
|
634
|
+
fsm_dynamo_1.DyFM_Log.info(`[famIngest] fs-summary (${scopeLabel}): ${parts.length} rész `
|
|
635
|
+
+ `(${changed} írva/frissítve, ${removed} fölös törölve), ${set.discovery.routedFiles.length} fájl, `
|
|
636
|
+
+ `${set.discovery.tables.length} tár`);
|
|
484
637
|
}
|
|
485
|
-
const entry = new fam_entry_data_model_1.FAM_Entry({
|
|
486
|
-
_id: existingEntry?._id,
|
|
487
|
-
table: fam_table_type_enum_1.FAM_Table.knowledge,
|
|
488
|
-
content: content,
|
|
489
|
-
contentHash: contentHash,
|
|
490
|
-
embeddingStatus: 'pending',
|
|
491
|
-
scopePath: set.scopePath,
|
|
492
|
-
kind: fam_scan_summary_util_1.FAM_ScanSummary_Util.KIND,
|
|
493
|
-
tags: ['fs-summary', 'filesystem', 'scan-summary'],
|
|
494
|
-
weight: 2,
|
|
495
|
-
source: {
|
|
496
|
-
type: 'scan-summary', root: set.discovery.resolvedRoot, absolutePath: set.discovery.resolvedRoot,
|
|
497
|
-
},
|
|
498
|
-
addedBy: 'scan',
|
|
499
|
-
ingestRunId: set.runId,
|
|
500
|
-
});
|
|
501
|
-
const saved = await dataService.saveData(entry);
|
|
502
|
-
await embedding_1.FAM_EmbeddingPipeline_ControlService.getInstance().embedAndPersist({
|
|
503
|
-
table: fam_table_type_enum_1.FAM_Table.knowledge, entry: saved, callType: 'embed-write',
|
|
504
|
-
});
|
|
505
|
-
fsm_dynamo_1.DyFM_Log.info(`[famIngest] fs-summary ${existingEntry ? 'frissítve' : 'létrehozva'} `
|
|
506
|
-
+ `(${scopeLabel}): ${set.discovery.routedFiles.length} fájl, ${set.discovery.tables.length} tár`);
|
|
507
638
|
}
|
|
508
639
|
/**
|
|
509
640
|
* A scan-gyökér PROJEKT-IDENTITÁSA (git + `package.json`) → (a) kereshető `knowledge` „project-identity"
|
|
@@ -558,6 +689,15 @@ class FAM_Ingest_ControlService {
|
|
|
558
689
|
// (b) reference-alias (a package-név + rövid alakok → a projekt scope-ja) — string/exact-match feloldás.
|
|
559
690
|
if (leaf) {
|
|
560
691
|
await this.persistProjectReference(identity, leaf, set.discovery.resolvedRoot, set.issuer);
|
|
692
|
+
// (c) kanonikus repo-kulcs stamp a leaf project-scope-ra (scope-prevenció, user-FR 2026-06-22) — a STABIL
|
|
693
|
+
// git/package identitás scope-szintű rögzítése; erre épül a repo-kulcs alapú duplikátum-detekció, ami
|
|
694
|
+
// a KÜLÖNBÖZŐ nevű, AZONOS repo eseteket (import vs scan) is elkapja. Best-effort (nincs kulcs → no-op).
|
|
695
|
+
// path-aware kulcs (2026-06-24 fix): a projekt-mappa neve diszkriminátorként → a monorepo/fork
|
|
696
|
+
// sub-projektek külön repoKey-t kapnak (nem keverednek egy scope-ba egy téves merge-dzsel).
|
|
697
|
+
const repoKey = fam_project_identity_util_1.FAM_ProjectIdentity_Util.canonicalRepoKey(identity, path.basename(set.discovery.resolvedRoot));
|
|
698
|
+
if (repoKey) {
|
|
699
|
+
await new scope_reference_2.FAM_Scope_DataService({ issuer: set.issuer }).setCanonicalRepoKey(leaf.scopeId, repoKey);
|
|
700
|
+
}
|
|
561
701
|
}
|
|
562
702
|
fsm_dynamo_1.DyFM_Log.info(`[famIngest] project-identity (${scopeLabel}): `
|
|
563
703
|
+ `${identity.packageName ?? '(nincs package.json)'}${identity.isGit ? ' [git]' : ''}`
|
|
@@ -693,7 +833,7 @@ class FAM_Ingest_ControlService {
|
|
|
693
833
|
ingestRunId: set.runId, trigger: set.request.operation, table: [],
|
|
694
834
|
scopePath: set.scopePath, rootPath: set.request.path,
|
|
695
835
|
filesProcessed: 0, filesSkipped: 0, chunkCount: 0,
|
|
696
|
-
verdicts: { new: 0, modified: 0, equal: 0, deleted: 0 },
|
|
836
|
+
verdicts: { new: 0, modified: 0, equal: 0, deleted: 0, superseded: 0 },
|
|
697
837
|
durationMs: Date.now() - set.startedAt, status: 'failed', errors: [errorEntry],
|
|
698
838
|
};
|
|
699
839
|
try {
|