noteconnection 1.8.0 → 1.9.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 +257 -1803
- package/dist/src/agent_workspace.contract.parity.test.js +105 -421
- package/dist/src/agent_workspace.frontend.test.js +410 -11
- package/dist/src/android.knowledgebase.picker.contract.test.js +102 -0
- package/dist/src/appimage.patchelf.test.js +113 -0
- package/dist/src/appimage.portability.test.js +156 -0
- package/dist/src/backend/FileLoader.js +18 -4
- package/dist/src/backend/FileLoader.test.js +125 -0
- package/dist/src/backend/GraphBuilder.identity.test.js +86 -0
- package/dist/src/backend/GraphBuilder.js +66 -18
- package/dist/src/backend/GraphBuilder.matching.test.js +127 -0
- package/dist/src/backend/GraphMetrics.js +0 -3
- package/dist/src/backend/ResourceIdentity.js +155 -0
- package/dist/src/backend/ResourceIdentity.test.js +113 -0
- package/dist/src/backend/algorithms/CycleDetection.js +0 -1
- package/dist/src/backend/algorithms/HybridEngine.js +0 -3
- package/dist/src/backend/algorithms/LayoutEngine.js +0 -1
- package/dist/src/backend/algorithms/StatisticalAnalyzer.js +0 -4
- package/dist/src/backend/algorithms/TopologicalSort.js +0 -2
- package/dist/src/backend/build_dag.js +0 -1
- package/dist/src/capacitor.bridge.serialization.contract.test.js +3 -0
- package/dist/src/capacitor.runtime.contract.test.js +11 -2
- package/dist/src/core/Graph.js +138 -42
- package/dist/src/core/Graph.test.js +74 -0
- package/dist/src/core/NoteConnection.js +2 -2
- package/dist/src/core/PathBridge.js +265 -2
- package/dist/src/core/ResourceReference.js +17 -0
- package/dist/src/desktop.godot.pack.test.js +142 -0
- package/dist/src/desktop.runtime.graph.test.js +66 -0
- package/dist/src/export/WorkspaceExportBundle.js +44 -0
- package/dist/src/export/WorkspaceExportBundle.test.js +61 -0
- package/dist/src/foundation.release.evidence.contract.test.js +127 -9
- package/dist/src/foundation.sqlite.runtime.contract.test.js +6 -0
- package/dist/src/frontend/README.md +257 -1803
- package/dist/src/frontend/agent_workspace.js +665 -7
- package/dist/src/frontend/index.html +31 -2
- package/dist/src/frontend/knowledge_projection_contract.js +230 -0
- package/dist/src/frontend/knowledge_projection_store.js +214 -0
- package/dist/src/frontend/libs/path_core.js +158 -42
- package/dist/src/frontend/locales/en.json +32 -0
- package/dist/src/frontend/locales/zh.json +32 -0
- package/dist/src/frontend/mobile_budget_runtime.js +33 -0
- package/dist/src/frontend/mobile_exact_analyzer.js +393 -0
- package/dist/src/frontend/mobile_identity_contract.js +221 -0
- package/dist/src/frontend/mobile_semantic_comparator.js +264 -0
- package/dist/src/frontend/path.html +6 -2
- package/dist/src/frontend/simulationWorker.js +1 -1
- package/dist/src/frontend/source_manager.js +110 -4
- package/dist/src/frontend/storage_provider.js +661 -71
- package/dist/src/frontend/styles.css +107 -13
- package/dist/src/frontend/workspace_panes.js +111 -7
- package/dist/src/frontend.runtime.assets.contract.test.js +81 -0
- package/dist/src/graph.answer.operator.contract.test.js +72 -0
- package/dist/src/index.js +6 -2
- package/dist/src/index.knowledge-root.test.js +118 -0
- package/dist/src/indexing/IndexLifecycle.js +22 -0
- package/dist/src/indexing/IndexLifecycle.test.js +24 -0
- package/dist/src/knowledge.api.contract.test.js +4 -2
- package/dist/src/knowledge_projection.contract.test.js +134 -0
- package/dist/src/knowledge_projection_store.test.js +200 -0
- package/dist/src/learning/AnswerQualityEvaluation.js +153 -0
- package/dist/src/learning/AnswerQualityEvaluation.test.js +118 -0
- package/dist/src/learning/KnowledgeLearningPlatform.js +4687 -3892
- package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +418 -0
- package/dist/src/learning/KnowledgeLearningPlatform.test.js +243 -6
- package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +25 -1
- package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +13 -5
- package/dist/src/learning/MeasurementEvidenceConflicts.test.js +78 -0
- package/dist/src/learning/PublicAnswerContract.test.js +85 -0
- package/dist/src/learning/PublicEvidenceSelection.test.js +94 -0
- package/dist/src/learning/PublicMeasurementDisclosure.test.js +54 -0
- package/dist/src/learning/agentConversationExecution.js +211 -0
- package/dist/src/learning/agentConversationExecution.test.js +209 -0
- package/dist/src/learning/agentConversationSerialization.js +266 -0
- package/dist/src/learning/agentConversationSerialization.test.js +165 -0
- package/dist/src/learning/agentResponseBudget.js +179 -0
- package/dist/src/learning/agentResponseBudget.test.js +97 -0
- package/dist/src/learning/agentWorkspaceCapabilityContracts.js +65 -0
- package/dist/src/learning/answerReleaseReview.js +1191 -250
- package/dist/src/learning/answerReleaseReview.test.js +1293 -7
- package/dist/src/learning/answerTaskPlan.js +473 -0
- package/dist/src/learning/conversationComposer.js +627 -386
- package/dist/src/learning/conversationComposer.test.js +1403 -0
- package/dist/src/learning/evidenceContextAssembler.js +312 -120
- package/dist/src/learning/graphAnswerCoverage.js +37 -0
- package/dist/src/learning/graphAnswerCoverage.test.js +93 -0
- package/dist/src/learning/graphAnswerCoverageCalibration.js +59 -0
- package/dist/src/learning/graphAnswerCoverageCalibration.test.js +46 -0
- package/dist/src/learning/graphAnswerFacts.js +94 -0
- package/dist/src/learning/graphAnswerFacts.test.js +39 -0
- package/dist/src/learning/graphAnswerPlan.js +908 -0
- package/dist/src/learning/graphAnswerPlan.test.js +907 -0
- package/dist/src/learning/graphAnswerQualityCalibration.js +255 -0
- package/dist/src/learning/graphAnswerQualityCalibration.test.js +72 -0
- package/dist/src/learning/graphAnswerQualityPolicy.js +30 -0
- package/dist/src/learning/graphClaimMatcher.js +104 -0
- package/dist/src/learning/graphConditionedContext.js +109 -0
- package/dist/src/learning/graphConditionedContext.test.js +99 -0
- package/dist/src/learning/graphContextAssembler.js +3 -13
- package/dist/src/learning/graphContextAssembler.test.js +2 -2
- package/dist/src/learning/graphExpansionPolicy.js +22 -0
- package/dist/src/learning/graphExpansionPolicy.test.js +33 -0
- package/dist/src/learning/identityMetadata.test.js +26 -0
- package/dist/src/learning/index.js +1 -0
- package/dist/src/learning/mobileAnswerProjection.js +104 -0
- package/dist/src/learning/mobileAnswerProjection.test.js +102 -0
- package/dist/src/learning/queryBackend.js +34 -10
- package/dist/src/learning/queryBackend.test.js +21 -0
- package/dist/src/learning/ragContextPack.js +115 -16
- package/dist/src/learning/ragContextPack.test.js +151 -0
- package/dist/src/learning/ragEvidenceQuality.js +96 -0
- package/dist/src/learning/ragEvidenceQuality.test.js +29 -0
- package/dist/src/learning/ragPublicText.js +67 -4
- package/dist/src/learning/ragPublicText.test.js +57 -0
- package/dist/src/learning/ragSufficiencyJudge.js +11 -1
- package/dist/src/learning/ragSufficiencyProviderJudge.js +16 -26
- package/dist/src/learning/ragSufficiencyProviderJudge.test.js +51 -0
- package/dist/src/learning/requestNormalization.js +106 -0
- package/dist/src/learning/requestNormalization.test.js +101 -0
- package/dist/src/learning/store.js +116 -12
- package/dist/src/learning/store.test.js +299 -0
- package/dist/src/learning/vectorAccelerationAdapter.js +22 -6
- package/dist/src/learning/vectorAccelerationAdapter.test.js +30 -0
- package/dist/src/learning/workspaceHydration.js +168 -0
- package/dist/src/learning/workspaceHydration.test.js +30 -0
- package/dist/src/license.policy.contract.test.js +9 -4
- package/dist/src/markdown/MarkdownGateway.js +4 -0
- package/dist/src/markdown/MarkdownGateway.test.js +59 -0
- package/dist/src/middleware/SseResponseWriter.js +122 -0
- package/dist/src/middleware/SseResponseWriter.test.js +54 -0
- package/dist/src/middleware/auth.js +28 -7
- package/dist/src/middleware/auth.test.js +33 -0
- package/dist/src/middleware/index.js +2 -1
- package/dist/src/mobile.android.evidence.contract.test.js +146 -0
- package/dist/src/mobile.android.signing.contract.test.js +66 -0
- package/dist/src/mobile.artifact.contract.test.js +144 -0
- package/dist/src/mobile.budget.contract.test.js +90 -0
- package/dist/src/mobile.canonical-id.readiness.test.js +54 -0
- package/dist/src/mobile.graph.parity.contract.test.js +207 -0
- package/dist/src/mobile.identity.contract.test.js +66 -0
- package/dist/src/mobile.identity.corpus.manifest.test.js +72 -0
- package/dist/src/mobile.identity.corpus.test.js +40 -0
- package/dist/src/mobile.native.recovery.contract.test.js +64 -0
- package/dist/src/mobile.pipeline.test.js +11 -1
- package/dist/src/mobile.profile.contract.test.js +225 -0
- package/dist/src/mobile.semantic.parity.test.js +92 -0
- package/dist/src/mobile_exact_analyzer.test.js +113 -0
- package/dist/src/notemd/ContentGenerator.js +4 -9
- package/dist/src/notemd/FileProcessor.js +4 -9
- package/dist/src/notemd/NotemdService.js +12 -17
- package/dist/src/notemd/Translator.js +4 -9
- package/dist/src/notemd/progressReporter.js +9 -0
- package/dist/src/notemd.server.integration.test.js +119 -4
- package/dist/src/notemd.workflow.pipeline.test.js +1 -1
- package/dist/src/pathbridge.handshake.contract.test.js +29 -0
- package/dist/src/pathbridge.host-adapter.contract.test.js +133 -0
- package/dist/src/pathcore.bundle.contract.test.js +8 -0
- package/dist/src/platform/ExportProfile.js +28 -0
- package/dist/src/platform/MobileBudget.js +11 -0
- package/dist/src/platform/PlatformCapabilities.js +14 -0
- package/dist/src/platform/PlatformCapabilities.test.js +6 -0
- package/dist/src/query_backend.external_http.integration.test.js +2 -2
- package/dist/src/release.godot.mirror.contract.test.js +28 -4
- package/dist/src/resources/ResourceRegistry.js +40 -0
- package/dist/src/resources/ResourceRegistry.test.js +36 -0
- package/dist/src/routes/index.js +0 -4
- package/dist/src/routes/knowledge.js +46 -33
- package/dist/src/routes/knowledgePayload.js +185 -0
- package/dist/src/routes/knowledgePayload.test.js +53 -0
- package/dist/src/routes/markdown.js +8 -0
- package/dist/src/routes/notemd.js +990 -234
- package/dist/src/routes/registry.contract.test.js +5 -2
- package/dist/src/routes/registry.shadow.contract.test.js +22 -0
- package/dist/src/routes/render.js +14 -51
- package/dist/src/runtime.capabilities.test.js +18 -2
- package/dist/src/server.js +566 -1090
- package/dist/src/server.migration.test.js +417 -15
- package/dist/src/sidecar.build.lock.contract.test.js +201 -0
- package/dist/src/sidecar.freshness.contract.test.js +184 -0
- package/dist/src/sidecar.replacement.boundary.contract.test.js +6 -3
- package/dist/src/sidecar.supply.readiness.contract.test.js +11 -0
- package/dist/src/simulation.worker.offline.test.js +104 -0
- package/dist/src/storage.provider.capacitor.content.contract.test.js +4 -1
- package/dist/src/storage.provider.contract.test.js +152 -1
- package/dist/src/tauri.frontend.build.contract.test.js +8 -0
- package/dist/src/tauri.window.evidence.execution.test.js +68 -0
- package/dist/src/utils/RuntimePaths.js +6 -0
- package/dist/src/utils/RuntimePaths.test.js +27 -0
- package/dist/src/windows.installer.build.test.js +109 -0
- package/dist/src/windows.installer.payload.test.js +76 -0
- package/dist/src/windows.installer.runtime.test.js +69 -0
- package/dist/src/workspace/WorkspaceRegistry.js +13 -0
- package/package.json +32 -10
- package/dist/src/routes/data.js +0 -267
- package/dist/src/routes/diagnostics.js +0 -51
|
@@ -38,12 +38,14 @@ exports.isOpsAdapter = isOpsAdapter;
|
|
|
38
38
|
exports.createFileBackedKnowledgeGraphStore = createFileBackedKnowledgeGraphStore;
|
|
39
39
|
exports.normalizeKnowledgeGraphStoreBackend = normalizeKnowledgeGraphStoreBackend;
|
|
40
40
|
exports.normalizeGraphDbSnapshotAdapterProvider = normalizeGraphDbSnapshotAdapterProvider;
|
|
41
|
+
exports.normalizeStorageProviderKind = normalizeStorageProviderKind;
|
|
41
42
|
exports.normalizeGraphDbStoreOperationMode = normalizeGraphDbStoreOperationMode;
|
|
42
43
|
exports.createFileGraphDbSnapshotAdapter = createFileGraphDbSnapshotAdapter;
|
|
43
44
|
exports.createGraphDbSnapshotAdapter = createGraphDbSnapshotAdapter;
|
|
44
45
|
exports.createKnowledgeGraphStore = createKnowledgeGraphStore;
|
|
45
46
|
const fs = __importStar(require("fs"));
|
|
46
47
|
const path = __importStar(require("path"));
|
|
48
|
+
const crypto_1 = require("crypto");
|
|
47
49
|
function filterNodesFromSnapshot(snapshot, filter) {
|
|
48
50
|
let results = snapshot.atoms;
|
|
49
51
|
if (filter.nodeIds && filter.nodeIds.length > 0) {
|
|
@@ -122,6 +124,8 @@ class FileBackedKnowledgeGraphStore {
|
|
|
122
124
|
this.loaded = false;
|
|
123
125
|
/** Cached snapshot for operation-level queries without re-reading from disk. */
|
|
124
126
|
this.cachedSnapshot = null;
|
|
127
|
+
/** Serializes complete replace transactions; concurrent rename is not portable on Windows. */
|
|
128
|
+
this.pendingSave = Promise.resolve();
|
|
125
129
|
}
|
|
126
130
|
// ── M10.5: Operational Semantics ──
|
|
127
131
|
getCapabilities() {
|
|
@@ -172,14 +176,17 @@ class FileBackedKnowledgeGraphStore {
|
|
|
172
176
|
if ((parsed.schemaVersion !== 1 && parsed.schemaVersion !== 2) || !Array.isArray(parsed.atoms) || !Array.isArray(parsed.documents)) {
|
|
173
177
|
throw new Error('Invalid knowledge graph snapshot schema.');
|
|
174
178
|
}
|
|
179
|
+
const snapshot = parsed;
|
|
180
|
+
this.cachedSnapshot = snapshot;
|
|
175
181
|
this.loaded = true;
|
|
176
182
|
this.lastLoadAt = new Date().toISOString();
|
|
177
|
-
return
|
|
183
|
+
return snapshot;
|
|
178
184
|
}
|
|
179
185
|
catch (error) {
|
|
180
186
|
const code = error?.code;
|
|
181
187
|
if (code === 'ENOENT' || code === 'ENOTDIR') {
|
|
182
188
|
this.loaded = false;
|
|
189
|
+
this.cachedSnapshot = null;
|
|
183
190
|
this.lastError = undefined;
|
|
184
191
|
return null;
|
|
185
192
|
}
|
|
@@ -188,15 +195,21 @@ class FileBackedKnowledgeGraphStore {
|
|
|
188
195
|
throw error;
|
|
189
196
|
}
|
|
190
197
|
}
|
|
191
|
-
|
|
198
|
+
saveSnapshot(snapshot) {
|
|
199
|
+
const saveOperation = this.pendingSave.then(() => this.persistSnapshot(snapshot));
|
|
200
|
+
this.pendingSave = saveOperation.catch(() => undefined);
|
|
201
|
+
return saveOperation;
|
|
202
|
+
}
|
|
203
|
+
async persistSnapshot(snapshot) {
|
|
192
204
|
const filePath = path.resolve(this.options.filePath);
|
|
193
205
|
const directory = path.dirname(filePath);
|
|
194
|
-
const tempPath = `${filePath}.tmp`;
|
|
206
|
+
const tempPath = `${filePath}.${process.pid}.${Date.now().toString(36)}.${(0, crypto_1.randomUUID)()}.tmp`;
|
|
195
207
|
await fs.promises.mkdir(directory, { recursive: true });
|
|
196
208
|
const serialized = JSON.stringify(snapshot, null, 2);
|
|
197
209
|
try {
|
|
198
210
|
await fs.promises.writeFile(tempPath, serialized, 'utf8');
|
|
199
211
|
await fs.promises.rename(tempPath, filePath);
|
|
212
|
+
this.cachedSnapshot = snapshot;
|
|
200
213
|
this.lastSaveAt = new Date().toISOString();
|
|
201
214
|
this.lastError = undefined;
|
|
202
215
|
}
|
|
@@ -231,6 +244,9 @@ class FileBackedKnowledgeGraphStore {
|
|
|
231
244
|
location: filePath,
|
|
232
245
|
exists: fs.existsSync(filePath),
|
|
233
246
|
loaded: this.loaded,
|
|
247
|
+
requestedProvider: 'file',
|
|
248
|
+
resolvedProvider: 'file',
|
|
249
|
+
storageEngine: 'file',
|
|
234
250
|
lastLoadAt: this.lastLoadAt,
|
|
235
251
|
lastSaveAt: this.lastSaveAt,
|
|
236
252
|
lastError: this.lastError,
|
|
@@ -297,6 +313,26 @@ function normalizeGraphDbSnapshotAdapterProvider(v) {
|
|
|
297
313
|
return 'file';
|
|
298
314
|
return 'file';
|
|
299
315
|
}
|
|
316
|
+
/**
|
|
317
|
+
* Normalize the product-level storage vocabulary without leaking adapter
|
|
318
|
+
* implementation names into cross-platform diagnostics.
|
|
319
|
+
*/
|
|
320
|
+
function normalizeStorageProviderKind(v) {
|
|
321
|
+
const normalized = String(v ?? '').trim().toLowerCase();
|
|
322
|
+
if (normalized === 'sqlite' || normalized === 'embedded_sqlite' || normalized === 'embedded-sqlite' || normalized === 'embedded') {
|
|
323
|
+
return 'sqlite';
|
|
324
|
+
}
|
|
325
|
+
if (normalized === 'file' || normalized === 'local-file' || normalized === 'snapshot') {
|
|
326
|
+
return 'file';
|
|
327
|
+
}
|
|
328
|
+
if (normalized === 'http' || normalized === 'external_http' || normalized === 'remote-http' || normalized === 'service' || normalized === 'remote') {
|
|
329
|
+
return 'remote';
|
|
330
|
+
}
|
|
331
|
+
if (normalized === 'projection' || normalized === 'mobile_projection' || normalized === 'mobile-slim') {
|
|
332
|
+
return 'projection';
|
|
333
|
+
}
|
|
334
|
+
return undefined;
|
|
335
|
+
}
|
|
300
336
|
function normalizeGraphDbStoreOperationMode(v) {
|
|
301
337
|
const s = String(v ?? 'snapshot').trim().toLowerCase();
|
|
302
338
|
if (s === 'snapshot' || s === 'snapshot_only')
|
|
@@ -330,6 +366,9 @@ function createFileGraphDbSnapshotAdapter(options) {
|
|
|
330
366
|
},
|
|
331
367
|
getDiagnostics: () => ({
|
|
332
368
|
...store.getDiagnostics(),
|
|
369
|
+
requestedProvider: 'file',
|
|
370
|
+
resolvedProvider: 'file',
|
|
371
|
+
storageEngine: 'file',
|
|
333
372
|
capabilityMode: 'ops_capable',
|
|
334
373
|
supportedReadOperations: ['load_snapshot', 'load_snapshot_by_ops', 'probe_snapshot_metadata'],
|
|
335
374
|
supportedWriteOperations: ['save_snapshot', 'save_snapshot_by_ops'],
|
|
@@ -680,6 +719,8 @@ function createSqliteGraphDbSnapshotAdapter(options) {
|
|
|
680
719
|
location: sqlitePath,
|
|
681
720
|
exists: fs.existsSync(sqlitePath),
|
|
682
721
|
loaded,
|
|
722
|
+
requestedProvider: 'sqlite',
|
|
723
|
+
resolvedProvider: 'sqlite',
|
|
683
724
|
lastLoadAt: lastLoadAt || undefined,
|
|
684
725
|
lastSaveAt: lastSaveAt || undefined,
|
|
685
726
|
lastError: lastError || undefined,
|
|
@@ -719,15 +760,22 @@ function createGraphDbSnapshotAdapter(options) {
|
|
|
719
760
|
return fileAdapter;
|
|
720
761
|
}
|
|
721
762
|
if (provider === 'sqlite') {
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
763
|
+
try {
|
|
764
|
+
const sqliteAdapter = createSqliteGraphDbSnapshotAdapter({
|
|
765
|
+
provider: 'sqlite',
|
|
766
|
+
filePath: options?.filePath,
|
|
767
|
+
sqlitePath: options?.sqlitePath,
|
|
768
|
+
id: (options?.id ?? options?.sqliteAdapterId ?? options?.adapterId),
|
|
769
|
+
});
|
|
770
|
+
if (!sqliteAdapter)
|
|
771
|
+
return null;
|
|
772
|
+
return sqliteAdapter;
|
|
773
|
+
}
|
|
774
|
+
catch {
|
|
775
|
+
// A sandboxed or read-only host must be allowed to use the
|
|
776
|
+
// configured file fallback instead of failing during startup.
|
|
729
777
|
return null;
|
|
730
|
-
|
|
778
|
+
}
|
|
731
779
|
}
|
|
732
780
|
// HTTP adapter — real HTTP communication with graphdb endpoint
|
|
733
781
|
const httpEndpoint = (options?.httpEndpoint ?? options?.baseUrl);
|
|
@@ -914,6 +962,9 @@ function createGraphDbSnapshotAdapter(options) {
|
|
|
914
962
|
exists: okCount > 0,
|
|
915
963
|
loaded: okCount > 0,
|
|
916
964
|
location: snapUrl,
|
|
965
|
+
requestedProvider: 'remote',
|
|
966
|
+
resolvedProvider: 'remote',
|
|
967
|
+
storageEngine: 'remote',
|
|
917
968
|
capabilityMode: 'ops_capable',
|
|
918
969
|
supportedReadOperations: ['load_snapshot', 'get_node', 'query_nodes', 'query_edges', 'find_path'],
|
|
919
970
|
supportedWriteOperations: ['save_snapshot'],
|
|
@@ -942,6 +993,20 @@ function createKnowledgeGraphStore(o) {
|
|
|
942
993
|
const graphdbAdapter = o.graphdb?.adapter ?? o.graphDbAdapter ?? null;
|
|
943
994
|
const graphdbOperationMode = (o.graphdb?.operationMode ?? o.graphDbOperationMode ?? 'snapshot');
|
|
944
995
|
const graphdbFallbackEnabled = (o.graphdb?.fallbackEnabled ?? o.graphDbFallbackEnabled ?? true);
|
|
996
|
+
const requestedProviderOverride = o.graphdb?.requestedProvider
|
|
997
|
+
?? o.graphDbRequestedProvider
|
|
998
|
+
?? o.graphDbAdapterProvider;
|
|
999
|
+
const requestedProviderValue = requestedProviderOverride
|
|
1000
|
+
?? graphdbAdapter?.provider
|
|
1001
|
+
?? 'unknown';
|
|
1002
|
+
const requestedProviderKind = normalizeStorageProviderKind(requestedProviderValue);
|
|
1003
|
+
const requestedProvider = requestedProviderKind
|
|
1004
|
+
?? (String(requestedProviderValue || '').trim().toLowerCase() || 'unknown');
|
|
1005
|
+
const adapterUnavailableReason = requestedProviderKind === 'sqlite'
|
|
1006
|
+
? 'sqlite_runtime_unavailable'
|
|
1007
|
+
: requestedProviderKind === 'remote'
|
|
1008
|
+
? 'remote_endpoint_unavailable'
|
|
1009
|
+
: 'graphdb_adapter_unavailable';
|
|
945
1010
|
if (backend === 'memory') {
|
|
946
1011
|
// In-memory store for testing
|
|
947
1012
|
let memorySnapshot = null;
|
|
@@ -974,6 +1039,19 @@ function createKnowledgeGraphStore(o) {
|
|
|
974
1039
|
pathOpsReadCount: 0,
|
|
975
1040
|
pathFallbackCount: 0,
|
|
976
1041
|
};
|
|
1042
|
+
const snapshotMetadataFromSnapshot = (snapshot) => {
|
|
1043
|
+
if (!snapshot) {
|
|
1044
|
+
return null;
|
|
1045
|
+
}
|
|
1046
|
+
return {
|
|
1047
|
+
schemaVersion: snapshot.schemaVersion,
|
|
1048
|
+
savedAt: snapshot.savedAt,
|
|
1049
|
+
atomCount: Array.isArray(snapshot.atoms) ? snapshot.atoms.length : 0,
|
|
1050
|
+
relationEdgeCount: Array.isArray(snapshot.relationEdges) ? snapshot.relationEdges.length : 0,
|
|
1051
|
+
temporalEdgeCount: Array.isArray(snapshot.temporalEdges) ? snapshot.temporalEdges.length : 0,
|
|
1052
|
+
documentCount: Array.isArray(snapshot.documents) ? snapshot.documents.length : 0,
|
|
1053
|
+
};
|
|
1054
|
+
};
|
|
977
1055
|
const resolveOpsPath = async () => {
|
|
978
1056
|
if (!opsCapable || requestMode !== 'ops_preferred')
|
|
979
1057
|
return false;
|
|
@@ -1004,6 +1082,9 @@ function createKnowledgeGraphStore(o) {
|
|
|
1004
1082
|
const result = await a.loadSnapshotByOps();
|
|
1005
1083
|
opsState.probed = true;
|
|
1006
1084
|
opsState.opsReadUsed = true;
|
|
1085
|
+
if (result) {
|
|
1086
|
+
opsState.lastProbeResult = snapshotMetadataFromSnapshot(result);
|
|
1087
|
+
}
|
|
1007
1088
|
return result;
|
|
1008
1089
|
}
|
|
1009
1090
|
catch {
|
|
@@ -1011,7 +1092,12 @@ function createKnowledgeGraphStore(o) {
|
|
|
1011
1092
|
}
|
|
1012
1093
|
}
|
|
1013
1094
|
}
|
|
1014
|
-
|
|
1095
|
+
const snapshot = await a.loadSnapshot();
|
|
1096
|
+
if (snapshot) {
|
|
1097
|
+
opsState.lastProbeResult = snapshotMetadataFromSnapshot(snapshot);
|
|
1098
|
+
opsState.probed = true;
|
|
1099
|
+
}
|
|
1100
|
+
return snapshot;
|
|
1015
1101
|
};
|
|
1016
1102
|
const saveSnapshotThroughAdapter = async (snapshot) => {
|
|
1017
1103
|
if (await resolveOpsPath() && typeof a.saveSnapshotByOps === 'function') {
|
|
@@ -1107,6 +1193,15 @@ function createKnowledgeGraphStore(o) {
|
|
|
1107
1193
|
getDiagnostics: () => {
|
|
1108
1194
|
const diag = a.getDiagnostics?.() ?? {};
|
|
1109
1195
|
const caps = resolveOpsCapabilities();
|
|
1196
|
+
const resolvedProvider = normalizeStorageProviderKind(diag.resolvedProvider
|
|
1197
|
+
?? diag.storageEngine
|
|
1198
|
+
?? a.provider);
|
|
1199
|
+
const fallbackReason = diag.fallbackReason
|
|
1200
|
+
?? (requestedProviderKind && resolvedProvider && requestedProviderKind !== resolvedProvider
|
|
1201
|
+
? requestedProviderKind === 'sqlite' && resolvedProvider === 'file'
|
|
1202
|
+
? 'sqlite_runtime_unavailable'
|
|
1203
|
+
: 'provider_request_mismatch'
|
|
1204
|
+
: undefined);
|
|
1110
1205
|
const totalQueryOpsReadCount = queryOpsState.nodeOpsReadCount
|
|
1111
1206
|
+ queryOpsState.nodesOpsReadCount
|
|
1112
1207
|
+ queryOpsState.edgesOpsReadCount
|
|
@@ -1123,6 +1218,9 @@ function createKnowledgeGraphStore(o) {
|
|
|
1123
1218
|
adapterId: a.id,
|
|
1124
1219
|
...diag,
|
|
1125
1220
|
storeType: 'graphdb',
|
|
1221
|
+
requestedProvider,
|
|
1222
|
+
resolvedProvider,
|
|
1223
|
+
fallbackReason,
|
|
1126
1224
|
graphDbOperationMode: normalizeGraphDbStoreOperationMode(graphdbOperationMode),
|
|
1127
1225
|
fallbackEnabled: graphdbFallbackEnabled,
|
|
1128
1226
|
graphDbAdapterCapabilityMode: diag.capabilityMode ?? caps.mode ?? (opsCapable ? 'ops_capable' : 'snapshot_only'),
|
|
@@ -1203,6 +1301,8 @@ function createKnowledgeGraphStore(o) {
|
|
|
1203
1301
|
usingFallback: false,
|
|
1204
1302
|
fallbackEnabled: false,
|
|
1205
1303
|
lastError: 'graphdb_adapter_unavailable_no_fallback',
|
|
1304
|
+
requestedProvider,
|
|
1305
|
+
fallbackReason: `${adapterUnavailableReason}_no_fallback`,
|
|
1206
1306
|
graphDbOperationMode: normalizeGraphDbStoreOperationMode(graphdbOperationMode),
|
|
1207
1307
|
graphDbAdapterCapabilityMode: 'unknown',
|
|
1208
1308
|
graphDbReadPath: 'fallback',
|
|
@@ -1229,6 +1329,10 @@ function createKnowledgeGraphStore(o) {
|
|
|
1229
1329
|
usingFallback: true,
|
|
1230
1330
|
fallbackEnabled: true,
|
|
1231
1331
|
fallbackStoreType: 'file',
|
|
1332
|
+
requestedProvider,
|
|
1333
|
+
resolvedProvider: 'file',
|
|
1334
|
+
fallbackReason: adapterUnavailableReason,
|
|
1335
|
+
storageEngine: 'file',
|
|
1232
1336
|
graphDbOperationMode: 'snapshot_only',
|
|
1233
1337
|
graphDbAdapterCapabilityMode: 'unknown',
|
|
1234
1338
|
graphDbReadPath: 'fallback',
|
|
@@ -413,6 +413,65 @@ describe('Knowledge graph store backend factory', () => {
|
|
|
413
413
|
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
414
414
|
}
|
|
415
415
|
});
|
|
416
|
+
test('refreshes the in-process snapshot cache after a save', async () => {
|
|
417
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'knowledge-store-cache-coherence-'));
|
|
418
|
+
const filePath = path.join(tempRoot, 'runtime_data', 'knowledge_graph_store.v1.json');
|
|
419
|
+
try {
|
|
420
|
+
const store = (0, store_1.createFileBackedKnowledgeGraphStore)({ filePath });
|
|
421
|
+
const first = createSnapshot('cache_first');
|
|
422
|
+
first.atoms = [createAtom('atom_first', 'First')];
|
|
423
|
+
const second = createSnapshot('cache_second');
|
|
424
|
+
second.atoms = [createAtom('atom_second', 'Second')];
|
|
425
|
+
await store.saveSnapshot(first);
|
|
426
|
+
expect(await store.getNode('atom_first')).toEqual(first.atoms[0]);
|
|
427
|
+
await store.saveSnapshot(second);
|
|
428
|
+
expect(await store.getNode('atom_first')).toBeNull();
|
|
429
|
+
expect(await store.getNode('atom_second')).toEqual(second.atoms[0]);
|
|
430
|
+
}
|
|
431
|
+
finally {
|
|
432
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
test('uses a unique sibling temporary path for each save', async () => {
|
|
436
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'knowledge-store-atomic-save-'));
|
|
437
|
+
const filePath = path.join(tempRoot, 'runtime_data', 'knowledge_graph_store.v1.json');
|
|
438
|
+
const renameSpy = jest.spyOn(fs.promises, 'rename');
|
|
439
|
+
try {
|
|
440
|
+
const store = (0, store_1.createKnowledgeGraphStore)({ backend: 'file', filePath });
|
|
441
|
+
await store.saveSnapshot(createSnapshot('atomic_first'));
|
|
442
|
+
await store.saveSnapshot(createSnapshot('atomic_second'));
|
|
443
|
+
const temporaryPaths = renameSpy.mock.calls
|
|
444
|
+
.map(([sourcePath]) => String(sourcePath));
|
|
445
|
+
expect(new Set(temporaryPaths).size).toBe(2);
|
|
446
|
+
expect(fs.existsSync(filePath)).toBe(true);
|
|
447
|
+
expect(fs.readdirSync(path.dirname(filePath)).filter((name) => name.includes('.tmp'))).toHaveLength(0);
|
|
448
|
+
}
|
|
449
|
+
finally {
|
|
450
|
+
renameSpy.mockRestore();
|
|
451
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
test('keeps the target parseable when saves overlap', async () => {
|
|
455
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'knowledge-store-concurrent-save-'));
|
|
456
|
+
const filePath = path.join(tempRoot, 'runtime_data', 'knowledge_graph_store.v1.json');
|
|
457
|
+
try {
|
|
458
|
+
const store = (0, store_1.createFileBackedKnowledgeGraphStore)({ filePath });
|
|
459
|
+
const first = createSnapshot('concurrent_first');
|
|
460
|
+
first.atoms = [createAtom('atom_concurrent_first', 'Concurrent First')];
|
|
461
|
+
const second = createSnapshot('concurrent_second');
|
|
462
|
+
second.atoms = [createAtom('atom_concurrent_second', 'Concurrent Second')];
|
|
463
|
+
await Promise.all([
|
|
464
|
+
store.saveSnapshot(first),
|
|
465
|
+
store.saveSnapshot(second),
|
|
466
|
+
]);
|
|
467
|
+
const persisted = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
468
|
+
expect(['atom_concurrent_first', 'atom_concurrent_second']).toContain(persisted.atoms[0]?.id);
|
|
469
|
+
expect(fs.readdirSync(path.dirname(filePath)).filter((name) => name.endsWith('.tmp'))).toHaveLength(0);
|
|
470
|
+
}
|
|
471
|
+
finally {
|
|
472
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
473
|
+
}
|
|
474
|
+
});
|
|
416
475
|
test('normalizes graphdb adapter provider aliases', () => {
|
|
417
476
|
expect((0, store_1.normalizeGraphDbSnapshotAdapterProvider)('file')).toBe('file');
|
|
418
477
|
expect((0, store_1.normalizeGraphDbSnapshotAdapterProvider)('local-file')).toBe('file');
|
|
@@ -426,6 +485,37 @@ describe('Knowledge graph store backend factory', () => {
|
|
|
426
485
|
expect((0, store_1.normalizeGraphDbSnapshotAdapterProvider)('fallback_only')).toBe('none');
|
|
427
486
|
expect((0, store_1.normalizeGraphDbSnapshotAdapterProvider)('unknown')).toBe('file');
|
|
428
487
|
});
|
|
488
|
+
test('normalizes cross-platform storage provider kinds without conflating http and projection', () => {
|
|
489
|
+
expect((0, store_1.normalizeStorageProviderKind)('sqlite')).toBe('sqlite');
|
|
490
|
+
expect((0, store_1.normalizeStorageProviderKind)('embedded-sqlite')).toBe('sqlite');
|
|
491
|
+
expect((0, store_1.normalizeStorageProviderKind)('file')).toBe('file');
|
|
492
|
+
expect((0, store_1.normalizeStorageProviderKind)('external_http')).toBe('remote');
|
|
493
|
+
expect((0, store_1.normalizeStorageProviderKind)('remote')).toBe('remote');
|
|
494
|
+
expect((0, store_1.normalizeStorageProviderKind)('mobile_projection')).toBe('projection');
|
|
495
|
+
expect((0, store_1.normalizeStorageProviderKind)('unknown')).toBeUndefined();
|
|
496
|
+
});
|
|
497
|
+
test('reports file resolution when a requested sqlite graph adapter is unavailable', async () => {
|
|
498
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'knowledge-store-provider-resolution-'));
|
|
499
|
+
const filePath = path.join(tempRoot, 'runtime_data', 'knowledge_graph_store.v1.json');
|
|
500
|
+
try {
|
|
501
|
+
const store = (0, store_1.createKnowledgeGraphStore)({
|
|
502
|
+
backend: 'graphdb',
|
|
503
|
+
filePath,
|
|
504
|
+
graphdb: { adapter: null },
|
|
505
|
+
graphDbRequestedProvider: 'sqlite',
|
|
506
|
+
graphDbFallbackEnabled: true,
|
|
507
|
+
});
|
|
508
|
+
expect(store.getDiagnostics()).toEqual(expect.objectContaining({
|
|
509
|
+
requestedProvider: 'sqlite',
|
|
510
|
+
resolvedProvider: 'file',
|
|
511
|
+
fallbackReason: 'sqlite_runtime_unavailable',
|
|
512
|
+
storageEngine: 'file',
|
|
513
|
+
}));
|
|
514
|
+
}
|
|
515
|
+
finally {
|
|
516
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
517
|
+
}
|
|
518
|
+
});
|
|
429
519
|
test('normalizes graphdb operation mode aliases', () => {
|
|
430
520
|
expect((0, store_1.normalizeGraphDbStoreOperationMode)('snapshot')).toBe('snapshot_only');
|
|
431
521
|
expect((0, store_1.normalizeGraphDbStoreOperationMode)('snapshot_only')).toBe('snapshot_only');
|
|
@@ -873,6 +963,8 @@ describe('Knowledge graph store backend factory', () => {
|
|
|
873
963
|
expect(diagnostics.capabilityMode).toBe('ops_capable');
|
|
874
964
|
expect(diagnostics.supportedReadOperations).toEqual(expect.arrayContaining(['load_snapshot', 'get_node', 'query_nodes', 'query_edges', 'find_path']));
|
|
875
965
|
expect(diagnostics.supportedWriteOperations).toEqual(['save_snapshot']);
|
|
966
|
+
expect(diagnostics.requestedProvider).toBe('remote');
|
|
967
|
+
expect(diagnostics.resolvedProvider).toBe('remote');
|
|
876
968
|
expect(String(diagnostics.connector?.healthStatus || '')).toBe('ready');
|
|
877
969
|
expect(String(diagnostics.connector?.circuitState || '')).toBe('closed');
|
|
878
970
|
expect(Number(diagnostics.connector?.requestCount || 0)).toBeGreaterThanOrEqual(2);
|
|
@@ -886,6 +978,55 @@ describe('Knowledge graph store backend factory', () => {
|
|
|
886
978
|
await mockServer.close();
|
|
887
979
|
}
|
|
888
980
|
});
|
|
981
|
+
test('reports a SQLite runtime fallback when a file adapter is supplied for a SQLite request', () => {
|
|
982
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'knowledge-store-provider-mismatch-'));
|
|
983
|
+
const filePath = path.join(tempRoot, 'runtime_data', 'knowledge_graph_store.graphdb.v1.json');
|
|
984
|
+
try {
|
|
985
|
+
const adapter = (0, store_1.createFileGraphDbSnapshotAdapter)({ filePath, id: 'file-fallback-adapter' });
|
|
986
|
+
const store = (0, store_1.createKnowledgeGraphStore)({
|
|
987
|
+
backend: 'graphdb',
|
|
988
|
+
filePath,
|
|
989
|
+
graphdb: { adapter },
|
|
990
|
+
graphDbRequestedProvider: 'sqlite',
|
|
991
|
+
graphDbFallbackEnabled: true,
|
|
992
|
+
});
|
|
993
|
+
expect(store.getDiagnostics()).toEqual(expect.objectContaining({
|
|
994
|
+
requestedProvider: 'sqlite',
|
|
995
|
+
resolvedProvider: 'file',
|
|
996
|
+
fallbackReason: 'sqlite_runtime_unavailable',
|
|
997
|
+
storageEngine: 'file',
|
|
998
|
+
}));
|
|
999
|
+
}
|
|
1000
|
+
finally {
|
|
1001
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
1002
|
+
}
|
|
1003
|
+
});
|
|
1004
|
+
test('returns an unavailable SQLite adapter instead of throwing when its parent path cannot be created', () => {
|
|
1005
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'knowledge-store-sqlite-init-failure-'));
|
|
1006
|
+
const parentFile = path.join(tempRoot, 'not-a-directory');
|
|
1007
|
+
fs.writeFileSync(parentFile, 'occupied', 'utf8');
|
|
1008
|
+
try {
|
|
1009
|
+
const adapter = (0, store_1.createGraphDbSnapshotAdapter)({
|
|
1010
|
+
provider: 'sqlite',
|
|
1011
|
+
sqlitePath: path.join(parentFile, 'knowledge.sqlite'),
|
|
1012
|
+
});
|
|
1013
|
+
expect(adapter).toBeNull();
|
|
1014
|
+
const fallbackPath = path.join(tempRoot, 'fallback', 'knowledge.json');
|
|
1015
|
+
const store = (0, store_1.createKnowledgeGraphStore)({
|
|
1016
|
+
backend: 'graphdb',
|
|
1017
|
+
filePath: fallbackPath,
|
|
1018
|
+
graphdb: { adapter: null },
|
|
1019
|
+
graphDbRequestedProvider: 'sqlite',
|
|
1020
|
+
});
|
|
1021
|
+
expect(store.getDiagnostics()).toEqual(expect.objectContaining({
|
|
1022
|
+
resolvedProvider: 'file',
|
|
1023
|
+
fallbackReason: 'sqlite_runtime_unavailable',
|
|
1024
|
+
}));
|
|
1025
|
+
}
|
|
1026
|
+
finally {
|
|
1027
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
1028
|
+
}
|
|
1029
|
+
});
|
|
889
1030
|
test('graphdb store proxies node/edge/path operations through http ops adapter when ops mode is preferred', async () => {
|
|
890
1031
|
const mockServer = await startMockGraphDbSnapshotServer();
|
|
891
1032
|
try {
|
|
@@ -980,6 +1121,9 @@ describe('Knowledge graph store backend factory', () => {
|
|
|
980
1121
|
expect(String(diagnostics.location || '')).toContain('knowledge_graph_store.graphdb.v1.sqlite');
|
|
981
1122
|
expect(diagnostics.storeType).toBe('graphdb');
|
|
982
1123
|
expect(String(diagnostics.storageEngine || '')).toBe('sqlite');
|
|
1124
|
+
expect(diagnostics.requestedProvider).toBe('sqlite');
|
|
1125
|
+
expect(diagnostics.resolvedProvider).toBe('sqlite');
|
|
1126
|
+
expect(diagnostics.fallbackReason).toBeUndefined();
|
|
983
1127
|
expect(diagnostics.capabilityMode).toBe('ops_capable');
|
|
984
1128
|
expect(diagnostics.supportedReadOperations).toEqual(expect.arrayContaining(['load_snapshot', 'get_node', 'query_nodes', 'query_edges', 'find_path']));
|
|
985
1129
|
expect(diagnostics.supportedWriteOperations).toEqual(['save_snapshot']);
|
|
@@ -1059,6 +1203,161 @@ describe('Knowledge graph store backend factory', () => {
|
|
|
1059
1203
|
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
1060
1204
|
}
|
|
1061
1205
|
});
|
|
1206
|
+
test('sqlite graphdb adapter reopens after close and replays the last committed snapshot', async () => {
|
|
1207
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'knowledge-store-sqlite-replay-'));
|
|
1208
|
+
const sqlitePath = path.join(tempRoot, 'runtime_data', 'knowledge_graph_store.graphdb.v1.sqlite');
|
|
1209
|
+
let firstAdapter = null;
|
|
1210
|
+
let secondAdapter = null;
|
|
1211
|
+
try {
|
|
1212
|
+
firstAdapter = (0, store_1.createGraphDbSnapshotAdapter)({
|
|
1213
|
+
provider: 'sqlite',
|
|
1214
|
+
sqlitePath,
|
|
1215
|
+
adapterId: 'embedded-sqlite-replay-before-close',
|
|
1216
|
+
});
|
|
1217
|
+
expect(firstAdapter).not.toBeNull();
|
|
1218
|
+
const snapshot = createSnapshot('sqlite_restart_user');
|
|
1219
|
+
snapshot.atoms = [createAtom('restart_atom', 'Restart Alpha')];
|
|
1220
|
+
snapshot.documents = [{
|
|
1221
|
+
documentId: 'restart_doc',
|
|
1222
|
+
sourcePath: 'restart.md',
|
|
1223
|
+
sourceHash: 'restart_hash',
|
|
1224
|
+
version: 1,
|
|
1225
|
+
updatedAt: '2026-01-01T00:00:00.000Z',
|
|
1226
|
+
atomStableKeyToId: [['restart_stable', 'restart_atom']],
|
|
1227
|
+
atomIds: ['restart_atom'],
|
|
1228
|
+
evidenceSpanIds: [],
|
|
1229
|
+
relationEdgeIds: [],
|
|
1230
|
+
temporalEdgeIds: [],
|
|
1231
|
+
}];
|
|
1232
|
+
await firstAdapter.saveSnapshot(snapshot);
|
|
1233
|
+
firstAdapter.close?.();
|
|
1234
|
+
secondAdapter = (0, store_1.createGraphDbSnapshotAdapter)({
|
|
1235
|
+
provider: 'sqlite',
|
|
1236
|
+
sqlitePath,
|
|
1237
|
+
adapterId: 'embedded-sqlite-replay-after-close',
|
|
1238
|
+
});
|
|
1239
|
+
expect(secondAdapter).not.toBeNull();
|
|
1240
|
+
await expect(secondAdapter.loadSnapshot()).resolves.toEqual(snapshot);
|
|
1241
|
+
await expect(secondAdapter.getNodeByOps?.('restart_atom')).resolves.toEqual(snapshot.atoms[0]);
|
|
1242
|
+
await expect(secondAdapter.probeSnapshotMetadata?.()).resolves.toEqual(expect.objectContaining({
|
|
1243
|
+
atomCount: 1,
|
|
1244
|
+
documentCount: 1,
|
|
1245
|
+
}));
|
|
1246
|
+
}
|
|
1247
|
+
finally {
|
|
1248
|
+
try {
|
|
1249
|
+
firstAdapter?.close?.();
|
|
1250
|
+
secondAdapter?.close?.();
|
|
1251
|
+
}
|
|
1252
|
+
catch {
|
|
1253
|
+
}
|
|
1254
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
1255
|
+
}
|
|
1256
|
+
});
|
|
1257
|
+
test('graphdb store diagnostics restores snapshot metadata after reopening the sqlite adapter', async () => {
|
|
1258
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'knowledge-store-sqlite-diagnostics-reopen-'));
|
|
1259
|
+
const sqlitePath = path.join(tempRoot, 'runtime_data', 'knowledge_graph_store.graphdb.v1.sqlite');
|
|
1260
|
+
let firstAdapter = null;
|
|
1261
|
+
let secondAdapter = null;
|
|
1262
|
+
let firstStore = null;
|
|
1263
|
+
let secondStore = null;
|
|
1264
|
+
try {
|
|
1265
|
+
const snapshot = createSnapshot('sqlite_store_diagnostics_reopen_user');
|
|
1266
|
+
snapshot.atoms = [createAtom('reopen_atom', 'Reopen Alpha')];
|
|
1267
|
+
snapshot.documents = [{
|
|
1268
|
+
documentId: 'reopen_doc',
|
|
1269
|
+
sourcePath: '/reopen/doc.md',
|
|
1270
|
+
sourceHash: 'reopen_hash',
|
|
1271
|
+
version: 1,
|
|
1272
|
+
updatedAt: '2026-01-01T00:00:00.000Z',
|
|
1273
|
+
atomStableKeyToId: [['reopen_atom_stable', 'reopen_atom']],
|
|
1274
|
+
atomIds: ['reopen_atom'],
|
|
1275
|
+
evidenceSpanIds: [],
|
|
1276
|
+
relationEdgeIds: [],
|
|
1277
|
+
temporalEdgeIds: [],
|
|
1278
|
+
}];
|
|
1279
|
+
firstAdapter = (0, store_1.createGraphDbSnapshotAdapter)({ provider: 'sqlite', sqlitePath, adapterId: 'sqlite-diagnostics-reopen-first' });
|
|
1280
|
+
expect(firstAdapter).not.toBeNull();
|
|
1281
|
+
firstStore = (0, store_1.createKnowledgeGraphStore)({
|
|
1282
|
+
backend: 'graphdb',
|
|
1283
|
+
graphdb: { adapter: firstAdapter },
|
|
1284
|
+
graphDbFallbackEnabled: false,
|
|
1285
|
+
graphDbOperationMode: 'ops_preferred',
|
|
1286
|
+
});
|
|
1287
|
+
await firstStore.saveSnapshot(snapshot);
|
|
1288
|
+
firstStore.close?.();
|
|
1289
|
+
firstStore = null;
|
|
1290
|
+
firstAdapter = null;
|
|
1291
|
+
secondAdapter = (0, store_1.createGraphDbSnapshotAdapter)({ provider: 'sqlite', sqlitePath, adapterId: 'sqlite-diagnostics-reopen-second' });
|
|
1292
|
+
expect(secondAdapter).not.toBeNull();
|
|
1293
|
+
secondStore = (0, store_1.createKnowledgeGraphStore)({
|
|
1294
|
+
backend: 'graphdb',
|
|
1295
|
+
graphdb: { adapter: secondAdapter },
|
|
1296
|
+
graphDbFallbackEnabled: false,
|
|
1297
|
+
graphDbOperationMode: 'ops_preferred',
|
|
1298
|
+
});
|
|
1299
|
+
await expect(secondStore.loadSnapshot()).resolves.toEqual(snapshot);
|
|
1300
|
+
expect(secondStore.getDiagnostics().graphDbLastSnapshotMetadata).toEqual(expect.objectContaining({
|
|
1301
|
+
atomCount: 1,
|
|
1302
|
+
documentCount: 1,
|
|
1303
|
+
}));
|
|
1304
|
+
}
|
|
1305
|
+
finally {
|
|
1306
|
+
try {
|
|
1307
|
+
firstStore?.close?.();
|
|
1308
|
+
}
|
|
1309
|
+
catch { }
|
|
1310
|
+
try {
|
|
1311
|
+
secondStore?.close?.();
|
|
1312
|
+
}
|
|
1313
|
+
catch { }
|
|
1314
|
+
try {
|
|
1315
|
+
firstAdapter?.close?.();
|
|
1316
|
+
}
|
|
1317
|
+
catch { }
|
|
1318
|
+
try {
|
|
1319
|
+
secondAdapter?.close?.();
|
|
1320
|
+
}
|
|
1321
|
+
catch { }
|
|
1322
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
1323
|
+
}
|
|
1324
|
+
});
|
|
1325
|
+
test('graphdb store diagnostics derives metadata from ops reads when the adapter has no probe method', async () => {
|
|
1326
|
+
const snapshot = createSnapshot('ops_read_metadata_user');
|
|
1327
|
+
snapshot.atoms = [createAtom('ops_read_atom', 'Ops Read Alpha')];
|
|
1328
|
+
snapshot.documents = [{
|
|
1329
|
+
documentId: 'ops_read_doc',
|
|
1330
|
+
sourcePath: '/ops-read/doc.md',
|
|
1331
|
+
sourceHash: 'ops_read_hash',
|
|
1332
|
+
version: 1,
|
|
1333
|
+
updatedAt: '2026-01-01T00:00:00.000Z',
|
|
1334
|
+
atomStableKeyToId: [['ops_read_atom_stable', 'ops_read_atom']],
|
|
1335
|
+
atomIds: ['ops_read_atom'],
|
|
1336
|
+
evidenceSpanIds: [],
|
|
1337
|
+
relationEdgeIds: [],
|
|
1338
|
+
temporalEdgeIds: [],
|
|
1339
|
+
}];
|
|
1340
|
+
const adapter = {
|
|
1341
|
+
id: 'ops-only-without-probe',
|
|
1342
|
+
opsCapable: true,
|
|
1343
|
+
loadSnapshot: async () => snapshot,
|
|
1344
|
+
saveSnapshot: async () => undefined,
|
|
1345
|
+
loadSnapshotByOps: async () => snapshot,
|
|
1346
|
+
saveSnapshotByOps: async () => undefined,
|
|
1347
|
+
getCapabilities: () => ({ mode: 'ops_capable', snapshotSupported: true, writeSupported: true }),
|
|
1348
|
+
};
|
|
1349
|
+
const store = (0, store_1.createKnowledgeGraphStore)({
|
|
1350
|
+
backend: 'graphdb',
|
|
1351
|
+
graphdb: { adapter },
|
|
1352
|
+
graphDbFallbackEnabled: false,
|
|
1353
|
+
graphDbOperationMode: 'ops_preferred',
|
|
1354
|
+
});
|
|
1355
|
+
await expect(store.loadSnapshot()).resolves.toEqual(snapshot);
|
|
1356
|
+
expect(store.getDiagnostics().graphDbLastSnapshotMetadata).toEqual(expect.objectContaining({
|
|
1357
|
+
atomCount: 1,
|
|
1358
|
+
documentCount: 1,
|
|
1359
|
+
}));
|
|
1360
|
+
});
|
|
1062
1361
|
test('built-in http graphdb adapter surfaces circuit-open telemetry after repeated transient failures', async () => {
|
|
1063
1362
|
const server = http.createServer((req, res) => {
|
|
1064
1363
|
const method = String(req.method || 'GET').toUpperCase();
|