@futdevpro/fdp-agent-memory 0.1.0
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/README.md +345 -0
- package/build/package.json +96 -0
- package/build/src/_assets/mcp-client-config/README.md +29 -0
- package/build/src/_assets/mcp-client-config/claude_desktop_config.json +15 -0
- package/build/src/_assets/mcp-client-config/mcp.json +15 -0
- package/build/src/_collections/config-catalog.const.js +180 -0
- package/build/src/_collections/config-error-codes.const.js +30 -0
- package/build/src/_collections/config-presets.const.js +25 -0
- package/build/src/_collections/error-banners.const.js +100 -0
- package/build/src/_collections/error-codes.const.js +150 -0
- package/build/src/_collections/fam-db-models.const.js +37 -0
- package/build/src/_collections/fam-entry-bootstrap.util.js +80 -0
- package/build/src/_collections/fam-error-context.util.js +90 -0
- package/build/src/_collections/fam-error-factory.util.js +64 -0
- package/build/src/_enums/fam-config-level.type-enum.js +15 -0
- package/build/src/_enums/fam-table.type-enum.js +20 -0
- package/build/src/_integration-tests/_helpers/fam-integration-test-setup.util.js +105 -0
- package/build/src/_models/data-models/fam-codebase.data-model.js +51 -0
- package/build/src/_models/data-models/fam-coding-patterns.data-model.js +58 -0
- package/build/src/_models/data-models/fam-config.data-model.js +68 -0
- package/build/src/_models/data-models/fam-documents.data-model.js +53 -0
- package/build/src/_models/data-models/fam-entry-base-properties.const.js +43 -0
- package/build/src/_models/data-models/fam-entry.data-model.js +81 -0
- package/build/src/_models/data-models/fam-error.data-model.js +88 -0
- package/build/src/_models/data-models/fam-ingest-run.data-model.js +74 -0
- package/build/src/_models/data-models/fam-knowledge.data-model.js +48 -0
- package/build/src/_models/data-models/fam-memory.data-model.js +55 -0
- package/build/src/_models/data-models/fam-reference.data-model.js +67 -0
- package/build/src/_models/data-models/fam-rules.data-model.js +51 -0
- package/build/src/_models/data-models/fam-scope.data-model.js +52 -0
- package/build/src/_models/interfaces/fam-common.interface.js +23 -0
- package/build/src/_models/interfaces/fam-config.interface.js +2 -0
- package/build/src/_models/interfaces/fam-error.interface.js +2 -0
- package/build/src/_modules/embedding/_collections/fam-embedding-pricing.const.js +22 -0
- package/build/src/_modules/embedding/_collections/fam-store-registry.const.js +63 -0
- package/build/src/_modules/embedding/_models/interfaces/fam-embedding-cost.interface.js +10 -0
- package/build/src/_modules/embedding/_models/interfaces/fam-embedding-provider.interface.js +2 -0
- package/build/src/_modules/embedding/_models/interfaces/fam-resolved-provider.interface.js +2 -0
- package/build/src/_modules/embedding/_services/fam-embedding-bootstrap.control-service.js +52 -0
- package/build/src/_modules/embedding/_services/fam-embedding-cost.control-service.js +175 -0
- package/build/src/_modules/embedding/_services/fam-embedding-pipeline.control-service.js +202 -0
- package/build/src/_modules/embedding/_services/fam-embedding-preset.control-service.js +66 -0
- package/build/src/_modules/embedding/_services/fam-embedding.control-service.js +253 -0
- package/build/src/_modules/embedding/_services/fam-entry.data-service.js +64 -0
- package/build/src/_modules/embedding/_services/fam-lmstudio-embedding.provider.js +112 -0
- package/build/src/_modules/embedding/_services/fam-mock-embedding.provider.js +64 -0
- package/build/src/_modules/embedding/_services/fam-openai-embedding.provider.js +64 -0
- package/build/src/_modules/embedding/_services/fam-vector-search.control-service.js +244 -0
- package/build/src/_modules/embedding/index.js +40 -0
- package/build/src/_modules/ingest/_collections/fam-content-hash.util.js +35 -0
- package/build/src/_modules/ingest/_collections/fam-file-routing.util.js +95 -0
- package/build/src/_modules/ingest/_collections/fam-glob-match.util.js +84 -0
- package/build/src/_modules/ingest/_collections/fam-md-chunker.util.js +164 -0
- package/build/src/_modules/ingest/_collections/fam-scan-path.util.js +91 -0
- package/build/src/_modules/ingest/_collections/fam-secret-exclude.util.js +54 -0
- package/build/src/_modules/ingest/_collections/fam-sliding-chunker.util.js +76 -0
- package/build/src/_modules/ingest/_collections/fam-ts-chunker.util.js +316 -0
- package/build/src/_modules/ingest/_models/interfaces/fam-ingest.interface.js +2 -0
- package/build/src/_modules/ingest/_services/fam-chunker.control-service.js +114 -0
- package/build/src/_modules/ingest/_services/fam-delta-compare.util.js +74 -0
- package/build/src/_modules/ingest/_services/fam-ingest-run.data-service.js +85 -0
- package/build/src/_modules/ingest/_services/fam-ingest.control-service.js +384 -0
- package/build/src/_modules/ingest/_services/fam-scan.control-service.js +211 -0
- package/build/src/_modules/ingest/index.js +46 -0
- package/build/src/_modules/mcp/_collections/fam-core-tools.const.js +186 -0
- package/build/src/_modules/mcp/_models/interfaces/fam-mcp.interface.js +31 -0
- package/build/src/_modules/mcp/_services/fam-capabilities-tool.service.js +111 -0
- package/build/src/_modules/mcp/_services/fam-capability-registry.service.js +1180 -0
- package/build/src/_modules/mcp/_services/fam-mcp-adapter.service.js +123 -0
- package/build/src/_modules/mcp/_services/fam-mcp-server.service.js +69 -0
- package/build/src/_modules/mcp/_services/fam-read-tool.service.js +99 -0
- package/build/src/_modules/mcp/_services/fam-write-tool.service.js +460 -0
- package/build/src/_modules/mcp/index.js +35 -0
- package/build/src/_modules/migration/_collections/fam-claude-mem-normalize.util.js +166 -0
- package/build/src/_modules/migration/_collections/fam-import-content-hash.util.js +38 -0
- package/build/src/_modules/migration/_collections/fam-target-mapping.util.js +90 -0
- package/build/src/_modules/migration/_enums/fam-claude-mem-source.type-enum.js +20 -0
- package/build/src/_modules/migration/_models/interfaces/fam-claude-mem.interface.js +26 -0
- package/build/src/_modules/migration/_services/fam-claude-mem-export-reader.service.js +134 -0
- package/build/src/_modules/migration/_services/fam-claude-mem-import.control-service.js +533 -0
- package/build/src/_modules/migration/_services/fam-claude-mem-sqlite-reader.service.js +144 -0
- package/build/src/_modules/migration/_services/fam-claude-mem-worker-reader.service.js +115 -0
- package/build/src/_modules/migration/_services/fam-import-dedup.data-service.js +102 -0
- package/build/src/_modules/migration/index.js +38 -0
- package/build/src/_modules/retrieval/_models/interfaces/fam-retrieval.interface.js +2 -0
- package/build/src/_modules/retrieval/_services/fam-retrieval-candidate.data-service.js +67 -0
- package/build/src/_modules/retrieval/_services/fam-retrieval-suggestions.util.js +182 -0
- package/build/src/_modules/retrieval/_services/fam-retrieval.control-service.js +282 -0
- package/build/src/_modules/retrieval/index.js +22 -0
- package/build/src/_modules/scope-reference/_collections/fam-fuzzy-match.util.js +86 -0
- package/build/src/_modules/scope-reference/_collections/fam-scope-normalize.util.js +47 -0
- package/build/src/_modules/scope-reference/_models/interfaces/fam-reference-resolution.interface.js +2 -0
- package/build/src/_modules/scope-reference/_models/interfaces/fam-resolution-trace.interface.js +2 -0
- package/build/src/_modules/scope-reference/_services/fam-reference.data-service.js +179 -0
- package/build/src/_modules/scope-reference/_services/fam-scope-resolver.control-service.js +473 -0
- package/build/src/_modules/scope-reference/_services/fam-scope.data-service.js +215 -0
- package/build/src/_modules/scope-reference/index.js +26 -0
- package/build/src/_routes/server/api/api.controller.js +400 -0
- package/build/src/_routes/server/client-app/client-app.control-service.js +132 -0
- package/build/src/_routes/server/client-app/client-app.controller.js +35 -0
- package/build/src/_routes/server/config/config.control-service.js +476 -0
- package/build/src/_routes/server/config/config.data-service.js +49 -0
- package/build/src/_routes/server/errors/errors.control-service.js +123 -0
- package/build/src/_routes/server/errors/errors.controller.js +65 -0
- package/build/src/_routes/server/errors/errors.data-service.js +80 -0
- package/build/src/_routes/server/server-status/server-status.control-service.js +19 -0
- package/build/src/_routes/server/server-status/server-status.controller.js +39 -0
- package/build/src/app.server.js +122 -0
- package/build/src/environments/environment.js +20 -0
- package/build/src/index.js +18 -0
- package/client-dist/chunk-GHKRM4SM.js +1 -0
- package/client-dist/chunk-LMTL7GA3.js +575 -0
- package/client-dist/index.html +17 -0
- package/client-dist/main-2KWB3QYK.js +2 -0
- package/client-dist/polyfills-HGDOEU5L.js +2 -0
- package/client-dist/styles-3J7JD5YE.css +1 -0
- package/package.json +96 -0
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FAM_ClaudeMemImport_ControlService = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const crypto = tslib_1.__importStar(require("crypto"));
|
|
6
|
+
const fsm_dynamo_1 = require("@futdevpro/fsm-dynamo");
|
|
7
|
+
const fam_table_type_enum_1 = require("../../../_enums/fam-table.type-enum");
|
|
8
|
+
const error_codes_const_1 = require("../../../_collections/error-codes.const");
|
|
9
|
+
const errors_control_service_1 = require("../../../_routes/server/errors/errors.control-service");
|
|
10
|
+
const fam_error_factory_util_1 = require("../../../_collections/fam-error-factory.util");
|
|
11
|
+
const fam_entry_data_model_1 = require("../../../_models/data-models/fam-entry.data-model");
|
|
12
|
+
const embedding_1 = require("../../embedding");
|
|
13
|
+
const scope_reference_1 = require("../../scope-reference");
|
|
14
|
+
const fam_claude_mem_source_type_enum_1 = require("../_enums/fam-claude-mem-source.type-enum");
|
|
15
|
+
const fam_import_content_hash_util_1 = require("../_collections/fam-import-content-hash.util");
|
|
16
|
+
const fam_target_mapping_util_1 = require("../_collections/fam-target-mapping.util");
|
|
17
|
+
const fam_claude_mem_export_reader_service_1 = require("./fam-claude-mem-export-reader.service");
|
|
18
|
+
const fam_claude_mem_sqlite_reader_service_1 = require("./fam-claude-mem-sqlite-reader.service");
|
|
19
|
+
const fam_claude_mem_worker_reader_service_1 = require("./fam-claude-mem-worker-reader.service");
|
|
20
|
+
const fam_import_dedup_data_service_1 = require("./fam-import-dedup.data-service");
|
|
21
|
+
/** A preview `mappingPreview` mintájának mérete (nem a teljes lista — a `map` adja a teljeset). */
|
|
22
|
+
const PREVIEW_SAMPLE_SIZE = 10;
|
|
23
|
+
/** A claude-mem `project` raw név alapértelmezett scope-layer-e (dsgn-009 §4 — project-layer). */
|
|
24
|
+
const PROJECT_SCOPE_LAYER = 'project';
|
|
25
|
+
/**
|
|
26
|
+
* `FAM_ClaudeMemImport_ControlService` (MP-11, dsgn-009) — a claude-mem → FAM **egyirányú** migrációs
|
|
27
|
+
* engine. Singleton. A négy-lépéses, nem-destruktív folyamatot (preview → map → run → rollback)
|
|
28
|
+
* implementálja, a SP-11.1 reader-ekre (export-JSON / SQLite / worker-api) + a SP-11.2 dedup +
|
|
29
|
+
* target-mapping-re + a MP-2 embedding-re + a MP-3 scope-resolve-ra építve.
|
|
30
|
+
*
|
|
31
|
+
* **Egyirányúság (HARD, dsgn-009 §7):** SOHA nincs FDP→claude-mem export/reverse — ez az engine csak
|
|
32
|
+
* BEOLVAS és FAM-be ír; az `importSource`/`importSourceId` provenance (audit/dedup), nem
|
|
33
|
+
* vissza-szinkron.
|
|
34
|
+
*
|
|
35
|
+
* **FIGYELEM (memory: dynts_dataservice_eager_resolve):** NEM tartunk élő DataService mezőt — minden
|
|
36
|
+
* DB-művelet előtt lazy `new FAM_Entry_DataService`.
|
|
37
|
+
*/
|
|
38
|
+
class FAM_ClaudeMemImport_ControlService {
|
|
39
|
+
static _instance;
|
|
40
|
+
/** Default issuer (a migrációs hibák issuer-éhez; dsgn-008). */
|
|
41
|
+
issuer = 'FAM_ClaudeMemImport_ControlService';
|
|
42
|
+
static getInstance() {
|
|
43
|
+
if (!FAM_ClaudeMemImport_ControlService._instance) {
|
|
44
|
+
FAM_ClaudeMemImport_ControlService._instance = new FAM_ClaudeMemImport_ControlService();
|
|
45
|
+
}
|
|
46
|
+
return FAM_ClaudeMemImport_ControlService._instance;
|
|
47
|
+
}
|
|
48
|
+
// =========================================================================
|
|
49
|
+
// (1) PREVIEW — nem-destruktív (dsgn-009 §5.1)
|
|
50
|
+
// =========================================================================
|
|
51
|
+
/**
|
|
52
|
+
* `preview` (dsgn-009 §5.1) — SEMMIT nem ír. Beolvassa a forrást, megadja a counts / byType
|
|
53
|
+
* bontást, a dedup-előrejelzést (a SP-11.2 dedup-lépcsőit write nélkül futtatva), a projekt-scope
|
|
54
|
+
* feloldásokat (confidence-szel) és egy leképezési mintát. A scope-feloldás itt is a MP-3-on megy,
|
|
55
|
+
* de SOHA nem hoz létre scope-ot (preview = read-only — az auto-create-et kihagyjuk).
|
|
56
|
+
*/
|
|
57
|
+
async preview(request) {
|
|
58
|
+
const data = await this.readSource(request);
|
|
59
|
+
const includePrompts = this.resolveIncludePrompts(request);
|
|
60
|
+
const warnings = [...data.warnings];
|
|
61
|
+
// byType (observation type-eloszlás).
|
|
62
|
+
const byType = {};
|
|
63
|
+
for (const record of data.records) {
|
|
64
|
+
if (record.type) {
|
|
65
|
+
byType[record.type] = (byType[record.type] ?? 0) + 1;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// dedup-előrejelzés (write nélkül) + a leképezési minta összeállítása.
|
|
69
|
+
const dedup = { willImport: 0, willSkipExisting: 0, willSkipDuplicateContent: 0 };
|
|
70
|
+
const mappingPreview = [];
|
|
71
|
+
const previewUncertainty = [];
|
|
72
|
+
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
|
+
});
|
|
77
|
+
this.tallyDedup(dedup, verdict);
|
|
78
|
+
if (mappingPreview.length < PREVIEW_SAMPLE_SIZE) {
|
|
79
|
+
mappingPreview.push(plan);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// projektscope-feloldások (a forrás distinct project-jeire), preview-only.
|
|
83
|
+
const projectScopes = await this.previewProjectScopes(data, request);
|
|
84
|
+
return {
|
|
85
|
+
source: request.source ?? 'claude-mem',
|
|
86
|
+
from: this.resolveFrom(request),
|
|
87
|
+
counts: data.counts,
|
|
88
|
+
byType: byType,
|
|
89
|
+
dedup: dedup,
|
|
90
|
+
projectScopes: projectScopes,
|
|
91
|
+
mappingPreview: mappingPreview,
|
|
92
|
+
warnings: warnings,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
// =========================================================================
|
|
96
|
+
// (2) MAP — override (dsgn-009 §5.2) — STILL nem ír
|
|
97
|
+
// =========================================================================
|
|
98
|
+
/**
|
|
99
|
+
* `map` (dsgn-009 §5.2) — a §4 default leképezés felülírása (byType / byId / scope / includePrompts)
|
|
100
|
+
* + a teljes feloldott leképezési terv visszaadása. STILL nem ír. A scope-resolve itt is read-only
|
|
101
|
+
* (a tényleges auto-create a `run`-ban történik). Az `appliedOverrides` echo az átláthatósághoz.
|
|
102
|
+
*/
|
|
103
|
+
async map(request) {
|
|
104
|
+
const data = await this.readSource(request);
|
|
105
|
+
const warnings = [...data.warnings];
|
|
106
|
+
const uncertainty = [];
|
|
107
|
+
const plans = [];
|
|
108
|
+
for (const record of data.records) {
|
|
109
|
+
plans.push(await this.buildPlan({
|
|
110
|
+
record: record, request: request, allowCreateScope: false, uncertainty: uncertainty,
|
|
111
|
+
}));
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
source: request.source ?? 'claude-mem',
|
|
115
|
+
from: this.resolveFrom(request),
|
|
116
|
+
plans: plans,
|
|
117
|
+
appliedOverrides: request.overrides ?? {},
|
|
118
|
+
warnings: warnings,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
// =========================================================================
|
|
122
|
+
// (3) RUN / IMPORT — írás egy batchId alatt (dsgn-009 §5.3)
|
|
123
|
+
// =========================================================================
|
|
124
|
+
/**
|
|
125
|
+
* `run` (dsgn-009 §5.3) — a tényleges beszúrás egy **import-batch** (`batchId` = `ingestRunId`)
|
|
126
|
+
* alatt. Rekordonként: dedup → SKIP (NEM embeddel) VAGY map + persist (`importSource`/
|
|
127
|
+
* `importSourceId`/`contentHash`/`ingestRunId`) + embed (MP-2 `embedAndPersist`). A scope-resolve
|
|
128
|
+
* itt auto-create-elhet (`createdBy='agent-write'` a MP-3-ban). A per-rekord hiba NEM buktatja a
|
|
129
|
+
* batch-et (`status:'partial-failed'`, strukturált `errors[]`). MON-esemény a végén.
|
|
130
|
+
*/
|
|
131
|
+
async run(request) {
|
|
132
|
+
const batchId = crypto.randomUUID();
|
|
133
|
+
const data = await this.readSource(request);
|
|
134
|
+
const byTable = {};
|
|
135
|
+
const errors = [];
|
|
136
|
+
const uncertaintyNotes = [];
|
|
137
|
+
let ingested = 0;
|
|
138
|
+
let skipped = 0;
|
|
139
|
+
let failed = 0;
|
|
140
|
+
// Embedding-költség pillanatkép a batch ELŐTT (a delta a batch embed-költsége — BFR-AM-007).
|
|
141
|
+
const costBefore = embedding_1.FAM_EmbeddingCost_ControlService.getInstance().getStats().totalEstimatedCostUsd;
|
|
142
|
+
for (const record of data.records) {
|
|
143
|
+
try {
|
|
144
|
+
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,
|
|
149
|
+
});
|
|
150
|
+
if (verdict !== 'import') {
|
|
151
|
+
// A skip-eltek NEM embeddelődnek (cost-takarékosság — dsgn-009 §5.3 / §9.3).
|
|
152
|
+
skipped++;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
await this.persistAndEmbed({ record: record, plan: plan, batchId: batchId });
|
|
156
|
+
ingested++;
|
|
157
|
+
byTable[plan.table] = (byTable[plan.table] ?? 0) + 1;
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
failed++;
|
|
161
|
+
const famError = await errors_control_service_1.FAM_Error_ControlService.getInstance().emit({
|
|
162
|
+
errorCode: error_codes_const_1.FAM_ERROR_CODES.impMap,
|
|
163
|
+
message: `Egy claude-mem rekord importja sikertelen (entity='${record.entity}', `
|
|
164
|
+
+ `id='${record.sourceRowId}'). A batch a többi rekorddal folytatódik (partial-failed).`,
|
|
165
|
+
cause: error,
|
|
166
|
+
context: { operation: 'import-run', runId: batchId },
|
|
167
|
+
});
|
|
168
|
+
errors.push({
|
|
169
|
+
errorCode: fsm_dynamo_1.DyFM_Error.getErrorCode(famError),
|
|
170
|
+
message: fsm_dynamo_1.DyFM_Error.getErrorMessage(famError),
|
|
171
|
+
importSourceId: fam_target_mapping_util_1.FAM_TargetMapping_Util.importSourceId(record),
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const costAfter = embedding_1.FAM_EmbeddingCost_ControlService.getInstance().getStats().totalEstimatedCostUsd;
|
|
176
|
+
const status = this.runStatus(ingested, failed);
|
|
177
|
+
const result = {
|
|
178
|
+
ok: status !== 'failed',
|
|
179
|
+
batchId: batchId,
|
|
180
|
+
ingested: ingested,
|
|
181
|
+
skipped: skipped,
|
|
182
|
+
failed: failed,
|
|
183
|
+
byTable: byTable,
|
|
184
|
+
embedTokenCost: Math.max(costAfter - costBefore, 0),
|
|
185
|
+
status: status,
|
|
186
|
+
uncertaintyNotes: this.dedupUncertainty(uncertaintyNotes),
|
|
187
|
+
errors: errors,
|
|
188
|
+
};
|
|
189
|
+
this.emitMonEvent({ batchId: batchId, data: data, result: result });
|
|
190
|
+
return result;
|
|
191
|
+
}
|
|
192
|
+
// =========================================================================
|
|
193
|
+
// (4) ROLLBACK — pontosan egy batch soft-delete (dsgn-009 §5.4)
|
|
194
|
+
// =========================================================================
|
|
195
|
+
/**
|
|
196
|
+
* `rollback` (dsgn-009 §5.4) — pontosan EGY import-batch összes entry-jének soft-delete-elése az
|
|
197
|
+
* `ingestRunId == batchId` szűrőre, MINDEN fő táron. Más batch-et NEM érint. Idempotens: már
|
|
198
|
+
* visszavont batch → `reverted:0`. A soft-delete a `DyNTS_DataService.deleteData(id, false)`-on át
|
|
199
|
+
* (archív → history, dsgn-001 §8).
|
|
200
|
+
*/
|
|
201
|
+
async rollback(set) {
|
|
202
|
+
if (!set.batchId || !set.batchId.trim().length) {
|
|
203
|
+
throw fam_error_factory_util_1.FAM_Error_Util.create({
|
|
204
|
+
errorCode: error_codes_const_1.FAM_ERROR_CODES.impMap,
|
|
205
|
+
message: 'A `rollback_import`-hoz `batchId` kötelező (egy korábbi import-batch azonosítója).',
|
|
206
|
+
issuer: this.issuer,
|
|
207
|
+
context: { operation: 'import-rollback' },
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
const table = {};
|
|
211
|
+
let reverted = 0;
|
|
212
|
+
// Minden fő tár: a batch-hez tartozó (importSource + ingestRunId) entry-k soft-delete-je.
|
|
213
|
+
for (const registryEntry of this.allRegistryEntries()) {
|
|
214
|
+
const dataService = new embedding_1.FAM_Entry_DataService({ dataParams: registryEntry.dataParams, issuer: this.issuer });
|
|
215
|
+
const filter = {
|
|
216
|
+
importSource: fam_import_dedup_data_service_1.FAM_ImportDedup_DataService.IMPORT_SOURCE,
|
|
217
|
+
ingestRunId: set.batchId,
|
|
218
|
+
};
|
|
219
|
+
const batchEntries = await dataService.findHydratableList(filter);
|
|
220
|
+
for (const entry of batchEntries) {
|
|
221
|
+
if (entry._id) {
|
|
222
|
+
await dataService.deleteData(entry._id, false);
|
|
223
|
+
reverted++;
|
|
224
|
+
table[registryEntry.table] = (table[registryEntry.table] ?? 0) + 1;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return { ok: true, batchId: set.batchId, reverted: reverted, table: table };
|
|
229
|
+
}
|
|
230
|
+
// =========================================================================
|
|
231
|
+
// forrás-olvasás (SP-11.1 reader-dispatch)
|
|
232
|
+
// =========================================================================
|
|
233
|
+
/** A forrás-csatorna feloldása (`from`; default export-JSON). */
|
|
234
|
+
resolveFrom(request) {
|
|
235
|
+
return request.from ?? fam_claude_mem_source_type_enum_1.FAM_ClaudeMemSource_Type.exportJson;
|
|
236
|
+
}
|
|
237
|
+
/** A `includePrompts` feloldása (override > request; default false). */
|
|
238
|
+
resolveIncludePrompts(request) {
|
|
239
|
+
if (request.overrides && typeof request.overrides.includePrompts === 'boolean') {
|
|
240
|
+
return request.overrides.includePrompts;
|
|
241
|
+
}
|
|
242
|
+
return request.includePrompts === true;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* A forrás beolvasása a `from` szerinti reader-en (SP-11.1 dispatch). Az `includePrompts`-ot az
|
|
246
|
+
* override-ral összevontan adja át. A forrás-hibát a reader strukturáltan dobja (`FAM-IMP-SOURCE-001`).
|
|
247
|
+
*/
|
|
248
|
+
async readSource(request) {
|
|
249
|
+
const from = this.resolveFrom(request);
|
|
250
|
+
const includePrompts = this.resolveIncludePrompts(request);
|
|
251
|
+
const path = request.path ?? '';
|
|
252
|
+
switch (from) {
|
|
253
|
+
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 });
|
|
255
|
+
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 });
|
|
257
|
+
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 });
|
|
260
|
+
default:
|
|
261
|
+
throw fam_error_factory_util_1.FAM_Error_Util.create({
|
|
262
|
+
errorCode: error_codes_const_1.FAM_ERROR_CODES.impSource,
|
|
263
|
+
message: `Ismeretlen claude-mem forrás-csatorna: '${from}'. Megengedett: export-json | sqlite | worker-api.`,
|
|
264
|
+
issuer: this.issuer,
|
|
265
|
+
context: { operation: 'import-read-source' },
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
// =========================================================================
|
|
270
|
+
// leképezési-terv építés (SP-11.2: target-mapping + scope-resolve)
|
|
271
|
+
// =========================================================================
|
|
272
|
+
/**
|
|
273
|
+
* Egy rekord teljes leképezési terve (SP-11.2). A target-mapping (`FAM_TargetMapping_Util`) +
|
|
274
|
+
* a content-kompozíció + a `contentHash` + a `project`→canonical scope (MP-3). A `allowCreateScope`
|
|
275
|
+
* `false` (preview/map) → read-only scope-feloldás (auto-create kihagyva), `true` (run) →
|
|
276
|
+
* `resolveForWrite` (auto-create-elhet). A scope-feloldás van-match-bizonytalansága az `uncertainty`
|
|
277
|
+
* gyűjtőbe (NEM hiba).
|
|
278
|
+
*/
|
|
279
|
+
async buildPlan(set) {
|
|
280
|
+
const target = fam_target_mapping_util_1.FAM_TargetMapping_Util.resolveTarget({ record: set.record, overrides: set.request.overrides });
|
|
281
|
+
const content = fam_target_mapping_util_1.FAM_TargetMapping_Util.composeContent(set.record);
|
|
282
|
+
const contentHash = fam_import_content_hash_util_1.FAM_ImportContentHash_Util.hash({
|
|
283
|
+
narrative: set.record.narrative, text: set.record.text, facts: set.record.facts,
|
|
284
|
+
});
|
|
285
|
+
const importSourceId = fam_target_mapping_util_1.FAM_TargetMapping_Util.importSourceId(set.record);
|
|
286
|
+
const scopeResolve = await this.resolveScopeForRecord({
|
|
287
|
+
record: set.record, request: set.request, allowCreateScope: set.allowCreateScope,
|
|
288
|
+
});
|
|
289
|
+
for (const note of scopeResolve.uncertaintyNotes) {
|
|
290
|
+
set.uncertainty.push(note);
|
|
291
|
+
}
|
|
292
|
+
return {
|
|
293
|
+
importSourceId: importSourceId,
|
|
294
|
+
entity: set.record.entity,
|
|
295
|
+
sourceType: set.record.type,
|
|
296
|
+
table: target.table,
|
|
297
|
+
kind: target.kind,
|
|
298
|
+
tags: target.tags,
|
|
299
|
+
content: content,
|
|
300
|
+
contentHash: contentHash,
|
|
301
|
+
scopePath: scopeResolve.scopePath,
|
|
302
|
+
project: set.record.project,
|
|
303
|
+
uncertaintyNotes: scopeResolve.uncertaintyNotes,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* A `project` → canonical scope feloldása (dsgn-002 §3.2, dsgn-009 §4). A scope a raw `project`
|
|
308
|
+
* stringből (`project`-layer) — VAGY az override-ból (`scope['project:<name>']`). `allowCreateScope:
|
|
309
|
+
* false` (preview/map) → read-only feloldás: ha nem oldható fel meglévő scope-pá, üres scopePath +
|
|
310
|
+
* uncertainty (NEM hoz létre). `allowCreateScope: true` (run) → `resolveForWrite` (auto-create-elhet).
|
|
311
|
+
* Project nélküli rekord → üres scopePath (a memory-tár scope nélkül is írható import-provenance-szal).
|
|
312
|
+
*/
|
|
313
|
+
async resolveScopeForRecord(set) {
|
|
314
|
+
const rawScope = this.rawScopeForRecord(set.record, set.request);
|
|
315
|
+
if (!rawScope || !rawScope.length) {
|
|
316
|
+
return { scopePath: [], uncertaintyNotes: [] };
|
|
317
|
+
}
|
|
318
|
+
if (set.allowCreateScope) {
|
|
319
|
+
// run: a write-path teljes feloldása (auto-create-elhet — createdBy='agent-write' a MP-3-ban).
|
|
320
|
+
const resolve = await scope_reference_1.FAM_ScopeResolver_ControlService.getInstance().resolveForWrite(rawScope);
|
|
321
|
+
return { scopePath: resolve.scopePath, uncertaintyNotes: resolve.uncertaintyNotes };
|
|
322
|
+
}
|
|
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);
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Egy rekord nyers scope-lánca: az override `scope['project:<name>']` explicit `scopePath`-ja
|
|
329
|
+
* elsőbbséget élvez; egyébként a raw `project` egyetlen `project`-layer-ként. Project nélkül undefined.
|
|
330
|
+
*/
|
|
331
|
+
rawScopeForRecord(record, request) {
|
|
332
|
+
if (!record.project) {
|
|
333
|
+
return undefined;
|
|
334
|
+
}
|
|
335
|
+
const overrideKey = `project:${record.project}`;
|
|
336
|
+
const override = request.overrides?.scope?.[overrideKey];
|
|
337
|
+
if (override && override.scopePath.length) {
|
|
338
|
+
return override.scopePath.map((layer) => ({ layer: layer.layer, rawName: layer.rawName }));
|
|
339
|
+
}
|
|
340
|
+
return [{ layer: PROJECT_SCOPE_LAYER, rawName: record.project }];
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Read-only (preview/map) scope-feloldás: a MP-3 `traceResolution`-on át eldönti, feloldható-e a
|
|
344
|
+
* scope-lánc biztosan (`confident` minden layeren). Ha igen → a feloldott meglévő scope-lánc; ha
|
|
345
|
+
* nem (no-match / ambiguous), NEM hoz létre scope-ot → üres scopePath + uncertainty (a preview
|
|
346
|
+
* jelzi, a user a `map`-ben adhat explicit scope-ot). SOHA nem mutál.
|
|
347
|
+
*/
|
|
348
|
+
async previewResolveScope(rawScope) {
|
|
349
|
+
const trace = await scope_reference_1.FAM_ScopeResolver_ControlService.getInstance().traceResolution(rawScope, 'write-resolve');
|
|
350
|
+
const allConfident = trace.steps.length > 0 && trace.steps.every((step) => step.classification === 'confident');
|
|
351
|
+
if (!allConfident) {
|
|
352
|
+
const note = {
|
|
353
|
+
rawToken: rawScope.map((layer) => layer.rawName).join('>'),
|
|
354
|
+
normalizedForm: trace.steps.map((step) => step.normalizedForm).join('>'),
|
|
355
|
+
resolvedTo: '',
|
|
356
|
+
confidence: 0,
|
|
357
|
+
matchSource: 'scope-name',
|
|
358
|
+
alternatives: [],
|
|
359
|
+
};
|
|
360
|
+
return { scopePath: [], uncertaintyNotes: [note] };
|
|
361
|
+
}
|
|
362
|
+
// Minden layer confident → a döntött canonical-ek nem hordoznak scopeId-t a trace-ben; a
|
|
363
|
+
// preview a confidence-t jelzi, a tényleges scopeId-feloldás a run-ban (write-resolve) történik.
|
|
364
|
+
return { scopePath: [], uncertaintyNotes: [] };
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* A forrás distinct project-jeinek preview-feloldása (dsgn-009 §5.1 `projectScopes`). Read-only:
|
|
368
|
+
* a `traceResolution` confidence-e dönt (high/uncertain). SOHA nem hoz létre scope-ot.
|
|
369
|
+
*/
|
|
370
|
+
async previewProjectScopes(data, request) {
|
|
371
|
+
const projects = new Set();
|
|
372
|
+
for (const record of data.records) {
|
|
373
|
+
if (record.project) {
|
|
374
|
+
projects.add(record.project);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
for (const session of data.sessions) {
|
|
378
|
+
if (session.project) {
|
|
379
|
+
projects.add(session.project);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
const result = [];
|
|
383
|
+
for (const project of projects) {
|
|
384
|
+
const rawScope = this.rawScopeForRecord({ project: project }, request) ?? [{ layer: PROJECT_SCOPE_LAYER, rawName: project }];
|
|
385
|
+
const trace = await scope_reference_1.FAM_ScopeResolver_ControlService.getInstance().traceResolution(rawScope, 'write-resolve');
|
|
386
|
+
const confident = trace.steps.length > 0 && trace.steps.every((step) => step.classification === 'confident');
|
|
387
|
+
result.push({
|
|
388
|
+
project: project,
|
|
389
|
+
resolvedScopeId: null,
|
|
390
|
+
confidence: confident ? 'high' : 'uncertain',
|
|
391
|
+
note: confident ? undefined
|
|
392
|
+
: 'A scope nem oldható fel biztosan — a `run` auto-regisztrál egy új scope-ot, '
|
|
393
|
+
+ 'vagy adj explicit `scopePath`-t a `map` lépésben (`overrides.scope`).',
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
return result;
|
|
397
|
+
}
|
|
398
|
+
// =========================================================================
|
|
399
|
+
// persist + embed (SP-11.3 írás)
|
|
400
|
+
// =========================================================================
|
|
401
|
+
/**
|
|
402
|
+
* Egy rekord persistálása + embeddelése a leképezési terv szerint (SP-11.3). A `FAM_Entry` a
|
|
403
|
+
* mapping-content + tags + kind + scope + a provenance-/dedup-mezőkkel (`importSource`/
|
|
404
|
+
* `importSourceId`/`contentHash`/`ingestRunId`=batchId, `addedBy='import'`, `source.type='import'`).
|
|
405
|
+
* A memory-tárnál a session-info (`sessionId`/`runId`) is hidratálva. Mentés UTÁN auto-embed (MP-2).
|
|
406
|
+
*/
|
|
407
|
+
async persistAndEmbed(set) {
|
|
408
|
+
const registryEntry = embedding_1.FAM_StoreRegistry_Util.getEntry(set.plan.table);
|
|
409
|
+
if (!registryEntry) {
|
|
410
|
+
throw fam_error_factory_util_1.FAM_Error_Util.create({
|
|
411
|
+
errorCode: error_codes_const_1.FAM_ERROR_CODES.impMap,
|
|
412
|
+
message: `A leképezett cél-tár nem fogad tartalom-importot: '${set.plan.table}'.`,
|
|
413
|
+
issuer: this.issuer,
|
|
414
|
+
context: { operation: 'import-persist', table: set.plan.table },
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
const dataService = new embedding_1.FAM_Entry_DataService({ dataParams: registryEntry.dataParams, issuer: this.issuer });
|
|
418
|
+
const source = {
|
|
419
|
+
type: 'import',
|
|
420
|
+
path: set.record.filesModified[0] ?? undefined,
|
|
421
|
+
locator: set.plan.importSourceId,
|
|
422
|
+
};
|
|
423
|
+
const entry = new fam_entry_data_model_1.FAM_Entry({
|
|
424
|
+
table: set.plan.table,
|
|
425
|
+
kind: set.plan.kind,
|
|
426
|
+
tags: set.plan.tags,
|
|
427
|
+
content: set.plan.content,
|
|
428
|
+
contentHash: set.plan.contentHash,
|
|
429
|
+
scopePath: set.plan.scopePath,
|
|
430
|
+
embeddingStatus: 'pending',
|
|
431
|
+
addedBy: 'import',
|
|
432
|
+
source: source,
|
|
433
|
+
sourceFilePath: set.record.filesModified[0] ?? undefined,
|
|
434
|
+
ingestRunId: set.batchId,
|
|
435
|
+
importSource: fam_import_dedup_data_service_1.FAM_ImportDedup_DataService.IMPORT_SOURCE,
|
|
436
|
+
importSourceId: set.plan.importSourceId,
|
|
437
|
+
...this.memoryFields(set.record, set.plan.table),
|
|
438
|
+
});
|
|
439
|
+
const saved = await dataService.saveData(entry);
|
|
440
|
+
await embedding_1.FAM_EmbeddingPipeline_ControlService.getInstance().embedAndPersist({
|
|
441
|
+
table: set.plan.table, entry: saved, callType: 'embed-write',
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* A memory-tár session-hidratálása (dsgn-009 §4): a `sessionId`/`runId` per-tár memory-mezők a
|
|
446
|
+
* forrás session-azonosítóiból. Csak a `memory` tárnál releváns (a per-tár mezők máshol nincsenek);
|
|
447
|
+
* a Mongo a deklarálatlan mezőt nem írja, de defenzíven csak a memory-tárra adjuk hozzá.
|
|
448
|
+
*/
|
|
449
|
+
memoryFields(record, table) {
|
|
450
|
+
if (table !== fam_table_type_enum_1.FAM_Table.memory) {
|
|
451
|
+
return {};
|
|
452
|
+
}
|
|
453
|
+
const fields = {};
|
|
454
|
+
if (record.sdkSessionId) {
|
|
455
|
+
fields.sessionId = record.sdkSessionId;
|
|
456
|
+
}
|
|
457
|
+
if (record.claudeSessionId) {
|
|
458
|
+
fields.runId = record.claudeSessionId;
|
|
459
|
+
}
|
|
460
|
+
return fields;
|
|
461
|
+
}
|
|
462
|
+
// =========================================================================
|
|
463
|
+
// helpers
|
|
464
|
+
// =========================================================================
|
|
465
|
+
/** A dedup-verdikt számlálók aggregálása a preview-hoz. */
|
|
466
|
+
tallyDedup(dedup, verdict) {
|
|
467
|
+
if (verdict === 'import') {
|
|
468
|
+
dedup.willImport++;
|
|
469
|
+
}
|
|
470
|
+
else if (verdict === 'skip-existing') {
|
|
471
|
+
dedup.willSkipExisting++;
|
|
472
|
+
}
|
|
473
|
+
else {
|
|
474
|
+
dedup.willSkipDuplicateContent++;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
/** A run-állapot: minden bukott → `failed`; van bukott de van beírt is → `partial-failed`; egyébként `ok`. */
|
|
478
|
+
runStatus(ingested, failed) {
|
|
479
|
+
if (failed && !ingested) {
|
|
480
|
+
return 'failed';
|
|
481
|
+
}
|
|
482
|
+
return failed ? 'partial-failed' : 'ok';
|
|
483
|
+
}
|
|
484
|
+
/** A bizonytalansági jegyzetek dedup-ja (rawToken+resolvedTo szerint) — a UI ne ismételjen. */
|
|
485
|
+
dedupUncertainty(notes) {
|
|
486
|
+
const seen = new Set();
|
|
487
|
+
const result = [];
|
|
488
|
+
for (const note of notes) {
|
|
489
|
+
const key = `${note.rawToken}→${note.resolvedTo}`;
|
|
490
|
+
if (!seen.has(key)) {
|
|
491
|
+
seen.add(key);
|
|
492
|
+
result.push(note);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
return result;
|
|
496
|
+
}
|
|
497
|
+
/** A 6 fő tár store-registry bejegyzései (a rollback minden tárán végigmegy). */
|
|
498
|
+
allRegistryEntries() {
|
|
499
|
+
const tables = [
|
|
500
|
+
fam_table_type_enum_1.FAM_Table.rules, fam_table_type_enum_1.FAM_Table.documents, fam_table_type_enum_1.FAM_Table.codebase,
|
|
501
|
+
fam_table_type_enum_1.FAM_Table.knowledge, fam_table_type_enum_1.FAM_Table.codingPatterns, fam_table_type_enum_1.FAM_Table.memory,
|
|
502
|
+
];
|
|
503
|
+
const entries = [];
|
|
504
|
+
for (const table of tables) {
|
|
505
|
+
const entry = embedding_1.FAM_StoreRegistry_Util.getEntry(table);
|
|
506
|
+
if (entry) {
|
|
507
|
+
entries.push(entry);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
return entries;
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* A `famImport` MON-esemény a batch végén (dsgn-009 §5.3; a CCAP `ragIngest` mintára). MVP1: a
|
|
514
|
+
* `DyFM_Log.info`-n strukturált payload-dal (a Logs-routing wiring KÉSŐBBI MP — NEM rántjuk előre,
|
|
515
|
+
* az ingest MON-eseménnyel azonos minta). A payload shape a dsgn-009 §5.3 szerinti.
|
|
516
|
+
*/
|
|
517
|
+
emitMonEvent(set) {
|
|
518
|
+
fsm_dynamo_1.DyFM_Log.info('[famImport]', {
|
|
519
|
+
event: 'famImport',
|
|
520
|
+
source: 'claude-mem',
|
|
521
|
+
from: set.data.from,
|
|
522
|
+
batchId: set.batchId,
|
|
523
|
+
chunkCount: set.result.ingested + set.result.skipped + set.result.failed,
|
|
524
|
+
ingested: set.result.ingested,
|
|
525
|
+
skipped: set.result.skipped,
|
|
526
|
+
failed: set.result.failed,
|
|
527
|
+
byTable: set.result.byTable,
|
|
528
|
+
embedTokenCost: set.result.embedTokenCost,
|
|
529
|
+
status: set.result.status,
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
exports.FAM_ClaudeMemImport_ControlService = FAM_ClaudeMemImport_ControlService;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FAM_ClaudeMemSqliteReader_Service = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const fs = tslib_1.__importStar(require("fs"));
|
|
6
|
+
const better_sqlite3_1 = tslib_1.__importDefault(require("better-sqlite3"));
|
|
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_normalize_util_1 = require("../_collections/fam-claude-mem-normalize.util");
|
|
11
|
+
/**
|
|
12
|
+
* `FAM_ClaudeMemSqliteReader_Service` (SP-11.1) — a **SQLite reader** (`from=sqlite`, BFR-AM-009
|
|
13
|
+
* fallback). A claude-mem `~/.claude-mem/claude-mem.db` táblái (`observations` / `session_summaries` /
|
|
14
|
+
* `user_prompts` / `sdk_sessions`) közvetlen, read-only olvasása egy IZOLÁLT, build-only natív
|
|
15
|
+
* függőséggel (`better-sqlite3`) — a Dynamo-stack Mongo-alapú, ezért a SQLite a projekt-lokális
|
|
16
|
+
* adapterben él. A reader UGYANAZT a normalizált `FAM_ClaudeMemRecord` halmazt adja, mint az
|
|
17
|
+
* export-JSON ugyanarra a tartalomra (paritás — dsgn-009 §9.2).
|
|
18
|
+
*
|
|
19
|
+
* **Csere-pont (BFR-AM-009, MP-15):** amikor a bedrock `DyNTS_Sqlite_Reader_Util` landol, CSAK ennek a
|
|
20
|
+
* service-nek a tábla-olvasó belseje cserélődik (a `readTable`), az adapter-interfész (`read`) és a
|
|
21
|
+
* normalizáló-szerződés (`FAM_ClaudeMemNormalize_Util`) változatlan marad — a dedup/mapping (SP-11.2)
|
|
22
|
+
* érintetlen.
|
|
23
|
+
*
|
|
24
|
+
* **Fail-soft (dsgn-009 §9):** fájl-hiány / megnyitás-hiba / hiányzó tábla → tiszta strukturált hiba
|
|
25
|
+
* (`FAM-IMP-SOURCE-001`), NEM hang/crash. A DB mindig zárul (try/finally).
|
|
26
|
+
*/
|
|
27
|
+
class FAM_ClaudeMemSqliteReader_Service {
|
|
28
|
+
static _instance;
|
|
29
|
+
/** Default issuer (a forrás-hibák issuer-éhez; dsgn-008). */
|
|
30
|
+
issuer = 'FAM_ClaudeMemSqliteReader_Service';
|
|
31
|
+
static getInstance() {
|
|
32
|
+
if (!FAM_ClaudeMemSqliteReader_Service._instance) {
|
|
33
|
+
FAM_ClaudeMemSqliteReader_Service._instance = new FAM_ClaudeMemSqliteReader_Service();
|
|
34
|
+
}
|
|
35
|
+
return FAM_ClaudeMemSqliteReader_Service._instance;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* A SQLite DB read-only megnyitása + a 4 tábla olvasása + normalizálás (SP-11.1). A `path` a
|
|
39
|
+
* `.db` fájl útvonala. Megnyitás-/olvasás-hiba → `FAM-IMP-SOURCE-001` (deskriptív). A hiányzó
|
|
40
|
+
* táblát üres listaként kezeli + warning (a séma drift-elhet, nem-néma drop).
|
|
41
|
+
*/
|
|
42
|
+
read(set) {
|
|
43
|
+
if (!set.path || !set.path.trim().length) {
|
|
44
|
+
throw fam_error_factory_util_1.FAM_Error_Util.create({
|
|
45
|
+
errorCode: error_codes_const_1.FAM_ERROR_CODES.impSource,
|
|
46
|
+
message: 'A SQLite importhoz `path` kötelező (a claude-mem `.db` fájl útvonala, '
|
|
47
|
+
+ 'pl. `~/.claude-mem/claude-mem.db`).',
|
|
48
|
+
issuer: this.issuer,
|
|
49
|
+
context: { operation: 'import-read-sqlite' },
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
if (!fs.existsSync(set.path)) {
|
|
53
|
+
throw fam_error_factory_util_1.FAM_Error_Util.create({
|
|
54
|
+
errorCode: error_codes_const_1.FAM_ERROR_CODES.impSource,
|
|
55
|
+
message: `A claude-mem SQLite DB nem található: '${set.path}'.`,
|
|
56
|
+
issuer: this.issuer,
|
|
57
|
+
context: { operation: 'import-read-sqlite' },
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
const warnings = [];
|
|
61
|
+
let db = undefined;
|
|
62
|
+
try {
|
|
63
|
+
db = new better_sqlite3_1.default(set.path, { readonly: true, fileMustExist: true });
|
|
64
|
+
const observations = this.readTable(db, 'observations', warnings);
|
|
65
|
+
const summaries = this.readTable(db, 'session_summaries', warnings);
|
|
66
|
+
const prompts = this.readTable(db, 'user_prompts', warnings);
|
|
67
|
+
const sessions = this.readTable(db, 'sdk_sessions', warnings);
|
|
68
|
+
const normalized = fam_claude_mem_normalize_util_1.FAM_ClaudeMemNormalize_Util.normalizeAll({
|
|
69
|
+
from: fam_claude_mem_source_type_enum_1.FAM_ClaudeMemSource_Type.sqlite,
|
|
70
|
+
observations: observations.map((row) => this.hydrateObservation(row)),
|
|
71
|
+
summaries: summaries, prompts: prompts, sessions: sessions,
|
|
72
|
+
includePrompts: set.includePrompts,
|
|
73
|
+
});
|
|
74
|
+
normalized.warnings = [...warnings, ...normalized.warnings];
|
|
75
|
+
return normalized;
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
// A normalizáló dobott FAM-hibát NEM csomagoljuk újra (már strukturált).
|
|
79
|
+
if (error && typeof error === 'object' && 'errorCode' in error) {
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
throw fam_error_factory_util_1.FAM_Error_Util.create({
|
|
83
|
+
errorCode: error_codes_const_1.FAM_ERROR_CODES.impSource,
|
|
84
|
+
message: `A claude-mem SQLite DB megnyitása/olvasása sikertelen: '${set.path}'.`,
|
|
85
|
+
issuer: this.issuer,
|
|
86
|
+
context: { operation: 'import-read-sqlite' },
|
|
87
|
+
cause: error,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
if (db) {
|
|
92
|
+
db.close();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Egy tábla összes sorának read-only olvasása (a BFR-AM-009 bedrock-helper cserélheti). A hiányzó
|
|
98
|
+
* tábla NEM hiba (a séma drift-elhet) → warning + []. A `concepts`/`files_*` JSON-string-oszlopok
|
|
99
|
+
* a `hydrateObservation`-ben oldódnak fel (a SQLite skalár-oszlopokat ad vissza).
|
|
100
|
+
*/
|
|
101
|
+
readTable(db, table, warnings) {
|
|
102
|
+
if (!this.tableExists(db, table)) {
|
|
103
|
+
warnings.push(`A '${table}' tábla nem létezik a SQLite DB-ben — üres listaként kezelve (séma-drift?).`);
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
const rows = db.prepare(`SELECT * FROM ${table}`).all();
|
|
107
|
+
return rows;
|
|
108
|
+
}
|
|
109
|
+
/** Egy tábla létezésének read-only ellenőrzése (`sqlite_master`). */
|
|
110
|
+
tableExists(db, table) {
|
|
111
|
+
const found = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(table);
|
|
112
|
+
return Boolean(found);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Egy SQLite observation-sor hidratálása: a `concepts`/`files_read`/`files_modified` a SQLite-ban
|
|
116
|
+
* JSON-string-oszlopként élnek (a Mongo/export tömb-ekvivalense) — itt tömbökre parse-oljuk, hogy
|
|
117
|
+
* a normalizált rekord a 3 forrásra azonos legyen (paritás). Hibás JSON → üres tömb (defenzív).
|
|
118
|
+
*/
|
|
119
|
+
hydrateObservation(row) {
|
|
120
|
+
return {
|
|
121
|
+
...row,
|
|
122
|
+
concepts: this.parseJsonArray(row.concepts),
|
|
123
|
+
files_read: this.parseJsonArray(row.files_read),
|
|
124
|
+
files_modified: this.parseJsonArray(row.files_modified),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/** Egy érték string-tömbként: már-tömb → ahogy van; JSON-string → parse; egyéb → []. */
|
|
128
|
+
parseJsonArray(value) {
|
|
129
|
+
if (Array.isArray(value)) {
|
|
130
|
+
return value.filter((item) => typeof item === 'string');
|
|
131
|
+
}
|
|
132
|
+
if (typeof value !== 'string' || !value.trim().length) {
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
const parsed = JSON.parse(value);
|
|
137
|
+
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === 'string') : [];
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return [];
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
exports.FAM_ClaudeMemSqliteReader_Service = FAM_ClaudeMemSqliteReader_Service;
|