@futdevpro/fdp-agent-memory 1.1.115 → 1.1.119
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/_collections/config-catalog.const.js +4 -0
- package/build/src/_models/data-models/fam-scan-job.data-model.js +7 -0
- package/build/src/_modules/embedding/_services/fam-embedding-bootstrap.control-service.js +14 -9
- package/build/src/_modules/ingest/_services/fam-scan-job.control-service.js +45 -6
- package/build/src/_modules/ingest/_services/fam-scan-job.data-service.js +18 -7
- package/build/src/_modules/ingest/_services/fam-scan-scheduler.control-service.js +49 -7
- package/package.json +1 -1
package/build/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@futdevpro/fdp-agent-memory",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.119",
|
|
4
4
|
"description": "Local-first, vector-backed multi-table agent memory exposed as an MCP server (read/write/capabilities). Public, FDP-Templates-free, no auth.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|
|
@@ -55,6 +55,10 @@ exports.SCAN_IGNORE_PATTERNS_DEFAULT = [
|
|
|
55
55
|
// Cookies / Web Data = SZEMÉLYES adat), olvashatatlanok + junk. (Lelet 2026-06-22: fdp-assistant/__agent/
|
|
56
56
|
// .local-state = 403 MB / 2452 fájl → 2208 `FAM-SCAN-READ-001`. Az `__agent` MAGA MARAD — csak a state junk.)
|
|
57
57
|
'**/.local-state/**', '**/browser-profiles/**', '**/Cache_Data/**',
|
|
58
|
+
// CSOMAGKEZELŐ content-addressed STORE/cache (2026-07-26 lelet): a repo-lokális `.pnpm-store` a `node_modules`-tól
|
|
59
|
+
// KÜLÖNÁLLÓ mappa, ezért a `**/node_modules/**` NEM fedte → egyetlen NPM-csomagban 18 881 junk-fájl került a
|
|
60
|
+
// felderítésbe (fdp-e2e-helpers: 22 618 „forrás"-fájlból 18 881 a store). Sosem forrás — hash-elt tarball-tartalom.
|
|
61
|
+
'**/.pnpm-store/**', '**/.yarn/cache/**', '**/.yarn/unplugged/**', '**/.pnp.cjs', '**/.pnp.js',
|
|
58
62
|
// A `.fdpfamignore` SAJÁT MAGÁT kizárja: ignore-spec-ként OLVASSUK (FAM_FamIgnore_Util.loadFromDir,
|
|
59
63
|
// a walk/routing megkerülésével), de SOHA nem ingesteljük content-ként (a router amúgy is skip-elné).
|
|
60
64
|
'**/.fdpfamignore',
|
|
@@ -39,6 +39,12 @@ class FAM_ScanJob_DataModel extends fsm_dynamo_1.DyFM_Metadata {
|
|
|
39
39
|
resumeCount;
|
|
40
40
|
/** Terminál ok-szöveg (pl. „resume-cap túllépve") — `failed`/`cancelled` job-on, a diagnózishoz. */
|
|
41
41
|
failureReason;
|
|
42
|
+
/**
|
|
43
|
+
* KI indította a job-ot (`FAM_AgentOrigin`: agent/sessionId/workspace/label) — **DURABLE** (2026-07-26 lelet: eddig
|
|
44
|
+
* csak in-memory volt, ezért restart + auto-resume után `origin: undefined` lett → a logból/`listJobs`-ból NEM
|
|
45
|
+
* derült ki, hogy a HETI AUTO re-scan (`agent:'fam-scheduler'`) futott-e, vagy egy manuális/CLI scan).
|
|
46
|
+
*/
|
|
47
|
+
origin;
|
|
42
48
|
constructor(set) {
|
|
43
49
|
super(set);
|
|
44
50
|
if (set) {
|
|
@@ -69,5 +75,6 @@ exports.famScanJob_dataParams = new fsm_dynamo_1.DyFM_DataModel_Params({
|
|
|
69
75
|
sourceLocation: { type: 'string' },
|
|
70
76
|
resumeCount: { type: 'number' },
|
|
71
77
|
failureReason: { type: 'string' },
|
|
78
|
+
origin: { type: 'object' },
|
|
72
79
|
},
|
|
73
80
|
});
|
|
@@ -46,6 +46,16 @@ class FAM_EmbeddingBootstrap_ControlService {
|
|
|
46
46
|
catch (error) {
|
|
47
47
|
fsm_dynamo_1.DyFM_Log.warn(`[FAM bootstrap] ENV-config seed sikertelen: ${error?.message}`);
|
|
48
48
|
}
|
|
49
|
+
// HETI AUTO RE-SCAN + DELETE-SYNC (dispatch „Problem 2", 2026-07-26): az in-process ütemező telepítése MÁR ITT,
|
|
50
|
+
// a lassú boot-karbantartás (hidratálás/scope/resume/dormancy) ELŐTT — hogy a heti-slot timer BIZTOSAN felkerüljön
|
|
51
|
+
// az event-loopra, függetlenül attól, meddig tart egy nagy korpusz hidratálása (a `setInterval` az `await`-ok alatt
|
|
52
|
+
// is tüzel; a lentebbi lépések egyike sem gátolhatja a telepítést). A tick maga vár a hidratálás végéig + best-effort.
|
|
53
|
+
try {
|
|
54
|
+
fam_scan_scheduler_control_service_1.FAM_ScanScheduler_ControlService.getInstance().install();
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
fsm_dynamo_1.DyFM_Log.warn(`[FAM bootstrap] heti re-scan scheduler telepítése sikertelen: ${error?.message}`);
|
|
58
|
+
}
|
|
49
59
|
// A readiness-coordinator hidratálás-bevárás time-outja a `read.hydrationWaitMs` config-ból (a hidratálás
|
|
50
60
|
// ELŐTT, hogy a `beginBoot` után érkező read-ek már a helyes időkorláttal várjanak). Best-effort (default 180s).
|
|
51
61
|
try {
|
|
@@ -63,6 +73,10 @@ class FAM_EmbeddingBootstrap_ControlService {
|
|
|
63
73
|
catch (error) {
|
|
64
74
|
fsm_dynamo_1.DyFM_Log.warn(`[FAM bootstrap] vektor-pool hidratálás sikertelen: ${error?.message}`);
|
|
65
75
|
}
|
|
76
|
+
// A hidratálás UTÁN AZONNAL egy scheduler-tick (fire-and-forget): a heti catch-up így PROMPT lefut a hidratálás
|
|
77
|
+
// végén (nem kell a 10-perces intervallumra várni), és FÜGGETLEN a lentebbi (potenciálisan lassú) boot-karbantartástól.
|
|
78
|
+
// A tick maga best-effort + a hidratálás-kaput már átlépte (complete), így itt biztonságosan indíthat re-scant.
|
|
79
|
+
void fam_scan_scheduler_control_service_1.FAM_ScanScheduler_ControlService.getInstance().tick(Date.now());
|
|
66
80
|
// Scope-integritás self-check (user-FR 2026-06-22): a rendszer MAGÁTÓL ismerje fel az árva/duplikált
|
|
67
81
|
// scope-okat (boot után, best-effort — a takarítás `merge_scopes`, nem itt). Csak figyelmeztet, nem ír.
|
|
68
82
|
try {
|
|
@@ -98,15 +112,6 @@ class FAM_EmbeddingBootstrap_ControlService {
|
|
|
98
112
|
catch (error) {
|
|
99
113
|
fsm_dynamo_1.DyFM_Log.warn(`[FAM bootstrap] memory-dormancy sweep sikertelen: ${error?.message}`);
|
|
100
114
|
}
|
|
101
|
-
// HETI AUTO RE-SCAN + DELETE-SYNC (dispatch „Problem 2", 2026-07-26): az in-process ütemező telepítése — a heti
|
|
102
|
-
// slotot (default hétfő 07:00) figyeli + boot-catch-up-ol. A reconcile-logika már létezik; ez csak az ütemezés.
|
|
103
|
-
// Best-effort (a telepítés hibája nem buktatja a bootot; a scheduler maga is best-effort végig).
|
|
104
|
-
try {
|
|
105
|
-
fam_scan_scheduler_control_service_1.FAM_ScanScheduler_ControlService.getInstance().install();
|
|
106
|
-
}
|
|
107
|
-
catch (error) {
|
|
108
|
-
fsm_dynamo_1.DyFM_Log.warn(`[FAM bootstrap] heti re-scan scheduler telepítése sikertelen: ${error?.message}`);
|
|
109
|
-
}
|
|
110
115
|
}
|
|
111
116
|
}
|
|
112
117
|
exports.FAM_EmbeddingBootstrap_ControlService = FAM_EmbeddingBootstrap_ControlService;
|
|
@@ -98,8 +98,13 @@ class FAM_ScanJob_ControlService {
|
|
|
98
98
|
void this.persistUpdate(job);
|
|
99
99
|
fsm_dynamo_1.DyFM_Log.warn(`[scanJob ${job.jobId}] ${job.failureReason}`);
|
|
100
100
|
});
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
// INDÍTÁS-LOG (2026-07-26 bővítés — „derüljön ki a logból, KI indította"): az origin (agent/label/session) + a
|
|
102
|
+
// TELJES cél-lista a log-bufferbe (`.log`, nem `.info` — csak az előbbi pufferelődik a `/api/logs/get`-be).
|
|
103
|
+
fsm_dynamo_1.DyFM_Log.log(`[scanJob ${job.jobId}] INDÍTVA — indító: ${FAM_ScanJob_ControlService.describeOrigin(job.origin)} `
|
|
104
|
+
+ `| ${specs.length} cél | cooldown ${cooldownMs}ms (ha ≥${cooldownMinChunks} embed chunk)`);
|
|
105
|
+
specs.forEach((spec, index) => {
|
|
106
|
+
fsm_dynamo_1.DyFM_Log.log(`[scanJob ${job.jobId}] cél ${index + 1}/${specs.length}: ${spec.project} → ${spec.path}`);
|
|
107
|
+
});
|
|
103
108
|
// Fast-path (user-FR 2026-06-22): a `start` legfeljebb `waitMs`-ig (default 15s) megvárja a befejezést — ha
|
|
104
109
|
// a job ennyin belül kész, a TELJES eredményt adjuk vissza AZONNAL (nincs külön poll-hívás); különben a futó
|
|
105
110
|
// job-ot (`jobId` + `running` + ETA) → a hívó poll-oz. `0` = ne várj.
|
|
@@ -120,6 +125,26 @@ class FAM_ScanJob_ControlService {
|
|
|
120
125
|
await fam_cooldown_util_1.FAM_Cooldown_Util.sleep(100);
|
|
121
126
|
}
|
|
122
127
|
}
|
|
128
|
+
/**
|
|
129
|
+
* Az INDÍTÓ ember-olvasható leírása a logokhoz (2026-07-26): `agent[/label][ · session]`. Aláírás nélküli (régi/
|
|
130
|
+
* kézi) job → `ismeretlen (aláíratlan)` — hogy a log EXPLICIT jelezze: nem tudjuk, ki indította (nem „hallgat").
|
|
131
|
+
*/
|
|
132
|
+
static describeOrigin(origin) {
|
|
133
|
+
if (!origin || (!origin.agent && !origin.label && !origin.sessionId)) {
|
|
134
|
+
return 'ismeretlen (aláíratlan)';
|
|
135
|
+
}
|
|
136
|
+
const label = origin.label ? `/${origin.label}` : '';
|
|
137
|
+
const session = origin.sessionId ? ` · session ${String(origin.sessionId).slice(0, 8)}` : '';
|
|
138
|
+
return `${origin.agent ?? 'ismeretlen-agent'}${label}${session}`;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Van-e AKTÍV (running/pending) scan-job? A heti auto-re-scan SINGLE-FLIGHT kapuja (2026-07-26 lelet: restart +
|
|
142
|
+
* auto-resume mellett HÁROM egyidejű 13-célos job futott) — soha ne indítsunk párhuzamos TELJES scant (a
|
|
143
|
+
* hidratált pool + embed erőforrás-kontenció miatt ez a gépet is megfoghatja).
|
|
144
|
+
*/
|
|
145
|
+
hasActiveJob() {
|
|
146
|
+
return Array.from(this.jobs.values()).some((job) => job.status === 'running' || job.status === 'pending');
|
|
147
|
+
}
|
|
123
148
|
/** Egy job állapota (poll). `undefined`, ha ismeretlen (a hívó 404-et ad). Az ETA + stall frissítve. */
|
|
124
149
|
getStatus(jobId) {
|
|
125
150
|
const job = this.jobs.get(jobId);
|
|
@@ -214,7 +239,8 @@ class FAM_ScanJob_ControlService {
|
|
|
214
239
|
job.finishedAt = Date.now();
|
|
215
240
|
job.failureReason = `felhasználói megszakítás a(z) ${index + 1}/${specs.length} projektnél`;
|
|
216
241
|
await this.persistUpdate(job);
|
|
217
|
-
fsm_dynamo_1.DyFM_Log.
|
|
242
|
+
fsm_dynamo_1.DyFM_Log.log(`[scanJob ${job.jobId}] MEGSZAKÍTVA — ${job.failureReason} | indító: `
|
|
243
|
+
+ `${FAM_ScanJob_ControlService.describeOrigin(job.origin)}`);
|
|
218
244
|
return;
|
|
219
245
|
}
|
|
220
246
|
// RESUME (FEAT-008 Phase 4): a már `ok` projekteket ÁTUGORJUK (a megszakítási ponttól folytatunk —
|
|
@@ -227,7 +253,11 @@ class FAM_ScanJob_ControlService {
|
|
|
227
253
|
job.lastProgressAt = Date.now(); // egy projekt feldolgozásának KEZDETE is haladás (a stall-óra resetelődik)
|
|
228
254
|
// Per-projekt PROGRESS a process-logba (user-FR 2026-06-25 — „hanyadik projektből hányadikon járunk"): a
|
|
229
255
|
// detached FAM-ablakot figyelve egyből látszik a pozíció (`[12/48] <projekt>`), nem kell a REST-et poll-ozni.
|
|
230
|
-
|
|
256
|
+
// `.log` (NEM `.info`) — a DyNTS log-buffer (`/api/logs/get`) CSAK a `.log`/`.warn`-t puffereli, ezért a
|
|
257
|
+
// `.info` kontextus-sorok korábban LÁTHATATLANOK voltak a log-lekérdezésben (2026-07-26 lelet: a user a
|
|
258
|
+
// `[famIngest] … feldolgozás` sort látta, de azt NEM, hogy melyik job/indító/cél-index tartozik hozzá).
|
|
259
|
+
fsm_dynamo_1.DyFM_Log.log(`[scanJob ${job.jobId}] [cél ${index + 1}/${specs.length}] ${specs[index].project} `
|
|
260
|
+
+ `(${specs[index].path}) — feldolgozás indul | indító: ${FAM_ScanJob_ControlService.describeOrigin(job.origin)}`);
|
|
231
261
|
const result = await runner(specs[index]);
|
|
232
262
|
job.projects[index] = result;
|
|
233
263
|
job.lastProgressAt = Date.now(); // per-target BEFEJEZÉS → friss haladás-jel a stall-detekciónak
|
|
@@ -252,7 +282,9 @@ class FAM_ScanJob_ControlService {
|
|
|
252
282
|
job.status = 'completed';
|
|
253
283
|
job.finishedAt = Date.now();
|
|
254
284
|
await this.persistUpdate(job);
|
|
255
|
-
fsm_dynamo_1.DyFM_Log.
|
|
285
|
+
fsm_dynamo_1.DyFM_Log.log(`[scanJob ${job.jobId}] KÉSZ: ${job.okCount}/${specs.length} ok | indító: `
|
|
286
|
+
+ `${FAM_ScanJob_ControlService.describeOrigin(job.origin)} | teljes futásidő `
|
|
287
|
+
+ `${Math.round(((job.finishedAt ?? Date.now()) - job.startedAt) / 1000)}s`);
|
|
256
288
|
}
|
|
257
289
|
/** A job durable rekordjának LÉTREHOZÁSA (best-effort — a perzisztencia hibája nem buktatja a scant). */
|
|
258
290
|
async persistCreate(job, specs, sourceLocation) {
|
|
@@ -262,6 +294,9 @@ class FAM_ScanJob_ControlService {
|
|
|
262
294
|
okCount: job.okCount, cooldownMs: job.cooldownMs, cooldownMinChunks: job.cooldownMinChunks,
|
|
263
295
|
startedAt: job.startedAt, projects: job.projects, specs: specs, sourceLocation: sourceLocation,
|
|
264
296
|
resumeCount: job.resumeCount ?? 0,
|
|
297
|
+
// KI indította — DURABLE-be is (2026-07-26 lelet): eddig csak in-memory volt, ezért a `listJobs`/log a
|
|
298
|
+
// restart+auto-resume után `origin: undefined`-ot mutatott → NEM derült ki, hogy a heti AUTO-scan futott-e.
|
|
299
|
+
origin: job.origin,
|
|
265
300
|
}));
|
|
266
301
|
}
|
|
267
302
|
catch (error) {
|
|
@@ -316,6 +351,9 @@ class FAM_ScanJob_ControlService {
|
|
|
316
351
|
resumeCount: priorResumes + 1,
|
|
317
352
|
cancelRequested: false,
|
|
318
353
|
projects: doc.projects ?? doc.specs.map((spec) => ({ project: spec.project, path: spec.path, status: 'pending' })),
|
|
354
|
+
// Az INDÍTÓ visszaállítása a durable rekordból — így a resume-olt job is beazonosítható (heti
|
|
355
|
+
// AUTO-scan vs manuális); enélkül a restart „elmosta" az origint (2026-07-26 lelet).
|
|
356
|
+
origin: doc.origin,
|
|
319
357
|
};
|
|
320
358
|
this.jobs.set(job.jobId, job);
|
|
321
359
|
this.stallThresholdMsByJob.set(job.jobId, stallThresholdMs);
|
|
@@ -323,7 +361,8 @@ class FAM_ScanJob_ControlService {
|
|
|
323
361
|
// számról indul → a cap konvergál; különben egy crash-loop sosem érné el a capet).
|
|
324
362
|
await dataService.updateJobByJobId(job.jobId, { resumeCount: job.resumeCount });
|
|
325
363
|
const doneCount = job.projects.filter((project) => project.status === 'ok').length;
|
|
326
|
-
fsm_dynamo_1.DyFM_Log.
|
|
364
|
+
fsm_dynamo_1.DyFM_Log.log(`[scanJob ${job.jobId}] RESUME (${job.resumeCount}/${maxResumeCount}) — eredeti indító: `
|
|
365
|
+
+ `${FAM_ScanJob_ControlService.describeOrigin(job.origin)} | ${doneCount}/${job.total} kész — folytatás a megszakítási ponttól`);
|
|
327
366
|
void this.process(job, doc.specs).catch((error) => {
|
|
328
367
|
job.status = 'failed';
|
|
329
368
|
job.finishedAt = Date.now();
|
|
@@ -31,20 +31,31 @@ class FAM_ScanJob_DataService extends nts_dynamo_1.DyNTS_DataService {
|
|
|
31
31
|
return this.findDataList({ status: { $in: ['running', 'pending'] } }, true);
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
|
-
* A
|
|
35
|
-
* szerint csökkenő rendben
|
|
36
|
-
*
|
|
34
|
+
* A KORÁBBAN SZKENNELT célok UNIÓJA a durable scan-job előzményből (weekly re-scan auto-derive, 2026-07-26). A
|
|
35
|
+
* `startedAt` szerint csökkenő rendben végigmegy a legutóbbi job-okon, és `path` szerint deduplikálva gyűjti a
|
|
36
|
+
* cél-specjeket — a FRISSEBB job spec-je nyer (az aktuálisabb scope/table beállítással).
|
|
37
|
+
*
|
|
38
|
+
* **Miért unió és nem „a legutóbbi job listája"?** (2026-07-26 lelet) A legutóbbi job gyakran RÉSZLEGES (pl. csak
|
|
39
|
+
* 13 NPM-csomag), így a „heti TELJES re-scan" valójában nem lett volna teljes — az azelőtt szkennelt projektek
|
|
40
|
+
* kimaradtak volna, és a törlés-szinkron sem futott volna rájuk. Az unió a „mindent, amit valaha szkenneltünk"
|
|
41
|
+
* közelítése. (A garantáltan teljes lefedettség továbbra is az explicit `scan.weeklyRescanRoot`.)
|
|
42
|
+
*
|
|
37
43
|
* `undefined`, ha még sosem volt cél-speces scan.
|
|
38
44
|
*/
|
|
39
45
|
async findLatestScannedTargets() {
|
|
40
|
-
const found = await this.searchData({ sortBy: [{ key: 'startedAt', order: -1 }], pageSize:
|
|
46
|
+
const found = await this.searchData({ sortBy: [{ key: 'startedAt', order: -1 }], pageSize: 100 });
|
|
47
|
+
const byPath = new Map();
|
|
41
48
|
for (const job of found.results ?? []) {
|
|
42
49
|
const specs = job.specs;
|
|
43
|
-
|
|
44
|
-
|
|
50
|
+
for (const spec of specs ?? []) {
|
|
51
|
+
const key = String(spec?.path ?? '').trim().toLowerCase();
|
|
52
|
+
// A frissebb job ELŐBB jön (startedAt desc) → az első találat a legfrissebb; a régebbit nem írjuk felül.
|
|
53
|
+
if (key && !byPath.has(key)) {
|
|
54
|
+
byPath.set(key, spec);
|
|
55
|
+
}
|
|
45
56
|
}
|
|
46
57
|
}
|
|
47
|
-
return undefined;
|
|
58
|
+
return byPath.size ? Array.from(byPath.values()) : undefined;
|
|
48
59
|
}
|
|
49
60
|
}
|
|
50
61
|
exports.FAM_ScanJob_DataService = FAM_ScanJob_DataService;
|
|
@@ -6,6 +6,7 @@ const fam_config_level_type_enum_1 = require("../../../_enums/fam-config-level.t
|
|
|
6
6
|
const config_control_service_1 = require("../../../_routes/server/config/config.control-service");
|
|
7
7
|
const fam_scan_job_control_service_1 = require("./fam-scan-job.control-service");
|
|
8
8
|
const fam_scan_job_data_service_1 = require("./fam-scan-job.data-service");
|
|
9
|
+
const embedding_1 = require("../../embedding");
|
|
9
10
|
/**
|
|
10
11
|
* `FAM_ScanScheduler_ControlService` (2026-07-26, dispatch „Problem 2") — a HETI automatikus TELJES FAM re-scan +
|
|
11
12
|
* törlés-szinkron (reconcile/delta) IN-PROCESS ütemezője. A reconcile-logika MÁR LÉTEZIK (a scan orphan-soft-delete-eli
|
|
@@ -13,6 +14,10 @@ const fam_scan_job_data_service_1 = require("./fam-scan-job.data-service");
|
|
|
13
14
|
* FAM-tartalom naprakész maradjon anélkül, hogy manuálisan re-scannelni kellene.
|
|
14
15
|
*
|
|
15
16
|
* **Slot:** LOKÁLIS idő `scan.weeklyRescanDayOfWeek` (default 1 = hétfő) @ `scan.weeklyRescanHour`:00 (default 7 → 07:00).
|
|
17
|
+
* **Telepítés (KRITIKUS):** a `boot()` a scheduler-t a LEHETŐ LEGKORÁBBAN (a lassú boot-karbantartás — hidratálás/scope-
|
|
18
|
+
* integritás/resume/dormancy — ELŐTT) telepíti, hogy a timer BIZTOSAN felkerüljön az event-loopra, függetlenül attól,
|
|
19
|
+
* hogy egy nagy korpusz hidratálása/karbantartása meddig tart (a `setInterval` az `await`-ok alatt is tüzel). A tick
|
|
20
|
+
* viszont a **hidratálás befejeztéig NEM indít re-scant** (erőforrás-kontenció ellen) — a következő tick bepótol.
|
|
16
21
|
* **Catch-up:** egy fix időközű tick (10 perc) figyeli, hogy a LEGUTÓBBI elmúlt slot óta futott-e már (`scan.weeklyRescanLastRunAt`).
|
|
17
22
|
* Ha a szerver a slot-időben nem futott (down/restart), a következő indításkor BEPÓTOL — a heti freshness így restart-tűrő.
|
|
18
23
|
* **Restart-storm ellen:** a `lastRunAt` a config-store-ban perzisztál → egy héten belüli restart NEM indít újabb re-scant.
|
|
@@ -26,6 +31,8 @@ class FAM_ScanScheduler_ControlService {
|
|
|
26
31
|
ticking = false;
|
|
27
32
|
/** A slot-ellenőrzés fix pollozási időköze (ms) — 10 perc. NEM a heti slot; csak „megnéztük-e már, hogy due-e". */
|
|
28
33
|
static CHECK_INTERVAL_MS = 600000;
|
|
34
|
+
/** Nap-nevek a logokhoz (a `getDay()` 0=vasárnap indexelése szerint) — hogy a log ember-olvasható legyen. */
|
|
35
|
+
static DAY_NAMES = ['vasárnap', 'hétfő', 'kedd', 'szerda', 'csütörtök', 'péntek', 'szombat'];
|
|
29
36
|
static getInstance() {
|
|
30
37
|
if (!FAM_ScanScheduler_ControlService._instance) {
|
|
31
38
|
FAM_ScanScheduler_ControlService._instance = new FAM_ScanScheduler_ControlService();
|
|
@@ -50,7 +57,7 @@ class FAM_ScanScheduler_ControlService {
|
|
|
50
57
|
if (typeof this.timer.unref === 'function') {
|
|
51
58
|
this.timer.unref();
|
|
52
59
|
}
|
|
53
|
-
fsm_dynamo_1.DyFM_Log.
|
|
60
|
+
fsm_dynamo_1.DyFM_Log.log(`[FAM scanScheduler] heti re-scan figyelő telepítve (tick ${FAM_ScanScheduler_ControlService.CHECK_INTERVAL_MS / 60000} percenként).`);
|
|
54
61
|
void this.tick(Date.now());
|
|
55
62
|
}
|
|
56
63
|
/** A timer leállítása (teszt / graceful). */
|
|
@@ -74,17 +81,43 @@ class FAM_ScanScheduler_ControlService {
|
|
|
74
81
|
const config = config_control_service_1.FAM_Config_ControlService.getInstance();
|
|
75
82
|
const enabled = (await config.resolve('scan.weeklyRescanEnabled')).value;
|
|
76
83
|
if (enabled === false) {
|
|
84
|
+
fsm_dynamo_1.DyFM_Log.log('[FAM scanScheduler] heti re-scan KIKAPCSOLVA (`scan.weeklyRescanEnabled=false`) — kihagyva.');
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
// HIDRATÁLÁS-KAPU: a scheduler-t a boot ELEJÉN telepítjük (nem a lassú boot-karbantartás mögött), ezért a
|
|
88
|
+
// tick a hidratálás ALATT is tüzelhet — de ILYENKOR NE indítsunk TELJES re-scant (erőforrás-kontenció a
|
|
89
|
+
// 100k+ vektor hidratálásával). Nem rögzítünk lastRun-t → a következő tick (a hidratálás után) BEPÓTOL.
|
|
90
|
+
const hydration = embedding_1.FAM_VectorSearch_ControlService.getInstance().hydrationSummary();
|
|
91
|
+
if (!hydration.complete) {
|
|
92
|
+
fsm_dynamo_1.DyFM_Log.log(`[FAM scanScheduler] hidratálás MÉG FOLYAMATBAN (${hydration.hydratedTables}/${hydration.totalTables} tár, `
|
|
93
|
+
+ `vár: ${hydration.pendingTables.join(', ')}) — a heti re-scan most kihagyva, a következő tick bepótol.`);
|
|
77
94
|
return;
|
|
78
95
|
}
|
|
79
96
|
const day = await this.resolveNum(config, 'scan.weeklyRescanDayOfWeek', 1);
|
|
80
97
|
const hour = await this.resolveNum(config, 'scan.weeklyRescanHour', 7);
|
|
81
98
|
const lastRun = await this.resolveNum(config, 'scan.weeklyRescanLastRunAt', 0);
|
|
82
99
|
const slot = FAM_ScanScheduler_ControlService.mostRecentSlotAtOrBefore(nowMs, day, hour);
|
|
100
|
+
const iso = (ms) => (ms > 0 ? new Date(ms).toISOString() : 'SOHA');
|
|
83
101
|
if (lastRun >= slot) {
|
|
84
|
-
|
|
102
|
+
// NEM esedékes — a döntést + a MIÉRT-et is logoljuk (a user kérése: derüljön ki a logból; a következő
|
|
103
|
+
// slot időpontjával, hogy látható legyen, mikor futna legközelebb).
|
|
104
|
+
fsm_dynamo_1.DyFM_Log.log(`[FAM scanScheduler] heti re-scan NEM esedékes — utolsó futás ${iso(lastRun)} ≥ aktuális heti `
|
|
105
|
+
+ `slot ${iso(slot)} (slot: ${FAM_ScanScheduler_ControlService.DAY_NAMES[day] ?? day} ${hour}:00). `
|
|
106
|
+
+ `Következő slot: ${iso(slot + 7 * 24 * 3600000)}.`);
|
|
107
|
+
return;
|
|
85
108
|
}
|
|
86
|
-
//
|
|
87
|
-
//
|
|
109
|
+
// SINGLE-FLIGHT (2026-07-26 lelet — „max heti 1x, ÚJRAINDULÁSTÓL FÜGGETLENÜL"): ha MÁR fut egy scan-job
|
|
110
|
+
// (pl. a boot auto-resume folytatta a megszakadt heti scant), NE indítsunk MÉG EGYET. Nem rögzítünk
|
|
111
|
+
// lastRun-t → ha a futó job nem a heti volt, a következő tick újra megvizsgálja.
|
|
112
|
+
if (fam_scan_job_control_service_1.FAM_ScanJob_ControlService.getInstance().hasActiveJob()) {
|
|
113
|
+
fsm_dynamo_1.DyFM_Log.log('[FAM scanScheduler] heti re-scan esedékes lenne, DE MÁR FUT egy scan-job '
|
|
114
|
+
+ '(single-flight kapu) — most kihagyva, a következő tick újrapróbálja.');
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
// DUE: ELŐBB rögzítjük a lastRun-t (így egy crash/restart mid-scan sem indít újra minden bootkor — ez a
|
|
118
|
+
// „max heti 1x, újraindulástól függetlenül" garanciájának a MAGJA), MAJD indítjuk a scant.
|
|
119
|
+
fsm_dynamo_1.DyFM_Log.log(`[FAM scanScheduler] heti re-scan ESEDÉKES — utolsó futás ${iso(lastRun)} < heti slot ${iso(slot)} `
|
|
120
|
+
+ `(slot: ${FAM_ScanScheduler_ControlService.DAY_NAMES[day] ?? day} ${hour}:00). Indítás…`);
|
|
88
121
|
await this.markRan(config, nowMs);
|
|
89
122
|
await this.triggerRescan();
|
|
90
123
|
}
|
|
@@ -102,15 +135,24 @@ class FAM_ScanScheduler_ControlService {
|
|
|
102
135
|
const rootValue = (await config.resolve('scan.weeklyRescanRoot')).value;
|
|
103
136
|
const root = typeof rootValue === 'string' ? rootValue.trim() : '';
|
|
104
137
|
if (root) {
|
|
138
|
+
fsm_dynamo_1.DyFM_Log.log(`[FAM scanScheduler] cél-forrás: EXPLICIT \`scan.weeklyRescanRoot\` = ${root} `
|
|
139
|
+
+ '(a root alatt felderített ÖSSZES projekt — teljes lefedettség).');
|
|
105
140
|
await fam_scan_job_control_service_1.FAM_ScanJob_ControlService.getInstance().start({ root: root, sourceLocation: root, origin: origin, waitMs: 0 });
|
|
106
|
-
fsm_dynamo_1.DyFM_Log.
|
|
141
|
+
fsm_dynamo_1.DyFM_Log.log(`[FAM scanScheduler] heti TELJES re-scan + reconcile INDÍTVA (root: ${root}).`);
|
|
107
142
|
return;
|
|
108
143
|
}
|
|
109
|
-
// Zero-config: a
|
|
144
|
+
// Zero-config: a KORÁBBAN SZKENNELT célok UNIÓJA (nem csak a legutóbbi job listája — 2026-07-26 lelet: a
|
|
145
|
+
// „legutóbbi job" egy RÉSZLEGES lista volt (13 NPM-csomag), így a „TELJES re-scan" nem volt teljes).
|
|
110
146
|
const targets = await this.loadLatestScannedTargets();
|
|
111
147
|
if (targets && targets.length) {
|
|
148
|
+
fsm_dynamo_1.DyFM_Log.log(`[FAM scanScheduler] cél-forrás: ZERO-CONFIG — a korábban szkennelt célok uniója a durable `
|
|
149
|
+
+ `scan-job előzményből (${targets.length} cél). FIGYELEM: ez CSAK azt fedi, amit már szkenneltünk; a `
|
|
150
|
+
+ 'garantáltan TELJES lefedettséghez állítsd be a `scan.weeklyRescanRoot`-ot a workspace-gyökérre.');
|
|
151
|
+
targets.forEach((spec, index) => {
|
|
152
|
+
fsm_dynamo_1.DyFM_Log.log(`[FAM scanScheduler] cél ${index + 1}/${targets.length}: ${spec.project} → ${spec.path}`);
|
|
153
|
+
});
|
|
112
154
|
await fam_scan_job_control_service_1.FAM_ScanJob_ControlService.getInstance().start({ projects: targets, origin: origin, waitMs: 0 });
|
|
113
|
-
fsm_dynamo_1.DyFM_Log.
|
|
155
|
+
fsm_dynamo_1.DyFM_Log.log(`[FAM scanScheduler] heti TELJES re-scan + reconcile INDÍTVA (${targets.length} cél, zero-config unió).`);
|
|
114
156
|
return;
|
|
115
157
|
}
|
|
116
158
|
fsm_dynamo_1.DyFM_Log.warn('[FAM scanScheduler] heti re-scan esedékes, DE nincs cél: állítsd be a `scan.weeklyRescanRoot`-ot '
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@futdevpro/fdp-agent-memory",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.119",
|
|
4
4
|
"description": "Local-first, vector-backed multi-table agent memory exposed as an MCP server (read/write/capabilities). Public, FDP-Templates-free, no auth.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|