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.
Files changed (195) hide show
  1. package/README.md +257 -1803
  2. package/dist/src/agent_workspace.contract.parity.test.js +105 -421
  3. package/dist/src/agent_workspace.frontend.test.js +410 -11
  4. package/dist/src/android.knowledgebase.picker.contract.test.js +102 -0
  5. package/dist/src/appimage.patchelf.test.js +113 -0
  6. package/dist/src/appimage.portability.test.js +156 -0
  7. package/dist/src/backend/FileLoader.js +18 -4
  8. package/dist/src/backend/FileLoader.test.js +125 -0
  9. package/dist/src/backend/GraphBuilder.identity.test.js +86 -0
  10. package/dist/src/backend/GraphBuilder.js +66 -18
  11. package/dist/src/backend/GraphBuilder.matching.test.js +127 -0
  12. package/dist/src/backend/GraphMetrics.js +0 -3
  13. package/dist/src/backend/ResourceIdentity.js +155 -0
  14. package/dist/src/backend/ResourceIdentity.test.js +113 -0
  15. package/dist/src/backend/algorithms/CycleDetection.js +0 -1
  16. package/dist/src/backend/algorithms/HybridEngine.js +0 -3
  17. package/dist/src/backend/algorithms/LayoutEngine.js +0 -1
  18. package/dist/src/backend/algorithms/StatisticalAnalyzer.js +0 -4
  19. package/dist/src/backend/algorithms/TopologicalSort.js +0 -2
  20. package/dist/src/backend/build_dag.js +0 -1
  21. package/dist/src/capacitor.bridge.serialization.contract.test.js +3 -0
  22. package/dist/src/capacitor.runtime.contract.test.js +11 -2
  23. package/dist/src/core/Graph.js +138 -42
  24. package/dist/src/core/Graph.test.js +74 -0
  25. package/dist/src/core/NoteConnection.js +2 -2
  26. package/dist/src/core/PathBridge.js +265 -2
  27. package/dist/src/core/ResourceReference.js +17 -0
  28. package/dist/src/desktop.godot.pack.test.js +142 -0
  29. package/dist/src/desktop.runtime.graph.test.js +66 -0
  30. package/dist/src/export/WorkspaceExportBundle.js +44 -0
  31. package/dist/src/export/WorkspaceExportBundle.test.js +61 -0
  32. package/dist/src/foundation.release.evidence.contract.test.js +127 -9
  33. package/dist/src/foundation.sqlite.runtime.contract.test.js +6 -0
  34. package/dist/src/frontend/README.md +257 -1803
  35. package/dist/src/frontend/agent_workspace.js +665 -7
  36. package/dist/src/frontend/index.html +31 -2
  37. package/dist/src/frontend/knowledge_projection_contract.js +230 -0
  38. package/dist/src/frontend/knowledge_projection_store.js +214 -0
  39. package/dist/src/frontend/libs/path_core.js +158 -42
  40. package/dist/src/frontend/locales/en.json +32 -0
  41. package/dist/src/frontend/locales/zh.json +32 -0
  42. package/dist/src/frontend/mobile_budget_runtime.js +33 -0
  43. package/dist/src/frontend/mobile_exact_analyzer.js +393 -0
  44. package/dist/src/frontend/mobile_identity_contract.js +221 -0
  45. package/dist/src/frontend/mobile_semantic_comparator.js +264 -0
  46. package/dist/src/frontend/path.html +6 -2
  47. package/dist/src/frontend/simulationWorker.js +1 -1
  48. package/dist/src/frontend/source_manager.js +110 -4
  49. package/dist/src/frontend/storage_provider.js +661 -71
  50. package/dist/src/frontend/styles.css +107 -13
  51. package/dist/src/frontend/workspace_panes.js +111 -7
  52. package/dist/src/frontend.runtime.assets.contract.test.js +81 -0
  53. package/dist/src/graph.answer.operator.contract.test.js +72 -0
  54. package/dist/src/index.js +6 -2
  55. package/dist/src/index.knowledge-root.test.js +118 -0
  56. package/dist/src/indexing/IndexLifecycle.js +22 -0
  57. package/dist/src/indexing/IndexLifecycle.test.js +24 -0
  58. package/dist/src/knowledge.api.contract.test.js +4 -2
  59. package/dist/src/knowledge_projection.contract.test.js +134 -0
  60. package/dist/src/knowledge_projection_store.test.js +200 -0
  61. package/dist/src/learning/AnswerQualityEvaluation.js +153 -0
  62. package/dist/src/learning/AnswerQualityEvaluation.test.js +118 -0
  63. package/dist/src/learning/KnowledgeLearningPlatform.js +4687 -3892
  64. package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +418 -0
  65. package/dist/src/learning/KnowledgeLearningPlatform.test.js +243 -6
  66. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +25 -1
  67. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +13 -5
  68. package/dist/src/learning/MeasurementEvidenceConflicts.test.js +78 -0
  69. package/dist/src/learning/PublicAnswerContract.test.js +85 -0
  70. package/dist/src/learning/PublicEvidenceSelection.test.js +94 -0
  71. package/dist/src/learning/PublicMeasurementDisclosure.test.js +54 -0
  72. package/dist/src/learning/agentConversationExecution.js +211 -0
  73. package/dist/src/learning/agentConversationExecution.test.js +209 -0
  74. package/dist/src/learning/agentConversationSerialization.js +266 -0
  75. package/dist/src/learning/agentConversationSerialization.test.js +165 -0
  76. package/dist/src/learning/agentResponseBudget.js +179 -0
  77. package/dist/src/learning/agentResponseBudget.test.js +97 -0
  78. package/dist/src/learning/agentWorkspaceCapabilityContracts.js +65 -0
  79. package/dist/src/learning/answerReleaseReview.js +1191 -250
  80. package/dist/src/learning/answerReleaseReview.test.js +1293 -7
  81. package/dist/src/learning/answerTaskPlan.js +473 -0
  82. package/dist/src/learning/conversationComposer.js +627 -386
  83. package/dist/src/learning/conversationComposer.test.js +1403 -0
  84. package/dist/src/learning/evidenceContextAssembler.js +312 -120
  85. package/dist/src/learning/graphAnswerCoverage.js +37 -0
  86. package/dist/src/learning/graphAnswerCoverage.test.js +93 -0
  87. package/dist/src/learning/graphAnswerCoverageCalibration.js +59 -0
  88. package/dist/src/learning/graphAnswerCoverageCalibration.test.js +46 -0
  89. package/dist/src/learning/graphAnswerFacts.js +94 -0
  90. package/dist/src/learning/graphAnswerFacts.test.js +39 -0
  91. package/dist/src/learning/graphAnswerPlan.js +908 -0
  92. package/dist/src/learning/graphAnswerPlan.test.js +907 -0
  93. package/dist/src/learning/graphAnswerQualityCalibration.js +255 -0
  94. package/dist/src/learning/graphAnswerQualityCalibration.test.js +72 -0
  95. package/dist/src/learning/graphAnswerQualityPolicy.js +30 -0
  96. package/dist/src/learning/graphClaimMatcher.js +104 -0
  97. package/dist/src/learning/graphConditionedContext.js +109 -0
  98. package/dist/src/learning/graphConditionedContext.test.js +99 -0
  99. package/dist/src/learning/graphContextAssembler.js +3 -13
  100. package/dist/src/learning/graphContextAssembler.test.js +2 -2
  101. package/dist/src/learning/graphExpansionPolicy.js +22 -0
  102. package/dist/src/learning/graphExpansionPolicy.test.js +33 -0
  103. package/dist/src/learning/identityMetadata.test.js +26 -0
  104. package/dist/src/learning/index.js +1 -0
  105. package/dist/src/learning/mobileAnswerProjection.js +104 -0
  106. package/dist/src/learning/mobileAnswerProjection.test.js +102 -0
  107. package/dist/src/learning/queryBackend.js +34 -10
  108. package/dist/src/learning/queryBackend.test.js +21 -0
  109. package/dist/src/learning/ragContextPack.js +115 -16
  110. package/dist/src/learning/ragContextPack.test.js +151 -0
  111. package/dist/src/learning/ragEvidenceQuality.js +96 -0
  112. package/dist/src/learning/ragEvidenceQuality.test.js +29 -0
  113. package/dist/src/learning/ragPublicText.js +67 -4
  114. package/dist/src/learning/ragPublicText.test.js +57 -0
  115. package/dist/src/learning/ragSufficiencyJudge.js +11 -1
  116. package/dist/src/learning/ragSufficiencyProviderJudge.js +16 -26
  117. package/dist/src/learning/ragSufficiencyProviderJudge.test.js +51 -0
  118. package/dist/src/learning/requestNormalization.js +106 -0
  119. package/dist/src/learning/requestNormalization.test.js +101 -0
  120. package/dist/src/learning/store.js +116 -12
  121. package/dist/src/learning/store.test.js +299 -0
  122. package/dist/src/learning/vectorAccelerationAdapter.js +22 -6
  123. package/dist/src/learning/vectorAccelerationAdapter.test.js +30 -0
  124. package/dist/src/learning/workspaceHydration.js +168 -0
  125. package/dist/src/learning/workspaceHydration.test.js +30 -0
  126. package/dist/src/license.policy.contract.test.js +9 -4
  127. package/dist/src/markdown/MarkdownGateway.js +4 -0
  128. package/dist/src/markdown/MarkdownGateway.test.js +59 -0
  129. package/dist/src/middleware/SseResponseWriter.js +122 -0
  130. package/dist/src/middleware/SseResponseWriter.test.js +54 -0
  131. package/dist/src/middleware/auth.js +28 -7
  132. package/dist/src/middleware/auth.test.js +33 -0
  133. package/dist/src/middleware/index.js +2 -1
  134. package/dist/src/mobile.android.evidence.contract.test.js +146 -0
  135. package/dist/src/mobile.android.signing.contract.test.js +66 -0
  136. package/dist/src/mobile.artifact.contract.test.js +144 -0
  137. package/dist/src/mobile.budget.contract.test.js +90 -0
  138. package/dist/src/mobile.canonical-id.readiness.test.js +54 -0
  139. package/dist/src/mobile.graph.parity.contract.test.js +207 -0
  140. package/dist/src/mobile.identity.contract.test.js +66 -0
  141. package/dist/src/mobile.identity.corpus.manifest.test.js +72 -0
  142. package/dist/src/mobile.identity.corpus.test.js +40 -0
  143. package/dist/src/mobile.native.recovery.contract.test.js +64 -0
  144. package/dist/src/mobile.pipeline.test.js +11 -1
  145. package/dist/src/mobile.profile.contract.test.js +225 -0
  146. package/dist/src/mobile.semantic.parity.test.js +92 -0
  147. package/dist/src/mobile_exact_analyzer.test.js +113 -0
  148. package/dist/src/notemd/ContentGenerator.js +4 -9
  149. package/dist/src/notemd/FileProcessor.js +4 -9
  150. package/dist/src/notemd/NotemdService.js +12 -17
  151. package/dist/src/notemd/Translator.js +4 -9
  152. package/dist/src/notemd/progressReporter.js +9 -0
  153. package/dist/src/notemd.server.integration.test.js +119 -4
  154. package/dist/src/notemd.workflow.pipeline.test.js +1 -1
  155. package/dist/src/pathbridge.handshake.contract.test.js +29 -0
  156. package/dist/src/pathbridge.host-adapter.contract.test.js +133 -0
  157. package/dist/src/pathcore.bundle.contract.test.js +8 -0
  158. package/dist/src/platform/ExportProfile.js +28 -0
  159. package/dist/src/platform/MobileBudget.js +11 -0
  160. package/dist/src/platform/PlatformCapabilities.js +14 -0
  161. package/dist/src/platform/PlatformCapabilities.test.js +6 -0
  162. package/dist/src/query_backend.external_http.integration.test.js +2 -2
  163. package/dist/src/release.godot.mirror.contract.test.js +28 -4
  164. package/dist/src/resources/ResourceRegistry.js +40 -0
  165. package/dist/src/resources/ResourceRegistry.test.js +36 -0
  166. package/dist/src/routes/index.js +0 -4
  167. package/dist/src/routes/knowledge.js +46 -33
  168. package/dist/src/routes/knowledgePayload.js +185 -0
  169. package/dist/src/routes/knowledgePayload.test.js +53 -0
  170. package/dist/src/routes/markdown.js +8 -0
  171. package/dist/src/routes/notemd.js +990 -234
  172. package/dist/src/routes/registry.contract.test.js +5 -2
  173. package/dist/src/routes/registry.shadow.contract.test.js +22 -0
  174. package/dist/src/routes/render.js +14 -51
  175. package/dist/src/runtime.capabilities.test.js +18 -2
  176. package/dist/src/server.js +566 -1090
  177. package/dist/src/server.migration.test.js +417 -15
  178. package/dist/src/sidecar.build.lock.contract.test.js +201 -0
  179. package/dist/src/sidecar.freshness.contract.test.js +184 -0
  180. package/dist/src/sidecar.replacement.boundary.contract.test.js +6 -3
  181. package/dist/src/sidecar.supply.readiness.contract.test.js +11 -0
  182. package/dist/src/simulation.worker.offline.test.js +104 -0
  183. package/dist/src/storage.provider.capacitor.content.contract.test.js +4 -1
  184. package/dist/src/storage.provider.contract.test.js +152 -1
  185. package/dist/src/tauri.frontend.build.contract.test.js +8 -0
  186. package/dist/src/tauri.window.evidence.execution.test.js +68 -0
  187. package/dist/src/utils/RuntimePaths.js +6 -0
  188. package/dist/src/utils/RuntimePaths.test.js +27 -0
  189. package/dist/src/windows.installer.build.test.js +109 -0
  190. package/dist/src/windows.installer.payload.test.js +76 -0
  191. package/dist/src/windows.installer.runtime.test.js +69 -0
  192. package/dist/src/workspace/WorkspaceRegistry.js +13 -0
  193. package/package.json +32 -10
  194. package/dist/src/routes/data.js +0 -267
  195. 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 parsed;
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
- async saveSnapshot(snapshot) {
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
- const sqliteAdapter = createSqliteGraphDbSnapshotAdapter({
723
- provider: 'sqlite',
724
- filePath: options?.filePath,
725
- sqlitePath: options?.sqlitePath,
726
- id: (options?.id ?? options?.sqliteAdapterId ?? options?.adapterId),
727
- });
728
- if (!sqliteAdapter)
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
- return sqliteAdapter;
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
- return a.loadSnapshot();
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();