@futdevpro/fdp-agent-memory 0.1.0 → 1.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +7 -7
  3. package/build/package.json +6 -5
  4. package/build/src/_cli/_collections/fam-arg.util.js +48 -0
  5. package/build/src/_cli/_collections/fam-cli.const.js +40 -0
  6. package/build/src/_cli/_collections/fam-output.util.js +86 -0
  7. package/build/src/_cli/_collections/fam-project-discovery.util.js +98 -0
  8. package/build/src/_cli/_commands/capture.command.js +73 -0
  9. package/build/src/_cli/_commands/config.command.js +93 -0
  10. package/build/src/_cli/_commands/doctor.command.js +124 -0
  11. package/build/src/_cli/_commands/errors.command.js +66 -0
  12. package/build/src/_cli/_commands/export.command.js +65 -0
  13. package/build/src/_cli/_commands/find-duplicates.command.js +97 -0
  14. package/build/src/_cli/_commands/import.command.js +136 -0
  15. package/build/src/_cli/_commands/init.command.js +147 -0
  16. package/build/src/_cli/_commands/read.command.js +109 -0
  17. package/build/src/_cli/_commands/scan-projects.command.js +138 -0
  18. package/build/src/_cli/_commands/scan.command.js +98 -0
  19. package/build/src/_cli/_commands/seed.command.js +40 -0
  20. package/build/src/_cli/_commands/serve.command.js +373 -0
  21. package/build/src/_cli/_commands/start.command.js +134 -0
  22. package/build/src/_cli/_commands/stats.command.js +54 -0
  23. package/build/src/_cli/_commands/write.command.js +103 -0
  24. package/build/src/_cli/_models/interfaces/fam-cli-global-options.interface.js +2 -0
  25. package/build/src/_cli/_models/interfaces/fam-cli-output.interface.js +9 -0
  26. package/build/src/_cli/_models/interfaces/fam-client-result.interface.js +2 -0
  27. package/build/src/_cli/_services/fam-client.service.js +140 -0
  28. package/build/src/_cli/register-commands.js +86 -0
  29. package/build/src/_collections/config-catalog.const.js +67 -1
  30. package/build/src/_collections/fam-console.util.js +367 -0
  31. package/build/src/_collections/fam-entry-bootstrap.util.js +158 -4
  32. package/build/src/_collections/fam-error-factory.util.js +0 -9
  33. package/build/src/_collections/fam-mcp-bridge.util.js +49 -0
  34. package/build/src/_collections/fam-reference-code.util.js +105 -0
  35. package/build/src/_collections/fam-version.const.js +10 -0
  36. package/build/src/_models/data-models/fam-entry-base-properties.const.js +1 -0
  37. package/build/src/_models/data-models/fam-entry.data-model.js +6 -0
  38. package/build/src/_models/data-models/fam-ingest-run.data-model.js +3 -1
  39. package/build/src/_models/data-models/fam-reference.data-model.js +7 -0
  40. package/build/src/_modules/capture/_collections/fam-capture.const.js +11 -0
  41. package/build/src/_modules/capture/_services/fam-auto-capture.control-service.js +87 -0
  42. package/build/src/_modules/capture/index.js +8 -0
  43. package/build/src/_modules/embedding/_collections/fam-embedding-prefix.util.js +77 -0
  44. package/build/src/_modules/embedding/_services/fam-duplicate-scan.control-service.js +202 -0
  45. package/build/src/_modules/embedding/_services/fam-embedding-pipeline.control-service.js +33 -9
  46. package/build/src/_modules/embedding/_services/fam-embedding.control-service.js +21 -2
  47. package/build/src/_modules/embedding/_services/fam-entry.data-service.js +135 -0
  48. package/build/src/_modules/embedding/_services/fam-vector-search.control-service.js +42 -32
  49. package/build/src/_modules/embedding/index.js +4 -1
  50. package/build/src/_modules/export/_collections/fam-export.const.js +22 -0
  51. package/build/src/_modules/export/_services/fam-export.control-service.js +64 -0
  52. package/build/src/_modules/export/index.js +8 -0
  53. package/build/src/_modules/ingest/_collections/fam-famignore.util.js +83 -0
  54. package/build/src/_modules/ingest/_collections/fam-file-routing.util.js +59 -48
  55. package/build/src/_modules/ingest/_collections/fam-git-repo.util.js +193 -0
  56. package/build/src/_modules/ingest/_collections/fam-project-identity.util.js +134 -0
  57. package/build/src/_modules/ingest/_collections/fam-scan-progress.util.js +57 -0
  58. package/build/src/_modules/ingest/_collections/fam-scan-summary.util.js +60 -0
  59. package/build/src/_modules/ingest/_collections/fam-scan-weight.util.js +53 -0
  60. package/build/src/_modules/ingest/_collections/fam-secret-exclude.util.js +37 -14
  61. package/build/src/_modules/ingest/_collections/fam-sliding-chunker.util.js +34 -0
  62. package/build/src/_modules/ingest/_collections/fam-ts-chunker.util.js +200 -14
  63. package/build/src/_modules/ingest/_services/fam-delta-compare.util.js +4 -1
  64. package/build/src/_modules/ingest/_services/fam-ingest-run.data-service.js +7 -4
  65. package/build/src/_modules/ingest/_services/fam-ingest.control-service.js +349 -17
  66. package/build/src/_modules/ingest/_services/fam-scan.control-service.js +25 -2
  67. package/build/src/_modules/ingest/index.js +3 -1
  68. package/build/src/_modules/mcp/_collections/fam-active-rules.util.js +56 -0
  69. package/build/src/_modules/mcp/_collections/fam-core-tools.const.js +47 -6
  70. package/build/src/_modules/mcp/_services/fam-capabilities-tool.service.js +4 -4
  71. package/build/src/_modules/mcp/_services/fam-capability-registry.service.js +224 -18
  72. package/build/src/_modules/mcp/_services/fam-mcp-adapter.service.js +4 -4
  73. package/build/src/_modules/mcp/_services/fam-mcp-server.service.js +4 -4
  74. package/build/src/_modules/mcp/_services/fam-read-tool.service.js +53 -1
  75. package/build/src/_modules/mcp/_services/fam-write-tool.service.js +104 -8
  76. package/build/src/_modules/mcp/index.js +4 -4
  77. package/build/src/_modules/migration/_collections/fam-claude-mem-normalize.util.js +66 -3
  78. package/build/src/_modules/migration/_collections/fam-prompt-aggregate.util.js +143 -0
  79. package/build/src/_modules/migration/_collections/fam-target-mapping.util.js +19 -0
  80. package/build/src/_modules/migration/_enums/fam-claude-mem-source.type-enum.js +6 -0
  81. package/build/src/_modules/migration/_models/interfaces/fam-claude-mem.interface.js +5 -0
  82. package/build/src/_modules/migration/_services/fam-agent-memory-reader.service.js +125 -0
  83. package/build/src/_modules/migration/_services/fam-claude-mem-import.control-service.js +101 -18
  84. package/build/src/_modules/migration/_services/fam-import-dedup.data-service.js +53 -0
  85. package/build/src/_modules/migration/index.js +3 -1
  86. package/build/src/_modules/retrieval/_services/fam-retrieval-candidate.data-service.js +78 -4
  87. package/build/src/_modules/retrieval/_services/fam-retrieval.control-service.js +293 -50
  88. package/build/src/_modules/scope-reference/_collections/fam-scope-normalize.util.js +6 -3
  89. package/build/src/_modules/scope-reference/_services/fam-reference.data-service.js +18 -0
  90. package/build/src/_modules/scope-reference/_services/fam-scope-resolver.control-service.js +79 -20
  91. package/build/src/_routes/server/api/api.controller.js +34 -2
  92. package/build/src/_routes/server/client-app/client-app.control-service.js +1 -1
  93. package/build/src/_routes/server/server-status/server-status.controller.js +2 -1
  94. package/build/src/app.server.js +13 -1
  95. package/build/src/environments/environment.js +1 -1
  96. package/build/src/index.js +1 -1
  97. package/client-dist/{chunk-GHKRM4SM.js → chunk-I77GXVAQ.js} +1 -1
  98. package/client-dist/{chunk-LMTL7GA3.js → chunk-YXHWCJ5O.js} +1 -1
  99. package/client-dist/index.html +1 -1
  100. package/client-dist/{main-2KWB3QYK.js → main-PJPEDVJT.js} +1 -1
  101. package/package.json +6 -5
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FAM_AgentMemoryReader_Service = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const fs = tslib_1.__importStar(require("fs"));
6
+ const path = tslib_1.__importStar(require("path"));
7
+ const error_codes_const_1 = require("../../../_collections/error-codes.const");
8
+ const fam_error_factory_util_1 = require("../../../_collections/fam-error-factory.util");
9
+ const fam_claude_mem_source_type_enum_1 = require("../_enums/fam-claude-mem-source.type-enum");
10
+ const fam_claude_mem_interface_1 = require("../_models/interfaces/fam-claude-mem.interface");
11
+ /**
12
+ * `FAM_AgentMemoryReader_Service` (agent-memory import, SP-11.1-mintára) — az agent (Claude) tartós
13
+ * memóriáját (`.claude/.../memory/*.md`) olvassa be **függőség-mentesen** (fs + egyszerű frontmatter-parse),
14
+ * és `FAM_ClaudeMemRecord[]`-dá normalizálja → a meglévő import-engine (preview/run/rollback/dedup/embed)
15
+ * VÁLTOZATLANUL feldolgozza (a `readSource` `agent-memory`-ágáról hívva).
16
+ *
17
+ * **Forrás-shape (a memória-fájlok frontmatter-e):**
18
+ * ```
19
+ * ---
20
+ * name: <kebab-slug>
21
+ * description: <egy-soros>
22
+ * metadata:
23
+ * type: user | feedback | project | reference
24
+ * ---
25
+ * <markdown body … [[link]] …>
26
+ * ```
27
+ * A `MEMORY.md` (mutató-index, NEM memória) **kihagyva**. Defenzív: parse-hiba/üres fájl → `warnings`, a
28
+ * többi folytatódik (nothing-lost). A cél-tár a `type`-ról képződik a `FAM_TargetMapping_Util`-ban
29
+ * (reference→knowledge, feedback→rules, project→memory, user→knowledge).
30
+ */
31
+ class FAM_AgentMemoryReader_Service {
32
+ static _instance;
33
+ issuer = 'FAM_AgentMemoryReader_Service';
34
+ /** A mutató-index fájl (egy-soros pointer-lista, NEM önálló memória) — az importból kihagyva. */
35
+ static INDEX_FILE = 'MEMORY.md';
36
+ /** Az agent-memory rekordok rögzített scope-projektje (egy scope minden bejegyzésre; a run auto-create-eli). */
37
+ static SCOPE_PROJECT = 'agent-memory';
38
+ static getInstance() {
39
+ if (!FAM_AgentMemoryReader_Service._instance) {
40
+ FAM_AgentMemoryReader_Service._instance = new FAM_AgentMemoryReader_Service();
41
+ }
42
+ return FAM_AgentMemoryReader_Service._instance;
43
+ }
44
+ /** A memória-mappa beolvasása → normalizált rekordok (a `path` a `*.md`-ket tartalmazó könyvtár). */
45
+ async read(set) {
46
+ const dir = set.path ?? '';
47
+ if (!dir || !fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
48
+ throw fam_error_factory_util_1.FAM_Error_Util.create({
49
+ errorCode: error_codes_const_1.FAM_ERROR_CODES.impSource,
50
+ message: `Az agent-memory mappa nem található vagy nem könyvtár: '${dir}'.`,
51
+ issuer: this.issuer,
52
+ context: { operation: 'agent-memory-read' },
53
+ });
54
+ }
55
+ const warnings = [];
56
+ const files = fs.readdirSync(dir)
57
+ .filter((file) => file.toLowerCase().endsWith('.md') && file !== FAM_AgentMemoryReader_Service.INDEX_FILE)
58
+ .sort();
59
+ const records = [];
60
+ for (const file of files) {
61
+ const full = path.join(dir, file);
62
+ try {
63
+ const parsed = FAM_AgentMemoryReader_Service.parse(fs.readFileSync(full, 'utf8'));
64
+ if (!parsed.body.trim() && !(parsed.description && parsed.description.trim())) {
65
+ warnings.push(`üres memória-fájl kihagyva: ${file}`);
66
+ continue;
67
+ }
68
+ records.push(this.toRecord(file, full, parsed));
69
+ }
70
+ catch (error) {
71
+ warnings.push(`agent-memory parse-hiba (${file}): ${error?.message}`);
72
+ }
73
+ }
74
+ return {
75
+ from: fam_claude_mem_source_type_enum_1.FAM_ClaudeMemSource_Type.agentMemory,
76
+ records: records,
77
+ sessions: [],
78
+ counts: { observations: records.length, summaries: 0, prompts: 0, sessions: 0 },
79
+ warnings: warnings,
80
+ };
81
+ }
82
+ /** Egy parse-olt memória → `FAM_ClaudeMemRecord` (entity=agentMemory; type a cél-tárhoz; slug a dedup-hoz). */
83
+ toRecord(file, fullPath, parsed) {
84
+ const slug = (parsed.name && parsed.name.trim()) ? parsed.name.trim() : file.replace(/\.md$/i, '');
85
+ return {
86
+ entity: fam_claude_mem_interface_1.FAM_ClaudeMemEntity_Type.agentMemory,
87
+ sourceRowId: slug,
88
+ project: FAM_AgentMemoryReader_Service.SCOPE_PROJECT,
89
+ type: parsed.type,
90
+ title: parsed.description,
91
+ narrative: parsed.body,
92
+ concepts: parsed.links,
93
+ filesRead: [],
94
+ filesModified: [fullPath],
95
+ };
96
+ }
97
+ /**
98
+ * Egy `.md` fájl frontmatter + body + `[[link]]` parse-e (függőség-mentes, defenzív). A frontmatter a
99
+ * `---\n…\n---` blokk; a `name`/`description` top-szintű, a `type` a `metadata:` alatt indentált — mindet a
100
+ * sor-szintű `field`-regex húzza (az indentet `^\s*` engedi). Frontmatter nélkül a teljes szöveg a body.
101
+ */
102
+ static parse(raw) {
103
+ const links = [...new Set((raw.match(/\[\[([^\]]+)\]\]/g) ?? []).map((m) => m.slice(2, -2).trim()))]
104
+ .filter((link) => link.length > 0);
105
+ const frontMatch = raw.match(/^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/);
106
+ if (!frontMatch) {
107
+ return { body: raw.trim(), links: links };
108
+ }
109
+ const front = frontMatch[1];
110
+ const body = (frontMatch[2] ?? '').trim();
111
+ return {
112
+ name: FAM_AgentMemoryReader_Service.field(front, 'name'),
113
+ description: FAM_AgentMemoryReader_Service.field(front, 'description'),
114
+ type: FAM_AgentMemoryReader_Service.field(front, 'type'),
115
+ body: body,
116
+ links: links,
117
+ };
118
+ }
119
+ /** Egy frontmatter-mező értéke (`key: value`, sor-szintű, indent-toleráns; a körülvevő idézőjel levágva). */
120
+ static field(front, key) {
121
+ const match = front.match(new RegExp(`^\\s*${key}\\s*:\\s*(.+)$`, 'm'));
122
+ return match ? match[1].trim().replace(/^["']|["']$/g, '').trim() : undefined;
123
+ }
124
+ }
125
+ exports.FAM_AgentMemoryReader_Service = FAM_AgentMemoryReader_Service;
@@ -14,12 +14,15 @@ const scope_reference_1 = require("../../scope-reference");
14
14
  const fam_claude_mem_source_type_enum_1 = require("../_enums/fam-claude-mem-source.type-enum");
15
15
  const fam_import_content_hash_util_1 = require("../_collections/fam-import-content-hash.util");
16
16
  const fam_target_mapping_util_1 = require("../_collections/fam-target-mapping.util");
17
+ const fam_agent_memory_reader_service_1 = require("./fam-agent-memory-reader.service");
17
18
  const fam_claude_mem_export_reader_service_1 = require("./fam-claude-mem-export-reader.service");
18
19
  const fam_claude_mem_sqlite_reader_service_1 = require("./fam-claude-mem-sqlite-reader.service");
19
20
  const fam_claude_mem_worker_reader_service_1 = require("./fam-claude-mem-worker-reader.service");
20
21
  const fam_import_dedup_data_service_1 = require("./fam-import-dedup.data-service");
21
22
  /** A preview `mappingPreview` mintájának mérete (nem a teljes lista — a `map` adja a teljeset). */
22
23
  const PREVIEW_SAMPLE_SIZE = 10;
24
+ /** Hány feldolgozott rekordonként írjon a run egy progress-sort a szerver-konzolra (user: „more feedback in logs"). */
25
+ const PROGRESS_LOG_EVERY = 2000;
23
26
  /** A claude-mem `project` raw név alapértelmezett scope-layer-e (dsgn-009 §4 — project-layer). */
24
27
  const PROJECT_SCOPE_LAYER = 'project';
25
28
  /**
@@ -69,11 +72,15 @@ class FAM_ClaudeMemImport_ControlService {
69
72
  const dedup = { willImport: 0, willSkipExisting: 0, willSkipDuplicateContent: 0 };
70
73
  const mappingPreview = [];
71
74
  const previewUncertainty = [];
75
+ // FAM-REV-063: per-projekt scope-feloldás memoizálás (különben 68k rekord = 68k embed → preview-timeout).
76
+ const scopeCache = new Map();
77
+ // FAM-REV-063: dedup-batch-index (a per-rekord 3 DB-query helyett in-memory) — a cél-tárak EGYSZER töltve.
78
+ const dedupIndex = await fam_import_dedup_data_service_1.FAM_ImportDedup_DataService.getInstance().buildIndex(this.targetTablesFor(data, request));
72
79
  for (const record of data.records) {
73
- const plan = await this.buildPlan({ record: record, request: request, allowCreateScope: false, uncertainty: previewUncertainty });
74
- const verdict = await fam_import_dedup_data_service_1.FAM_ImportDedup_DataService.getInstance().verdictFor({
75
- record: record, table: plan.table, importSourceId: plan.importSourceId, contentHash: plan.contentHash,
76
- });
80
+ const plan = await this.buildPlan({ record: record, request: request, allowCreateScope: false, uncertainty: previewUncertainty, scopeCache: scopeCache });
81
+ const verdict = fam_import_dedup_data_service_1.FAM_ImportDedup_DataService.getInstance().verdictForIndexed({
82
+ table: plan.table, importSourceId: plan.importSourceId, contentHash: plan.contentHash,
83
+ }, dedupIndex);
77
84
  this.tallyDedup(dedup, verdict);
78
85
  if (mappingPreview.length < PREVIEW_SAMPLE_SIZE) {
79
86
  mappingPreview.push(plan);
@@ -105,9 +112,10 @@ class FAM_ClaudeMemImport_ControlService {
105
112
  const warnings = [...data.warnings];
106
113
  const uncertainty = [];
107
114
  const plans = [];
115
+ const scopeCache = new Map(); // FAM-REV-063
108
116
  for (const record of data.records) {
109
117
  plans.push(await this.buildPlan({
110
- record: record, request: request, allowCreateScope: false, uncertainty: uncertainty,
118
+ record: record, request: request, allowCreateScope: false, uncertainty: uncertainty, scopeCache: scopeCache,
111
119
  }));
112
120
  }
113
121
  return {
@@ -139,14 +147,26 @@ class FAM_ClaudeMemImport_ControlService {
139
147
  let failed = 0;
140
148
  // Embedding-költség pillanatkép a batch ELŐTT (a delta a batch embed-költsége — BFR-AM-007).
141
149
  const costBefore = embedding_1.FAM_EmbeddingCost_ControlService.getInstance().getStats().totalEstimatedCostUsd;
150
+ // FAM-REV-063: per-projekt scope-feloldás memoizálás (a 68k rekord ~25 projektje egyszer oldódjon fel/
151
+ // auto-create-elődjön; a run dominánsan a content-embed, de a scope-embed így is 68k→25-re csökken).
152
+ const scopeCache = new Map();
153
+ // FAM-REV-063: dedup-batch-index (in-memory, futó-akkumulátor) a per-rekord DB-query helyett.
154
+ const dedupIndex = await fam_import_dedup_data_service_1.FAM_ImportDedup_DataService.getInstance().buildIndex(this.targetTablesFor(data, request));
155
+ // Progress-feedback (user-direktíva 2026-06-21: „add more feedback in logs"): a hosszú import alatt a
156
+ // szerver-konzol mutassa a haladást (nem csak az LM Studio pörög). START + periodikus + KÉSZ sorok.
157
+ const total = data.records.length;
158
+ const startMs = Date.now();
159
+ let processed = 0;
160
+ fsm_dynamo_1.DyFM_Log.log(`[claude-mem import] ▶ START — ${total} rekord, batch=${batchId} `
161
+ + `(includePrompts=${request.includePrompts ?? false}). A dedup-skip nem embeddel (gyors); az ÚJ rekordok embeddelődnek.`);
142
162
  for (const record of data.records) {
143
163
  try {
144
164
  const plan = await this.buildPlan({
145
- record: record, request: request, allowCreateScope: true, uncertainty: uncertaintyNotes,
146
- });
147
- const verdict = await fam_import_dedup_data_service_1.FAM_ImportDedup_DataService.getInstance().verdictFor({
148
- record: record, table: plan.table, importSourceId: plan.importSourceId, contentHash: plan.contentHash,
165
+ record: record, request: request, allowCreateScope: true, uncertainty: uncertaintyNotes, scopeCache: scopeCache,
149
166
  });
167
+ const verdict = fam_import_dedup_data_service_1.FAM_ImportDedup_DataService.getInstance().verdictForIndexed({
168
+ table: plan.table, importSourceId: plan.importSourceId, contentHash: plan.contentHash,
169
+ }, dedupIndex);
150
170
  if (verdict !== 'import') {
151
171
  // A skip-eltek NEM embeddelődnek (cost-takarékosság — dsgn-009 §5.3 / §9.3).
152
172
  skipped++;
@@ -171,7 +191,19 @@ class FAM_ClaudeMemImport_ControlService {
171
191
  importSourceId: fam_target_mapping_util_1.FAM_TargetMapping_Util.importSourceId(record),
172
192
  });
173
193
  }
194
+ // Periodikus progress (minden PROGRESS_LOG_EVERY rekord + a legvégén): feldolgozva / összes,
195
+ // ingested/skipped/failed, processed-ráta + durva ETA — a szerver-konzolon élőben látszik.
196
+ processed++;
197
+ if (processed % PROGRESS_LOG_EVERY === 0 || processed === total) {
198
+ const elapsedS = Math.max(1, Math.round((Date.now() - startMs) / 1000));
199
+ const procRate = processed / elapsedS;
200
+ const etaMin = procRate > 0 ? Math.round((total - processed) / procRate / 60) : 0;
201
+ fsm_dynamo_1.DyFM_Log.log(`[claude-mem import] ${processed}/${total} — ingested ${ingested}, skipped ${skipped} (dedup), `
202
+ + `failed ${failed} | ${procRate.toFixed(1)} rec/s, ~${etaMin} perc hátra (elapsed ${elapsedS}s).`);
203
+ }
174
204
  }
205
+ fsm_dynamo_1.DyFM_Log.log(`[claude-mem import] ✓ KÉSZ — ${ingested} ingested + ${skipped} skipped (dedup) + ${failed} failed `
206
+ + `= ${total} rekord, ${Math.max(1, Math.round((Date.now() - startMs) / 1000))}s alatt. batch=${batchId}.`);
175
207
  const costAfter = embedding_1.FAM_EmbeddingCost_ControlService.getInstance().getStats().totalEstimatedCostUsd;
176
208
  const status = this.runStatus(ingested, failed);
177
209
  const result = {
@@ -249,14 +281,22 @@ class FAM_ClaudeMemImport_ControlService {
249
281
  const from = this.resolveFrom(request);
250
282
  const includePrompts = this.resolveIncludePrompts(request);
251
283
  const path = request.path ?? '';
284
+ let data;
252
285
  switch (from) {
253
286
  case fam_claude_mem_source_type_enum_1.FAM_ClaudeMemSource_Type.exportJson:
254
- return fam_claude_mem_export_reader_service_1.FAM_ClaudeMemExportReader_Service.getInstance().read({ path: path, includePrompts: includePrompts });
287
+ data = await fam_claude_mem_export_reader_service_1.FAM_ClaudeMemExportReader_Service.getInstance().read({ path: path, includePrompts: includePrompts });
288
+ break;
255
289
  case fam_claude_mem_source_type_enum_1.FAM_ClaudeMemSource_Type.sqlite:
256
- return fam_claude_mem_sqlite_reader_service_1.FAM_ClaudeMemSqliteReader_Service.getInstance().read({ path: path, includePrompts: includePrompts });
290
+ data = await fam_claude_mem_sqlite_reader_service_1.FAM_ClaudeMemSqliteReader_Service.getInstance().read({ path: path, includePrompts: includePrompts });
291
+ break;
257
292
  case fam_claude_mem_source_type_enum_1.FAM_ClaudeMemSource_Type.workerApi:
258
- return fam_claude_mem_worker_reader_service_1.FAM_ClaudeMemWorkerReader_Service.getInstance()
259
- .read({ baseUrl: path, includePrompts: includePrompts });
293
+ data = await fam_claude_mem_worker_reader_service_1.FAM_ClaudeMemWorkerReader_Service.getInstance().read({ baseUrl: path, includePrompts: includePrompts });
294
+ break;
295
+ case fam_claude_mem_source_type_enum_1.FAM_ClaudeMemSource_Type.agentMemory:
296
+ // Agent-memory markdown-mappa (`.claude/.../memory/*.md`) — a reader `FAM_ClaudeMemRecord`-ot
297
+ // termel, így a teljes run/preview/rollback/dedup/embed VÁLTOZATLANUL feldolgozza.
298
+ data = await fam_agent_memory_reader_service_1.FAM_AgentMemoryReader_Service.getInstance().read({ path: path });
299
+ break;
260
300
  default:
261
301
  throw fam_error_factory_util_1.FAM_Error_Util.create({
262
302
  errorCode: error_codes_const_1.FAM_ERROR_CODES.impSource,
@@ -265,6 +305,24 @@ class FAM_ClaudeMemImport_ControlService {
265
305
  context: { operation: 'import-read-source' },
266
306
  });
267
307
  }
308
+ return this.applyImportFilters(data, request);
309
+ }
310
+ /**
311
+ * Kurált-subset szűrés a beolvasott rekordokra (L3 „A" opció + teszt-limit). `excludeTypes` → a megadott
312
+ * observation-`type`-ok kihagyva (a summary/prompt — nincs `type` — MARAD); `maxRecords` → a szűrt lista
313
+ * levágva. Mindkettő a `preview`-ban ÉS a `run`-ban hat (a `readSource` a közös belépő). Szűrés nélkül a
314
+ * forrás-objektum változatlanul tér vissza (nincs fölös másolás).
315
+ */
316
+ applyImportFilters(data, request) {
317
+ let records = data.records;
318
+ if (request.excludeTypes && request.excludeTypes.length) {
319
+ const exclude = new Set(request.excludeTypes);
320
+ records = records.filter((record) => !(record.type && exclude.has(record.type)));
321
+ }
322
+ if (typeof request.maxRecords === 'number' && request.maxRecords >= 0) {
323
+ records = records.slice(0, request.maxRecords);
324
+ }
325
+ return records === data.records ? data : { ...data, records: records };
268
326
  }
269
327
  // =========================================================================
270
328
  // leképezési-terv építés (SP-11.2: target-mapping + scope-resolve)
@@ -276,6 +334,17 @@ class FAM_ClaudeMemImport_ControlService {
276
334
  * `resolveForWrite` (auto-create-elhet). A scope-feloldás van-match-bizonytalansága az `uncertainty`
277
335
  * gyűjtőbe (NEM hiba).
278
336
  */
337
+ /**
338
+ * A rekordok DISTINCT cél-tárai (FAM-REV-063, a dedup-index építéséhez) — OLCSÓ: csak a target-mapping
339
+ * (`resolveTarget`) fut rekordonként (NINCS embed/scope-feloldás), így a 68k rekordon is ms-ek.
340
+ */
341
+ targetTablesFor(data, request) {
342
+ const tables = new Set();
343
+ for (const record of data.records) {
344
+ tables.add(fam_target_mapping_util_1.FAM_TargetMapping_Util.resolveTarget({ record: record, overrides: request.overrides }).table);
345
+ }
346
+ return Array.from(tables);
347
+ }
279
348
  async buildPlan(set) {
280
349
  const target = fam_target_mapping_util_1.FAM_TargetMapping_Util.resolveTarget({ record: set.record, overrides: set.request.overrides });
281
350
  const content = fam_target_mapping_util_1.FAM_TargetMapping_Util.composeContent(set.record);
@@ -284,7 +353,7 @@ class FAM_ClaudeMemImport_ControlService {
284
353
  });
285
354
  const importSourceId = fam_target_mapping_util_1.FAM_TargetMapping_Util.importSourceId(set.record);
286
355
  const scopeResolve = await this.resolveScopeForRecord({
287
- record: set.record, request: set.request, allowCreateScope: set.allowCreateScope,
356
+ record: set.record, request: set.request, allowCreateScope: set.allowCreateScope, cache: set.scopeCache,
288
357
  });
289
358
  for (const note of scopeResolve.uncertaintyNotes) {
290
359
  set.uncertainty.push(note);
@@ -315,14 +384,28 @@ class FAM_ClaudeMemImport_ControlService {
315
384
  if (!rawScope || !rawScope.length) {
316
385
  return { scopePath: [], uncertaintyNotes: [] };
317
386
  }
387
+ // FAM-REV-063: a scope a rekord `project`-jétől függ (a 68k rekord ~25 distinct projektre), de a
388
+ // feloldás (`previewResolveScope`/`resolveForWrite`) EMBEDDEL → per-rekord hívás = 68k embed (preview-
389
+ // timeout). Memoizálás a rawScope-kulcson: projektenként EGYSZER oldunk fel. Cache-HIT → a scopePath
390
+ // ugyanaz, de az uncertaintyNotes ÜRES (a note-ot az első [miss] feloldás már begyűjtötte → nincs 68k dup).
391
+ const cacheKey = JSON.stringify(rawScope);
392
+ const cached = set.cache?.get(cacheKey);
393
+ if (cached) {
394
+ return { scopePath: cached.scopePath, uncertaintyNotes: [] };
395
+ }
396
+ let resolved;
318
397
  if (set.allowCreateScope) {
319
398
  // run: a write-path teljes feloldása (auto-create-elhet — createdBy='agent-write' a MP-3-ban).
320
399
  const resolve = await scope_reference_1.FAM_ScopeResolver_ControlService.getInstance().resolveForWrite(rawScope);
321
- return { scopePath: resolve.scopePath, uncertaintyNotes: resolve.uncertaintyNotes };
400
+ resolved = { scopePath: resolve.scopePath, uncertaintyNotes: resolve.uncertaintyNotes };
401
+ }
402
+ else {
403
+ // preview/map: read-only — a read-expand-szerű feloldás NEM hoz létre scope-ot. A trace-feloldáson
404
+ // át döntjük el, feloldható-e biztosan; ha nem, üres scopePath + a bizonytalanság jelezve.
405
+ resolved = await this.previewResolveScope(rawScope);
322
406
  }
323
- // preview/map: read-only — a read-expand-szerű feloldás NEM hoz létre scope-ot. A trace-feloldáson
324
- // át döntjük el, feloldható-e biztosan; ha nem, üres scopePath + a bizonytalanság jelezve.
325
- return this.previewResolveScope(rawScope);
407
+ set.cache?.set(cacheKey, resolved);
408
+ return resolved;
326
409
  }
327
410
  /**
328
411
  * Egy rekord nyers scope-lánca: az override `scope['project:<name>']` explicit `scopePath`-ja
@@ -71,6 +71,59 @@ class FAM_ImportDedup_DataService {
71
71
  }
72
72
  return 'import';
73
73
  }
74
+ /**
75
+ * **Batch-index építése (FAM-REV-063)** — a per-rekord `verdictFor` 3 DB-query-je 68k rekordon ~136s
76
+ * (preview-timeout). Ehelyett a cél-tárak MEGLÉVŐ claude-mem entry-jeinek `importSourceId` + `contentHash`
77
+ * kulcsait EGYSZER töltjük Set-ekbe (táranként), és a `verdictForIndexed` in-memory dönt. Első importnál a
78
+ * forrás-szűrt halmaz üres → azonnali. (Megjegyzés: a betöltés még nem projektált [lean-find = BFR-AM-013],
79
+ * így RE-importnál a már beimportált halmaz teljes entry-it tölti — első importnál ez 0.)
80
+ */
81
+ async buildIndex(tables) {
82
+ const byTable = new Map();
83
+ for (const table of tables) {
84
+ const entry = { sourceIds: new Set(), contentHashes: new Set() };
85
+ byTable.set(table, entry);
86
+ const dataService = this.dataServiceFor(table);
87
+ if (!dataService) {
88
+ continue;
89
+ }
90
+ const existing = await dataService.findHydratableList({
91
+ importSource: FAM_ImportDedup_DataService.IMPORT_SOURCE,
92
+ });
93
+ for (const item of existing) {
94
+ if (item.importSourceId) {
95
+ entry.sourceIds.add(item.importSourceId);
96
+ }
97
+ if (item.contentHash) {
98
+ entry.contentHashes.add(item.contentHash);
99
+ }
100
+ }
101
+ }
102
+ return { byTable: byTable };
103
+ }
104
+ /**
105
+ * In-memory dedup-verdikt a batch-indexszel (FAM-REV-063) — a `verdictFor` (1)+(2) lépcsőjének
106
+ * O(1) megfelelője, NULLA DB-query-vel. Az index egyben **futó-akkumulátor**: az `import`-nak ítélt
107
+ * rekord kulcsai bekerülnek, így a forráson-belüli (within-batch) duplikátumok is `skip-duplicate-content`-
108
+ * et kapnak (pontosabb forecast + a sequential run idempotenciája). A (3) ritka, (2)-vel gyakorlatilag
109
+ * redundáns kompozit-fallback NEM része az indexelt útnak (perf-tradeoff — dsgn-009 §3).
110
+ */
111
+ verdictForIndexed(set, index) {
112
+ let entry = index.byTable.get(set.table);
113
+ if (!entry) {
114
+ entry = { sourceIds: new Set(), contentHashes: new Set() };
115
+ index.byTable.set(set.table, entry);
116
+ }
117
+ if (entry.sourceIds.has(set.importSourceId)) {
118
+ return 'skip-existing';
119
+ }
120
+ if (entry.contentHashes.has(set.contentHash)) {
121
+ return 'skip-duplicate-content';
122
+ }
123
+ entry.sourceIds.add(set.importSourceId);
124
+ entry.contentHashes.add(set.contentHash);
125
+ return 'import';
126
+ }
74
127
  /** A (3) fallback kompozit-kulcs jelenléte: sdk_session_id + title + created_at_epoch mind kell. */
75
128
  hasCompositeKey(record) {
76
129
  return Boolean(record.sdkSessionId && record.title && typeof record.createdAtEpoch === 'number');
@@ -11,7 +11,7 @@
11
11
  * függőség (BFR-AM-009 fallback) — a bedrock-reader landolásakor (MP-15) CSAK a reader-impl cserélődik.
12
12
  */
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.FAM_ClaudeMemEntity_Type = exports.FAM_ClaudeMemSource_Type = exports.FAM_ImportContentHash_Util = exports.FAM_TargetMapping_Util = exports.FAM_ImportDedup_DataService = exports.FAM_ClaudeMemNormalize_Util = exports.FAM_ClaudeMemWorkerReader_Service = exports.FAM_ClaudeMemSqliteReader_Service = exports.FAM_ClaudeMemExportReader_Service = exports.FAM_ClaudeMemImport_ControlService = void 0;
14
+ exports.FAM_ClaudeMemEntity_Type = exports.FAM_ClaudeMemSource_Type = exports.FAM_ImportContentHash_Util = exports.FAM_TargetMapping_Util = exports.FAM_ImportDedup_DataService = exports.FAM_ClaudeMemNormalize_Util = exports.FAM_AgentMemoryReader_Service = exports.FAM_ClaudeMemWorkerReader_Service = exports.FAM_ClaudeMemSqliteReader_Service = exports.FAM_ClaudeMemExportReader_Service = exports.FAM_ClaudeMemImport_ControlService = void 0;
15
15
  // SP-11.3 — a migrációs engine (preview → map → run → rollback)
16
16
  var fam_claude_mem_import_control_service_1 = require("./_services/fam-claude-mem-import.control-service");
17
17
  Object.defineProperty(exports, "FAM_ClaudeMemImport_ControlService", { enumerable: true, get: function () { return fam_claude_mem_import_control_service_1.FAM_ClaudeMemImport_ControlService; } });
@@ -22,6 +22,8 @@ var fam_claude_mem_sqlite_reader_service_1 = require("./_services/fam-claude-mem
22
22
  Object.defineProperty(exports, "FAM_ClaudeMemSqliteReader_Service", { enumerable: true, get: function () { return fam_claude_mem_sqlite_reader_service_1.FAM_ClaudeMemSqliteReader_Service; } });
23
23
  var fam_claude_mem_worker_reader_service_1 = require("./_services/fam-claude-mem-worker-reader.service");
24
24
  Object.defineProperty(exports, "FAM_ClaudeMemWorkerReader_Service", { enumerable: true, get: function () { return fam_claude_mem_worker_reader_service_1.FAM_ClaudeMemWorkerReader_Service; } });
25
+ var fam_agent_memory_reader_service_1 = require("./_services/fam-agent-memory-reader.service");
26
+ Object.defineProperty(exports, "FAM_AgentMemoryReader_Service", { enumerable: true, get: function () { return fam_agent_memory_reader_service_1.FAM_AgentMemoryReader_Service; } });
25
27
  var fam_claude_mem_normalize_util_1 = require("./_collections/fam-claude-mem-normalize.util");
26
28
  Object.defineProperty(exports, "FAM_ClaudeMemNormalize_Util", { enumerable: true, get: function () { return fam_claude_mem_normalize_util_1.FAM_ClaudeMemNormalize_Util; } });
27
29
  // SP-11.2 — dedup + target-mapping + content-hash
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FAM_RetrievalCandidate_DataService = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const mongoose_1 = tslib_1.__importDefault(require("mongoose"));
4
6
  const fam_table_type_enum_1 = require("../../../_enums/fam-table.type-enum");
5
7
  const embedding_1 = require("../../embedding");
6
8
  /**
@@ -33,8 +35,77 @@ class FAM_RetrievalCandidate_DataService {
33
35
  return [];
34
36
  }
35
37
  const filter = this.buildFilter(input, registryEntry);
36
- const dataService = new embedding_1.FAM_Entry_DataService({ dataParams: registryEntry.dataParams, issuer: this.issuer });
37
- return dataService.findDataList(filter);
38
+ return this.findActiveCandidates(registryEntry, filter);
39
+ }
40
+ /**
41
+ * FEAT-003 — egy tár azon entry-jei, amik az adott azonosító-kódok BÁRMELYIKÉT citálják
42
+ * (`referenceCodes $in codes`), az AZONOS prefilter-kontextussal (scope subtree + tag + kind +
43
+ * `rules.isActive` guard), mint a vektor-jelölt-betöltés. Ez a determinisztikus read-expanzió Mongo-
44
+ * lekérdezése: a kód-keresés a vektor-hasonlóság HELYETT explicit link → az ÖSSZES citáló elem,
45
+ * `embeddingStatus`-tól függetlenül. Üres kód-lista / ismeretlen tár → üres lista (no-op).
46
+ */
47
+ async loadByReferenceCodes(input, codes) {
48
+ if (!codes.length) {
49
+ return [];
50
+ }
51
+ const registryEntry = embedding_1.FAM_StoreRegistry_Util.getEntry(input.table);
52
+ if (!registryEntry) {
53
+ return [];
54
+ }
55
+ const filter = this.buildFilter(input, registryEntry);
56
+ filter.referenceCodes = { $in: codes };
57
+ return this.findActiveCandidates(registryEntry, filter);
58
+ }
59
+ /**
60
+ * A topK-hit metaadat-betöltése `_id`-listából (az UNSCOPED read-fast-path-hez, SP-5.x perf). A főpool-
61
+ * keresés UTÁN CSAK a tényleges hit-eket tölti (a hit-row + weight + recency összefűzéséhez), NEM mind a
62
+ * jelöltet — unscoped query-nél ez a Mongo-munkát ~242×-ére csökkenti (mért: 727ms → 3ms). Aktív-only
63
+ * (`_deleted:null`) + `contentVector`-kihagyó lean (a `findActiveCandidates`-en át; a `_id $in` string-ID-it
64
+ * a Mongoose a `_id`-úton ObjectId-ra castolja). Üres lista / ismeretlen tár → üres (no-op).
65
+ */
66
+ async loadByIds(table, ids) {
67
+ if (!ids.length) {
68
+ return [];
69
+ }
70
+ const registryEntry = embedding_1.FAM_StoreRegistry_Util.getEntry(table);
71
+ if (!registryEntry) {
72
+ return [];
73
+ }
74
+ const filter = {};
75
+ filter._id = { $in: ids };
76
+ return this.findActiveCandidates(registryEntry, filter);
77
+ }
78
+ /**
79
+ * PERF (FAM-REV / BFR-AM-013): a jelölt-betöltés a `contentVector`-t (a domináns memória-/transzfer-
80
+ * költség: ~2560 float ≈ 20 KB/entry; 23k codebase-entry ≈ 486 MB) **KIHAGYJA** — a vektorok MÁR az
81
+ * in-memory LVS-pool-ban élnek, a candidate-stage SOHA nem olvassa az `entry.contentVector`-t (a
82
+ * `toHit`/`mergeCodeHit` sem). **Lean** (plain object, NEM Mongoose-doc → nincs schema-hidratálás) +
83
+ * `contentVector`-kihagyó projekció a mongoose-modellen → a per-query jelölt-load **~40s → ~1s** (mért
84
+ * 46,9× a raw-load-on; a Mongoose-hidratálás-megtakarítás tetézi). A `_deleted:null` az aktív-szűrő
85
+ * (a `findDataList` DB-service-szintű soft-delete-kizárását replikálja: a soft-delete `_deleted`-et
86
+ * `Date`-re állítja, az aktív entry-n hiányzik/`null`). Model-hiány (boot-előtt) → `findDataList`-fallback
87
+ * (korrekt, csak lassabb).
88
+ */
89
+ async findActiveCandidates(registryEntry, filter) {
90
+ const dataName = registryEntry.dataParams.dataName;
91
+ const model = mongoose_1.default.models[dataName];
92
+ if (!model) {
93
+ // Fallback: a model még nincs regisztrálva (pl. boot-bootstrap előtt) → teljes-doc findDataList.
94
+ const dataService = new embedding_1.FAM_Entry_DataService({ dataParams: registryEntry.dataParams, issuer: this.issuer });
95
+ return dataService.findDataList(filter);
96
+ }
97
+ // `_deleted:null` → aktív-only (a soft-delete Date-marker kizárva); `contentVector:0` → a 486MB pool-
98
+ // duplikátum kihagyva; `.lean()` → nincs Mongoose-doc hidratálás.
99
+ const leanFilter = { ...filter, _deleted: null };
100
+ const docs = await model.find(leanFilter, { contentVector: 0 }).lean().exec();
101
+ // KRITIKUS (_id-konzisztencia): a `.lean()` a NYERS Mongo `_id`-t adja (ObjectId), DE a
102
+ // `DyFM_Metadata._id` STRING, és a vektor-pool-kulcsok + a `findDataList` is string `_id`-vel
103
+ // dolgoznak. ObjectId-kulcs SOHA nem egyezne a string pool-kulccsal (`Map.get` referencia-
104
+ // egyenlőség) → a candidateId-match 0 jelöltet adna → 0 hit. Stringify → bit-azonos a findDataList-tel.
105
+ for (const doc of docs) {
106
+ doc._id = String(doc._id);
107
+ }
108
+ return docs;
38
109
  }
39
110
  /**
40
111
  * A Mongo-prefilter összeállítása (dsgn-005 §2.1/§2.2). A `scopePath.scopeId $in scopeIdSet`
@@ -57,9 +128,12 @@ class FAM_RetrievalCandidate_DataService {
57
128
  if (input.kindFilter) {
58
129
  filter.kind = input.kindFilter;
59
130
  }
60
- // Soft-delete/aktív guard a `rules` táron (dsgn-001 §4): inaktív szabály SOHA nem jelölt.
131
+ // Soft-delete/aktív guard a `rules` táron (dsgn-001 §4): inaktív szabály SOHA nem jelölt. „Inaktív"
132
+ // = EXPLICIT `isActive:false`; a hiányzó/`true` mező AKTÍV (a `create` a bázis `FAM_Entry`-t menti,
133
+ // amin a per-tár `isActive:true` schema-default nem mindig perzisztálódik → `$ne:false` robusztus,
134
+ // hogy az újonnan írt szabály is jelölt legyen; csak a kézzel-deaktivált zárandó ki).
61
135
  if (registryEntry.table === fam_table_type_enum_1.FAM_Table.rules) {
62
- filter.isActive = true;
136
+ filter.isActive = { $ne: false };
63
137
  }
64
138
  return filter;
65
139
  }