@futdevpro/fdp-agent-memory 1.1.30 → 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 +1 -1
- 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 +148 -6
- 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/_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/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 +31 -5
- package/build/src/_modules/embedding/_services/fam-embedding.control-service.js +142 -9
- package/build/src/_modules/embedding/_services/fam-entry.data-service.js +9 -0
- 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 +173 -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/_services/fam-capability-registry.service.js +151 -8
- package/build/src/_modules/mcp/_services/fam-read-tool.service.js +70 -0
- package/build/src/_modules/mcp/_services/fam-write-tool.service.js +36 -0
- 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 +76 -0
- package/build/src/_modules/scope-reference/_services/fam-scope-maintenance.control-service.js +233 -0
- package/build/src/_modules/scope-reference/_services/fam-scope.data-service.js +25 -0
- package/build/src/_routes/server/api/api.controller.js +270 -0
- package/build/src/_routes/server/api/fam-doctor.control-service.js +156 -0
- package/build/src/app.server.js +13 -10
- 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 +1 -1
- package/build/src/_cli/_collections/fam-project-discovery.util.js +0 -98
- 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 {
|
|
@@ -388,7 +436,7 @@ class FAM_Ingest_ControlService {
|
|
|
388
436
|
await this.persistAndEmbed({
|
|
389
437
|
chunk: item.chunk, existingId: item.existingId, contentHash: item.contentHash,
|
|
390
438
|
file: set.file, table: set.table, runId: set.runId, scopePath: set.scopePath,
|
|
391
|
-
root: set.root, dataService: set.dataService,
|
|
439
|
+
root: set.root, dataService: set.dataService, directives: set.directives,
|
|
392
440
|
});
|
|
393
441
|
}
|
|
394
442
|
}
|
|
@@ -402,12 +450,21 @@ class FAM_Ingest_ControlService {
|
|
|
402
450
|
}
|
|
403
451
|
continue;
|
|
404
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
|
+
}
|
|
405
462
|
// new / modified → upsert + embed (a `chunk` itt sosem null).
|
|
406
463
|
if (item.chunk) {
|
|
407
464
|
await this.persistAndEmbed({
|
|
408
465
|
chunk: item.chunk, existingId: item.existingId, contentHash: item.contentHash,
|
|
409
466
|
file: set.file, table: set.table, runId: set.runId, scopePath: set.scopePath,
|
|
410
|
-
root: set.root, dataService: set.dataService,
|
|
467
|
+
root: set.root, dataService: set.dataService, directives: set.directives,
|
|
411
468
|
});
|
|
412
469
|
}
|
|
413
470
|
}
|
|
@@ -441,6 +498,9 @@ class FAM_Ingest_ControlService {
|
|
|
441
498
|
addedBy: 'scan',
|
|
442
499
|
ingestRunId: set.runId,
|
|
443
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);
|
|
444
504
|
// Persist (saveData = upsert: meglévő `_id` → modify, hiányzó → insert).
|
|
445
505
|
const saved = await set.dataService.saveData(entry);
|
|
446
506
|
// Embed CSAK a new/modified-re (MP-2). Az embed-hiba az entry-t `error`-státuszba teszi +
|
|
@@ -449,6 +509,51 @@ class FAM_Ingest_ControlService {
|
|
|
449
509
|
table: set.table, entry: saved, callType: 'embed-write',
|
|
450
510
|
});
|
|
451
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
|
+
}
|
|
452
557
|
/**
|
|
453
558
|
* A file-rendszer-összefoglaló (`knowledge`, kind `fs-summary`) generálása + idempotens upsert-je a scan után.
|
|
454
559
|
* A `FAM_ScanSummary_Util` a mappastruktúrából építi a kereshető „mi van a projektben" szöveget. **Identitás:**
|
|
@@ -463,15 +568,16 @@ class FAM_Ingest_ControlService {
|
|
|
463
568
|
}
|
|
464
569
|
const dataService = new embedding_1.FAM_Entry_DataService({ dataParams: registryEntry.dataParams, issuer: set.issuer });
|
|
465
570
|
const scopeLabel = set.scopePath.map((ref) => ref.canonicalName).join(' > ') || '(globális)';
|
|
466
|
-
|
|
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({
|
|
467
574
|
scopeLabel: scopeLabel,
|
|
468
575
|
root: set.discovery.resolvedRoot,
|
|
469
576
|
relativePaths: set.discovery.routedFiles.map((file) => file.relativePath),
|
|
470
577
|
skippedCount: set.discovery.skippedFiles.length,
|
|
471
578
|
tables: set.discovery.tables.map((table) => String(table)),
|
|
472
579
|
});
|
|
473
|
-
|
|
474
|
-
// 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.
|
|
475
581
|
const leafScopeId = set.scopePath.length ? set.scopePath[set.scopePath.length - 1].scopeId : undefined;
|
|
476
582
|
const filter = {};
|
|
477
583
|
filter['source.type'] = fam_scan_summary_util_1.FAM_ScanSummary_Util.SOURCE_TYPE;
|
|
@@ -481,33 +587,54 @@ class FAM_Ingest_ControlService {
|
|
|
481
587
|
filter['scopePath.scopeId'] = leafScopeId;
|
|
482
588
|
}
|
|
483
589
|
const existing = await dataService.findHydratableList(filter);
|
|
484
|
-
const
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
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`);
|
|
488
637
|
}
|
|
489
|
-
const entry = new fam_entry_data_model_1.FAM_Entry({
|
|
490
|
-
_id: existingEntry?._id,
|
|
491
|
-
table: fam_table_type_enum_1.FAM_Table.knowledge,
|
|
492
|
-
content: content,
|
|
493
|
-
contentHash: contentHash,
|
|
494
|
-
embeddingStatus: 'pending',
|
|
495
|
-
scopePath: set.scopePath,
|
|
496
|
-
kind: fam_scan_summary_util_1.FAM_ScanSummary_Util.KIND,
|
|
497
|
-
tags: ['fs-summary', 'filesystem', 'scan-summary'],
|
|
498
|
-
weight: 2,
|
|
499
|
-
source: {
|
|
500
|
-
type: 'scan-summary', root: set.discovery.resolvedRoot, absolutePath: set.discovery.resolvedRoot,
|
|
501
|
-
},
|
|
502
|
-
addedBy: 'scan',
|
|
503
|
-
ingestRunId: set.runId,
|
|
504
|
-
});
|
|
505
|
-
const saved = await dataService.saveData(entry);
|
|
506
|
-
await embedding_1.FAM_EmbeddingPipeline_ControlService.getInstance().embedAndPersist({
|
|
507
|
-
table: fam_table_type_enum_1.FAM_Table.knowledge, entry: saved, callType: 'embed-write',
|
|
508
|
-
});
|
|
509
|
-
fsm_dynamo_1.DyFM_Log.info(`[famIngest] fs-summary ${existingEntry ? 'frissítve' : 'létrehozva'} `
|
|
510
|
-
+ `(${scopeLabel}): ${set.discovery.routedFiles.length} fájl, ${set.discovery.tables.length} tár`);
|
|
511
638
|
}
|
|
512
639
|
/**
|
|
513
640
|
* A scan-gyökér PROJEKT-IDENTITÁSA (git + `package.json`) → (a) kereshető `knowledge` „project-identity"
|
|
@@ -562,6 +689,15 @@ class FAM_Ingest_ControlService {
|
|
|
562
689
|
// (b) reference-alias (a package-név + rövid alakok → a projekt scope-ja) — string/exact-match feloldás.
|
|
563
690
|
if (leaf) {
|
|
564
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
|
+
}
|
|
565
701
|
}
|
|
566
702
|
fsm_dynamo_1.DyFM_Log.info(`[famIngest] project-identity (${scopeLabel}): `
|
|
567
703
|
+ `${identity.packageName ?? '(nincs package.json)'}${identity.isGit ? ' [git]' : ''}`
|
|
@@ -697,7 +833,7 @@ class FAM_Ingest_ControlService {
|
|
|
697
833
|
ingestRunId: set.runId, trigger: set.request.operation, table: [],
|
|
698
834
|
scopePath: set.scopePath, rootPath: set.request.path,
|
|
699
835
|
filesProcessed: 0, filesSkipped: 0, chunkCount: 0,
|
|
700
|
-
verdicts: { new: 0, modified: 0, equal: 0, deleted: 0 },
|
|
836
|
+
verdicts: { new: 0, modified: 0, equal: 0, deleted: 0, superseded: 0 },
|
|
701
837
|
durationMs: Date.now() - set.startedAt, status: 'failed', errors: [errorEntry],
|
|
702
838
|
};
|
|
703
839
|
try {
|