enigma-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.
Files changed (105) hide show
  1. package/LICENSE +55 -0
  2. package/README.md +379 -0
  3. package/apps/cli/bin/enigma.mjs +1117 -0
  4. package/apps/desktop/src/app.js +1327 -0
  5. package/apps/desktop/src/index.html +1373 -0
  6. package/apps/desktop/src/styles.css +386 -0
  7. package/apps/gateway/bin/enigma-gateway.mjs +52 -0
  8. package/apps/gateway/src/server.mjs +1403 -0
  9. package/apps/native-host/README.md +265 -0
  10. package/apps/native-host/bin/enigma-native-host.mjs +499 -0
  11. package/apps/native-host/manifests/com.enigma.native_host.chrome.json +9 -0
  12. package/apps/native-host/manifests/com.enigma.native_host.edge.json +9 -0
  13. package/apps/native-host/manifests/com.enigma.native_host.firefox.json +9 -0
  14. package/apps/relay/bin/enigma-relay.mjs +52 -0
  15. package/apps/relay/src/server.mjs +1571 -0
  16. package/apps/verifier/bin/enigma-verify.mjs +240 -0
  17. package/deploy/docker-compose.production.example.yml +150 -0
  18. package/deploy/kubernetes/enigma-backend.example.yaml +580 -0
  19. package/package.json +196 -0
  20. package/packages/adapters/PACKAGE_CONTRACT.md +47 -0
  21. package/packages/adapters/src/index.js +183 -0
  22. package/packages/boundary/src/index.js +468 -0
  23. package/packages/connectors/src/index.js +533 -0
  24. package/packages/core/src/index.js +766 -0
  25. package/packages/enterprise/src/index.js +505 -0
  26. package/packages/importers/src/index.js +1083 -0
  27. package/packages/mcp-server/bin/enigma-mcp.mjs +19 -0
  28. package/packages/mcp-server/src/index.js +1185 -0
  29. package/packages/mesh/src/index.js +531 -0
  30. package/packages/metering/PACKAGE_CONTRACT.md +20 -0
  31. package/packages/metering/src/index.js +246 -0
  32. package/packages/optimizer/src/index.js +707 -0
  33. package/packages/passport/src/index.js +651 -0
  34. package/packages/settlement/PACKAGE_CONTRACT.md +28 -0
  35. package/packages/settlement/src/index.js +369 -0
  36. package/packages/storage/PACKAGE_CONTRACT.md +23 -0
  37. package/packages/storage/src/index.js +392 -0
  38. package/packages/vault/src/index.js +904 -0
  39. package/scripts/build-ai-orchestration-plan.mjs +248 -0
  40. package/scripts/build-cloudflare-pages-release-packet.mjs +287 -0
  41. package/scripts/build-cloudflare-token-policy.mjs +239 -0
  42. package/scripts/build-cloudflare-token-request.mjs +314 -0
  43. package/scripts/build-demo-assets.mjs +263 -0
  44. package/scripts/build-edge-backend-workers.mjs +353 -0
  45. package/scripts/build-goal-completion-audit.mjs +398 -0
  46. package/scripts/build-hosted-probe-worker.mjs +191 -0
  47. package/scripts/build-operator-acceptance-packet.mjs +254 -0
  48. package/scripts/build-operator-evidence-starter.mjs +525 -0
  49. package/scripts/build-production-backend-env-kit.mjs +384 -0
  50. package/scripts/build-production-dependency-report.mjs +369 -0
  51. package/scripts/build-production-handoff-packet.mjs +475 -0
  52. package/scripts/build-production-readiness-manifest.mjs +209 -0
  53. package/scripts/build-production-status-board.mjs +297 -0
  54. package/scripts/build-production-storage-migration.mjs +86 -0
  55. package/scripts/build-production-workplan.mjs +371 -0
  56. package/scripts/build-review-packet.mjs +594 -0
  57. package/scripts/check.mjs +419 -0
  58. package/scripts/cloudflare-ops.mjs +1672 -0
  59. package/scripts/cloudflare-secret-env.mjs +128 -0
  60. package/scripts/collect-hosted-backend-live-evidence.mjs +263 -0
  61. package/scripts/infrastructure-readiness.mjs +944 -0
  62. package/scripts/install-enigma-local.mjs +270 -0
  63. package/scripts/memory-optimization-benchmark.mjs +242 -0
  64. package/scripts/monitor-live-endpoints.mjs +358 -0
  65. package/scripts/release-audit.mjs +5588 -0
  66. package/scripts/release-provenance.mjs +250 -0
  67. package/scripts/run-backend-readiness-smoke.mjs +220 -0
  68. package/scripts/validate-backup-restore-drill.mjs +166 -0
  69. package/scripts/validate-cloudflare-credentials.mjs +112 -0
  70. package/scripts/validate-cloudflare-worker-inspect.mjs +179 -0
  71. package/scripts/validate-domain-tls.mjs +248 -0
  72. package/scripts/validate-hosted-backend-live.mjs +319 -0
  73. package/scripts/validate-incident-drill.mjs +221 -0
  74. package/scripts/validate-kms-custody.mjs +235 -0
  75. package/scripts/validate-legal-compliance-approval.mjs +274 -0
  76. package/scripts/validate-monitoring-alerting.mjs +263 -0
  77. package/scripts/validate-network-access-policy.mjs +285 -0
  78. package/scripts/validate-operator-acceptance.mjs +298 -0
  79. package/scripts/validate-production-manifests.mjs +325 -0
  80. package/scripts/validate-public-site-security.mjs +252 -0
  81. package/scripts/validate-security-threat-model.mjs +285 -0
  82. package/scripts/validate-service-settlement.mjs +256 -0
  83. package/scripts/validate-support-sla.mjs +190 -0
  84. package/scripts/validate-tenant-policy-approval.mjs +228 -0
  85. package/scripts/validate-usage-metering.mjs +224 -0
  86. package/scripts/validate-whitepaper-claims.mjs +166 -0
  87. package/specs/boundary-manifest-v1.schema.json +35 -0
  88. package/specs/capsule-v1.schema.json +55 -0
  89. package/specs/claim-boundary-manifest-v1.schema.json +22 -0
  90. package/specs/connector-profile-v1.schema.json +62 -0
  91. package/specs/deletion-tombstone-v1.schema.json +26 -0
  92. package/specs/enterprise-policy-v1.schema.json +59 -0
  93. package/specs/gateway-request-v1.schema.json +32 -0
  94. package/specs/goal-completion-audit-v1.schema.json +70 -0
  95. package/specs/hosted-backend-live-evidence-v1.schema.json +147 -0
  96. package/specs/import-report-v1.schema.json +214 -0
  97. package/specs/infrastructure-readiness-manifest-v1.schema.json +130 -0
  98. package/specs/memory-event-v1.schema.json +42 -0
  99. package/specs/mesh-node-v1.schema.json +126 -0
  100. package/specs/passport-v1.schema.json +50 -0
  101. package/specs/production-handoff-packet-v1.schema.json +149 -0
  102. package/specs/receipt-v1.schema.json +61 -0
  103. package/specs/relay-record-v1.schema.json +43 -0
  104. package/specs/state-checkpoint-v1.schema.json +37 -0
  105. package/specs/trust-bundle-v1.schema.json +56 -0
@@ -0,0 +1,1083 @@
1
+ import { canonicalize, sha256Hex, MerkleSet, verifyReceipt } from '../../core/src/index.js';
2
+ import { remember } from '../../vault/src/index.js';
3
+ import { createCapsuleManifest, createMeshNode, verifyCapsuleManifest } from '../../mesh/src/index.js';
4
+
5
+ const REPORT_SCHEMA = 'enigma.import_report.v1';
6
+ const CAPSULE_SCHEMA = 'enigma.import_capsule.v1';
7
+ const CAPSULE_PAYLOAD_SCHEMA = 'enigma.import_capsule_payload.v1';
8
+ const VERIFIER_METADATA_SCHEMA = 'enigma.import_capsule_verifier_metadata.v1';
9
+ const DEFAULT_NOW = '1970-01-01T00:00:00.000Z';
10
+ const DEFAULT_EXPIRY = '2100-01-01T00:00:00.000Z';
11
+ const CONFIDENCE_ORDER = new Map([
12
+ ['low', 0],
13
+ ['medium', 1],
14
+ ['high', 2],
15
+ ['source_asserted', 3],
16
+ ['user_confirmed', 4]
17
+ ]);
18
+
19
+ function isRecord(value) {
20
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
21
+ }
22
+
23
+ function asArray(value) {
24
+ if (value === undefined || value === null) return [];
25
+ return Array.isArray(value) ? value : [value];
26
+ }
27
+
28
+ function stable(value) {
29
+ try {
30
+ return canonicalize(value);
31
+ } catch {
32
+ const seen = new WeakSet();
33
+ return JSON.stringify(value, (_key, item) => {
34
+ if (typeof item === 'bigint') return item.toString();
35
+ if (typeof item === 'undefined' || typeof item === 'function' || typeof item === 'symbol') return null;
36
+ if (item !== null && typeof item === 'object') {
37
+ if (seen.has(item)) return '[Circular]';
38
+ seen.add(item);
39
+ }
40
+ return item;
41
+ }) ?? 'null';
42
+ }
43
+ }
44
+
45
+ function rootOf(value) {
46
+ return `sha256:${sha256Hex(typeof value === 'string' ? value : stable(value))}`;
47
+ }
48
+
49
+ function shortHash(value) {
50
+ return sha256Hex(typeof value === 'string' ? value : stable(value)).slice(0, 32);
51
+ }
52
+
53
+ function nowFrom(options) {
54
+ const now = options?.now ?? DEFAULT_NOW;
55
+ if (now instanceof Date) return now.toISOString();
56
+ if (typeof now === 'string') return new Date(now).toISOString();
57
+ return new Date(now).toISOString();
58
+ }
59
+
60
+ function parseMaybeJson(input) {
61
+ if (typeof input !== 'string') return input;
62
+ const trimmed = input.trim();
63
+ if (trimmed.length === 0) return '';
64
+ if ((trimmed.startsWith('{') && trimmed.endsWith('}')) || (trimmed.startsWith('[') && trimmed.endsWith(']'))) {
65
+ try {
66
+ return JSON.parse(trimmed);
67
+ } catch {
68
+ return input;
69
+ }
70
+ }
71
+ return input;
72
+ }
73
+
74
+ function stringValue(value) {
75
+ if (value === undefined || value === null) return '';
76
+ if (typeof value === 'string') return value.trim();
77
+ if (typeof value === 'number' || typeof value === 'boolean') return String(value);
78
+ if (Array.isArray(value)) return value.map((item) => stringValue(item)).filter(Boolean).join('\n').trim();
79
+ if (isRecord(value)) {
80
+ if (Array.isArray(value.parts)) return stringValue(value.parts);
81
+ if (typeof value.text === 'string') return value.text.trim();
82
+ if (typeof value.content === 'string') return value.content.trim();
83
+ if (typeof value.value === 'string') return value.value.trim();
84
+ if (typeof value.memory === 'string') return value.memory.trim();
85
+ if (typeof value.fact === 'string') return value.fact.trim();
86
+ if (typeof value.summary === 'string') return value.summary.trim();
87
+ if (typeof value.message === 'string') return value.message.trim();
88
+ }
89
+ return '';
90
+ }
91
+
92
+ function textLines(value) {
93
+ if (typeof value !== 'string') return [];
94
+ return value
95
+ .split(/\r?\n/)
96
+ .map((line) => line.replace(/^[-*]\s+/, '').trim())
97
+ .filter(Boolean);
98
+ }
99
+
100
+ function firstString(record, names) {
101
+ if (!isRecord(record)) return '';
102
+ for (const name of names) {
103
+ const value = stringValue(record[name]);
104
+ if (value) return value;
105
+ }
106
+ return '';
107
+ }
108
+
109
+ function collectLimitations(input, defaults) {
110
+ const limitations = [];
111
+ for (const item of defaults) limitations.push(item);
112
+ if (isRecord(input)) {
113
+ for (const key of ['limitations', 'limits', 'warnings', 'caveats']) {
114
+ for (const value of asArray(input[key])) {
115
+ const text = stringValue(value);
116
+ if (text) limitations.push(text);
117
+ }
118
+ }
119
+ if (isRecord(input.metadata)) {
120
+ for (const value of asArray(input.metadata.limitations)) {
121
+ const text = stringValue(value);
122
+ if (text) limitations.push(text);
123
+ }
124
+ }
125
+ }
126
+ return uniqueStrings(limitations);
127
+ }
128
+
129
+ function uniqueStrings(values) {
130
+ const seen = new Set();
131
+ const out = [];
132
+ for (const value of values) {
133
+ const text = String(value ?? '').trim();
134
+ if (text.length === 0 || seen.has(text)) continue;
135
+ seen.add(text);
136
+ out.push(text);
137
+ }
138
+ return out;
139
+ }
140
+
141
+ function parseCompletenessFlag(value) {
142
+ if (value === true || value === false) return value;
143
+ if (typeof value !== 'string') return null;
144
+ const normalized = value.toLowerCase();
145
+ if (normalized === 'complete' || normalized === 'full' || normalized === 'explicit_complete') return true;
146
+ if (normalized === 'incomplete' || normalized === 'partial' || normalized === 'unknown' || normalized === 'partial_or_unknown') return false;
147
+ return null;
148
+ }
149
+
150
+ function completenessInputs(input) {
151
+ if (!isRecord(input)) return [];
152
+ return [
153
+ input.complete,
154
+ input.is_complete,
155
+ input.export_complete,
156
+ input.exportComplete,
157
+ input.completeness,
158
+ input.metadata?.complete,
159
+ input.metadata?.is_complete,
160
+ input.metadata?.export_complete,
161
+ input.metadata?.completeness
162
+ ].filter((value) => value !== undefined && value !== null);
163
+ }
164
+
165
+ function completenessEvidence(input) {
166
+ let recognized_count = 0;
167
+ let unsupported_count = 0;
168
+ let true_count = 0;
169
+ let false_count = 0;
170
+ for (const value of completenessInputs(input)) {
171
+ const parsed = parseCompletenessFlag(value);
172
+ if (parsed === null) {
173
+ unsupported_count += 1;
174
+ continue;
175
+ }
176
+ recognized_count += 1;
177
+ if (parsed) true_count += 1;
178
+ else false_count += 1;
179
+ }
180
+ const conflicting = true_count > 0 && false_count > 0;
181
+ return {
182
+ recognized_count,
183
+ unsupported_count,
184
+ conflicting,
185
+ explicit_assertion: recognized_count > 0,
186
+ complete: !conflicting && true_count > 0 && false_count === 0
187
+ };
188
+ }
189
+
190
+ function explicitCompleteness(input) {
191
+ const evidence = completenessEvidence(input);
192
+ if (evidence.conflicting) return false;
193
+ if (evidence.recognized_count === 0) return null;
194
+ return evidence.complete;
195
+ }
196
+
197
+ function completenessLimitations(input) {
198
+ const evidence = completenessEvidence(input);
199
+ if (evidence.conflicting) {
200
+ return {
201
+ complete: false,
202
+ evidence,
203
+ limitations: ['source completeness flags conflict; imported memories are treated as partial until the source is reconciled']
204
+ };
205
+ }
206
+ if (evidence.complete) return { complete: true, evidence, limitations: [] };
207
+ if (evidence.recognized_count > 0) {
208
+ return {
209
+ complete: false,
210
+ evidence,
211
+ limitations: ['source explicitly reports an incomplete export; do not treat imported memories as complete']
212
+ };
213
+ }
214
+ return {
215
+ complete: false,
216
+ evidence,
217
+ limitations: ['source did not include an explicit completeness flag; imported memories have unknown coverage']
218
+ };
219
+ }
220
+
221
+ function normalizeConfidence(value, fallback = 'medium') {
222
+ if (typeof value !== 'string') return fallback;
223
+ const normalized = value.toLowerCase().replace(/[^a-z_]/g, '_');
224
+ if (CONFIDENCE_ORDER.has(normalized)) return normalized;
225
+ if (normalized === 'certain') return 'high';
226
+ if (normalized === 'maybe' || normalized === 'inferred') return 'low';
227
+ return fallback;
228
+ }
229
+
230
+ function aggregateConfidence(candidates) {
231
+ if (candidates.length === 0) return 'low';
232
+ let lowest = 'user_confirmed';
233
+ for (const candidate of candidates) {
234
+ if ((CONFIDENCE_ORDER.get(candidate.confidence) ?? 0) < (CONFIDENCE_ORDER.get(lowest) ?? 0)) lowest = candidate.confidence;
235
+ }
236
+ return lowest;
237
+ }
238
+
239
+ function sourceRef(sourceType, path, id, fragment) {
240
+ const ref = {
241
+ source_type: sourceType,
242
+ path,
243
+ source_hash: rootOf(fragment ?? `${sourceType}:${path}:${id ?? ''}`)
244
+ };
245
+ if (id !== undefined && id !== null && String(id).length > 0) ref.source_id = String(id);
246
+ return ref;
247
+ }
248
+
249
+ const PLAINTEXT_METADATA_KEYS = new Set([
250
+ 'body',
251
+ 'content',
252
+ 'memory',
253
+ 'plaintext',
254
+ 'prompt',
255
+ 'rawmemory',
256
+ 'response',
257
+ 'summary',
258
+ 'text',
259
+ 'value'
260
+ ]);
261
+
262
+ function normalizedMetadataKey(key) {
263
+ return String(key).toLowerCase().replace(/[^a-z0-9]/g, '');
264
+ }
265
+
266
+ function metadataCommitmentKey(key) {
267
+ return `${String(key).replace(/[^A-Za-z0-9_:-]/g, '_')}_commitment`;
268
+ }
269
+
270
+ function assignMetadataValue(metadata, key, item) {
271
+ if (item !== null && !['string', 'number', 'boolean'].includes(typeof item)) return;
272
+ if (PLAINTEXT_METADATA_KEYS.has(normalizedMetadataKey(key))) {
273
+ if (item !== null) metadata[metadataCommitmentKey(key)] = rootOf(String(item));
274
+ return;
275
+ }
276
+ metadata[key] = item;
277
+ }
278
+
279
+ function normalizeMetadata(value, extra = {}) {
280
+ const metadata = {};
281
+ for (const [key, item] of Object.entries(extra)) assignMetadataValue(metadata, key, item);
282
+ if (!isRecord(value)) return metadata;
283
+ for (const [key, item] of Object.entries(value)) assignMetadataValue(metadata, key, item);
284
+ return metadata;
285
+ }
286
+
287
+ function makeCandidate({ importer, sourceType, content, source_refs, limitations, confidence, kind, metadata }) {
288
+ const normalizedContent = stringValue(content);
289
+ if (!normalizedContent) return null;
290
+ const refs = source_refs.length > 0 ? source_refs : [sourceRef(sourceType, 'unknown', undefined, normalizedContent)];
291
+ return {
292
+ schema: 'enigma.memory_candidate.v1',
293
+ candidate_id: `cand_${shortHash({ importer, refs, normalizedContent })}`,
294
+ kind: kind ?? 'fact',
295
+ content: normalizedContent,
296
+ source_refs: refs,
297
+ limitations: uniqueStrings(limitations),
298
+ confidence: normalizeConfidence(confidence),
299
+ metadata: normalizeMetadata(metadata, { importer })
300
+ };
301
+ }
302
+
303
+ function addCandidate(out, args) {
304
+ const candidate = makeCandidate(args);
305
+ if (candidate) out.push(candidate);
306
+ }
307
+
308
+ function reportFrom(importer, sourceType, input, candidates, baseLimitations, options = {}) {
309
+ const { complete, limitations: completenessLimits, evidence } = completenessLimitations(input);
310
+ const limitations = uniqueStrings([...baseLimitations, ...completenessLimits]);
311
+ const memoryCandidates = candidates.map((candidate) => ({
312
+ ...candidate,
313
+ limitations: uniqueStrings([...candidate.limitations, ...limitations])
314
+ }));
315
+ const vault_writes = writeCandidatesToVault(memoryCandidates, options);
316
+ return {
317
+ schema: REPORT_SCHEMA,
318
+ report_id: `imp_${shortHash({ importer, source: input, memoryCandidates })}`,
319
+ importer,
320
+ source_type: sourceType,
321
+ source_fingerprint: rootOf(input),
322
+ generated_at: nowFrom(options),
323
+ complete,
324
+ completeness: complete ? 'explicit_complete' : 'partial_or_unknown',
325
+ completeness_evidence: evidence,
326
+ recognized_candidate_count: memoryCandidates.length,
327
+ unsupported_item_count: evidence.unsupported_count,
328
+ private_plaintext_boundary: {
329
+ contains_private_plaintext: memoryCandidates.length > 0,
330
+ private_fields: ['memory_candidates.content'],
331
+ public_artifact_policy: 'hashes_and_commitments_only'
332
+ },
333
+ source_refs: uniqueSourceRefs(memoryCandidates.flatMap((candidate) => candidate.source_refs)),
334
+ limitations,
335
+ confidence: aggregateConfidence(memoryCandidates),
336
+ memory_candidates: memoryCandidates,
337
+ vault_writes
338
+ };
339
+ }
340
+
341
+ function uniqueSourceRefs(refs) {
342
+ const seen = new Set();
343
+ const out = [];
344
+ for (const ref of refs) {
345
+ const key = stable(ref);
346
+ if (seen.has(key)) continue;
347
+ seen.add(key);
348
+ out.push(ref);
349
+ }
350
+ return out;
351
+ }
352
+
353
+ function publicKeyForVault(vault) {
354
+ const publicKey = vault?.signingKeyPair?.publicKey ?? vault?.publicKey ?? vault?.signer?.public_key;
355
+ if (typeof publicKey === 'string') return publicKey;
356
+ if (publicKey && typeof publicKey.export === 'function') {
357
+ const exported = publicKey.export({ type: 'spki', format: 'pem' });
358
+ return Buffer.isBuffer(exported) ? exported.toString('utf8') : String(exported);
359
+ }
360
+ return undefined;
361
+ }
362
+
363
+ function writeCandidatesToVault(candidates, options) {
364
+ const vault = options?.vault;
365
+ if (!vault) return [];
366
+ const writes = [];
367
+ for (const candidate of candidates) {
368
+ const rememberArgs = {
369
+ content: candidate.content,
370
+ kind: candidate.kind,
371
+ source_refs: candidate.source_refs,
372
+ confidence: candidate.confidence,
373
+ limitations: candidate.limitations,
374
+ metadata: {
375
+ candidate_id: candidate.candidate_id,
376
+ importer: candidate.metadata.importer,
377
+ limitations_hash: rootOf(candidate.limitations)
378
+ },
379
+ now: options.now
380
+ };
381
+ const result = typeof vault.remember === 'function'
382
+ ? vault.remember(rememberArgs)
383
+ : remember({ vault, ...rememberArgs });
384
+ const receipt = isRecord(result.receipt) ? result.receipt : undefined;
385
+ const receiptPublicKey = publicKeyForVault(vault);
386
+ writes.push({
387
+ candidate_id: candidate.candidate_id,
388
+ memory_addr: result.memory_addr,
389
+ receipt_hash: rootOf(result.receipt),
390
+ ...(typeof result.event?.event_id === 'string' ? { event_id: result.event.event_id } : {}),
391
+ ...(typeof receiptPublicKey === 'string' ? { receipt_public_key: receiptPublicKey } : {}),
392
+ ...(receipt ? { receipt } : {})
393
+ });
394
+ }
395
+ return writes;
396
+ }
397
+
398
+ function pathsFromMapping(mapping) {
399
+ if (!isRecord(mapping)) return [];
400
+ const paths = [];
401
+ for (const [key, node] of Object.entries(mapping)) {
402
+ const message = node?.message;
403
+ const role = message?.author?.role ?? message?.role;
404
+ const content = stringValue(message?.content ?? message);
405
+ if (content) paths.push({ key, role, message, content });
406
+ }
407
+ return paths;
408
+ }
409
+
410
+ export function importChatGptExport(input, options = {}) {
411
+ const parsed = parseMaybeJson(input);
412
+ const sourceType = 'chatgpt_export';
413
+ const limitations = collectLimitations(parsed, [
414
+ 'ChatGPT exports may omit deleted chats, disabled memory, workspace policy state, and server-side personalization not present in the file',
415
+ 'conversation-derived candidates are inferred from transcript text and are not equivalent to provider-native memory assertions'
416
+ ]);
417
+ const candidates = [];
418
+ const memoryItems = [...asArray(parsed?.memories), ...asArray(parsed?.memory?.items), ...asArray(parsed?.user_memory), ...asArray(parsed?.memory)];
419
+ memoryItems.forEach((item, index) => {
420
+ const content = firstString(item, ['memory', 'content', 'text', 'value', 'summary']) || stringValue(item);
421
+ addCandidate(candidates, {
422
+ importer: 'importChatGptExport',
423
+ sourceType,
424
+ content,
425
+ source_refs: [sourceRef(sourceType, `memories/${index}`, item?.id ?? item?.memory_id, item)],
426
+ limitations,
427
+ confidence: item?.confidence ?? 'medium',
428
+ kind: item?.kind ?? 'preference',
429
+ metadata: item
430
+ });
431
+ });
432
+ for (const [conversationIndex, conversation] of asArray(parsed?.conversations).entries()) {
433
+ const messages = Array.isArray(conversation?.messages)
434
+ ? conversation.messages.map((message, messageIndex) => ({ key: messageIndex, message, role: message.role ?? message.author?.role, content: stringValue(message.content ?? message) }))
435
+ : pathsFromMapping(conversation?.mapping);
436
+ for (const message of messages) {
437
+ if (message.role && message.role !== 'user') continue;
438
+ addCandidate(candidates, {
439
+ importer: 'importChatGptExport',
440
+ sourceType,
441
+ content: message.content,
442
+ source_refs: [sourceRef(sourceType, `conversations/${conversationIndex}/messages/${message.key}`, conversation?.id, message.message)],
443
+ limitations,
444
+ confidence: 'low',
445
+ kind: 'conversation_observation',
446
+ metadata: { conversation_id: conversation?.id, role: message.role ?? 'unknown' }
447
+ });
448
+ }
449
+ }
450
+ if (typeof parsed === 'string') {
451
+ textLines(parsed).forEach((line, index) => addCandidate(candidates, {
452
+ importer: 'importChatGptExport',
453
+ sourceType,
454
+ content: line,
455
+ source_refs: [sourceRef(sourceType, `text/${index}`, undefined, line)],
456
+ limitations,
457
+ confidence: 'low',
458
+ kind: 'text_line',
459
+ metadata: { text_import: true }
460
+ }));
461
+ }
462
+ return reportFrom('importChatGptExport', sourceType, parsed, candidates, limitations, options);
463
+ }
464
+
465
+ export function importClaudeMemory(input, options = {}) {
466
+ const parsed = parseMaybeJson(input);
467
+ const sourceType = 'claude_memory';
468
+ const limitations = collectLimitations(parsed, [
469
+ 'Claude memory exports may be workspace-scoped and can omit deleted, expired, or policy-hidden memories',
470
+ 'project instructions and conversation context are not proof of durable memory unless exported as explicit memory records'
471
+ ]);
472
+ const candidates = [];
473
+ const items = [
474
+ ...asArray(parsed?.memories),
475
+ ...asArray(parsed?.memory),
476
+ ...asArray(parsed?.preferences),
477
+ ...asArray(parsed?.facts),
478
+ ...asArray(parsed?.entries)
479
+ ];
480
+ items.forEach((item, index) => addCandidate(candidates, {
481
+ importer: 'importClaudeMemory',
482
+ sourceType,
483
+ content: firstString(item, ['memory', 'content', 'text', 'value', 'fact', 'preference']) || stringValue(item),
484
+ source_refs: [sourceRef(sourceType, `entries/${index}`, item?.id ?? item?.uuid, item)],
485
+ limitations,
486
+ confidence: item?.confidence ?? 'medium',
487
+ kind: item?.kind ?? 'preference',
488
+ metadata: item
489
+ }));
490
+ if (typeof parsed === 'string') {
491
+ textLines(parsed).forEach((line, index) => addCandidate(candidates, {
492
+ importer: 'importClaudeMemory',
493
+ sourceType,
494
+ content: line,
495
+ source_refs: [sourceRef(sourceType, `text/${index}`, undefined, line)],
496
+ limitations,
497
+ confidence: 'low',
498
+ kind: 'text_line',
499
+ metadata: { text_import: true }
500
+ }));
501
+ }
502
+ return reportFrom('importClaudeMemory', sourceType, parsed, candidates, limitations, options);
503
+ }
504
+
505
+ export function importMem0Export(input, options = {}) {
506
+ const parsed = parseMaybeJson(input);
507
+ const sourceType = 'mem0_export';
508
+ const limitations = collectLimitations(parsed, [
509
+ 'Mem0 exports preserve exported records only; scoring, deduplication, deletion history, and application-specific filters may not be complete',
510
+ 'Mem0 provider-native memory remains a cache and is not canonical custody after import'
511
+ ]);
512
+ const candidates = [];
513
+ const items = [...asArray(parsed?.memories), ...asArray(parsed?.results), ...asArray(parsed?.items), ...asArray(parsed)];
514
+ items.forEach((item, index) => addCandidate(candidates, {
515
+ importer: 'importMem0Export',
516
+ sourceType,
517
+ content: firstString(item, ['memory', 'content', 'text', 'value']) || stringValue(item),
518
+ source_refs: [sourceRef(sourceType, `memories/${index}`, item?.id ?? item?.memory_id, item)],
519
+ limitations,
520
+ confidence: item?.confidence ?? (item?.score === undefined ? 'medium' : scoreConfidence(item.score)),
521
+ kind: item?.kind ?? 'fact',
522
+ metadata: { ...normalizeMetadata(item), user_id: item?.user_id, agent_id: item?.agent_id }
523
+ }));
524
+ return reportFrom('importMem0Export', sourceType, parsed, candidates, limitations, options);
525
+ }
526
+
527
+ function scoreConfidence(score) {
528
+ if (typeof score !== 'number') return 'medium';
529
+ if (score >= 0.8) return 'high';
530
+ if (score >= 0.45) return 'medium';
531
+ return 'low';
532
+ }
533
+
534
+ export function importLettaAgentFile(input, options = {}) {
535
+ const parsed = parseMaybeJson(input);
536
+ const sourceType = 'letta_agent_file';
537
+ const limitations = collectLimitations(parsed, [
538
+ 'Letta agent files describe one exported agent state and may not include full external tool state, deleted archival rows, or recall history outside the file',
539
+ 'agent memory blocks can be prompts or instructions; they are imported as candidates rather than confirmed personal facts'
540
+ ]);
541
+ const candidates = [];
542
+ const blocks = [
543
+ ...asArray(parsed?.memory?.blocks),
544
+ ...asArray(parsed?.memory_blocks),
545
+ ...asArray(parsed?.core_memory),
546
+ ...asArray(parsed?.agent_state?.memory?.blocks)
547
+ ];
548
+ blocks.forEach((block, index) => addCandidate(candidates, {
549
+ importer: 'importLettaAgentFile',
550
+ sourceType,
551
+ content: firstString(block, ['value', 'content', 'text', 'memory']) || stringValue(block),
552
+ source_refs: [sourceRef(sourceType, `memory_blocks/${index}`, block?.id ?? block?.label, block)],
553
+ limitations,
554
+ confidence: 'medium',
555
+ kind: block?.label ?? block?.name ?? 'agent_memory_block',
556
+ metadata: { label: block?.label ?? block?.name, agent_id: parsed?.id ?? parsed?.agent_id }
557
+ }));
558
+ const archival = [...asArray(parsed?.archival_memory), ...asArray(parsed?.archivalMemory), ...asArray(parsed?.agent_state?.archival_memory)];
559
+ archival.forEach((entry, index) => addCandidate(candidates, {
560
+ importer: 'importLettaAgentFile',
561
+ sourceType,
562
+ content: firstString(entry, ['text', 'content', 'memory', 'value']) || stringValue(entry),
563
+ source_refs: [sourceRef(sourceType, `archival_memory/${index}`, entry?.id, entry)],
564
+ limitations,
565
+ confidence: 'medium',
566
+ kind: 'archival_memory',
567
+ metadata: { agent_id: parsed?.id ?? parsed?.agent_id }
568
+ }));
569
+ return reportFrom('importLettaAgentFile', sourceType, parsed, candidates, limitations, options);
570
+ }
571
+
572
+ export function importLangGraphStore(input, options = {}) {
573
+ const parsed = parseMaybeJson(input);
574
+ const sourceType = 'langgraph_store';
575
+ const limitations = collectLimitations(parsed, [
576
+ 'LangGraph store exports reflect selected namespaces only; TTL, tombstones, checkpointer state, and application-level filters may be absent',
577
+ 'stored values are schema-flexible, so non-memory application state is imported only as low-confidence candidates when it contains text'
578
+ ]);
579
+ const candidates = [];
580
+ const items = [...asArray(parsed?.items), ...asArray(parsed?.store), ...asArray(parsed?.records), ...asArray(parsed)];
581
+ items.forEach((item, index) => {
582
+ const value = item?.value ?? item?.document ?? item;
583
+ addCandidate(candidates, {
584
+ importer: 'importLangGraphStore',
585
+ sourceType,
586
+ content: firstString(value, ['memory', 'content', 'text', 'summary', 'value']) || stringValue(value),
587
+ source_refs: [sourceRef(sourceType, `items/${index}`, item?.key ?? item?.id, item)],
588
+ limitations,
589
+ confidence: item?.confidence ?? 'low',
590
+ kind: item?.kind ?? 'store_value',
591
+ metadata: { namespace: Array.isArray(item?.namespace) ? item.namespace.join('/') : item?.namespace, key: item?.key }
592
+ });
593
+ });
594
+ return reportFrom('importLangGraphStore', sourceType, parsed, candidates, limitations, options);
595
+ }
596
+
597
+ export function importZepGraphitiExport(input, options = {}) {
598
+ const parsed = parseMaybeJson(input);
599
+ const sourceType = 'zep_graphiti_export';
600
+ const limitations = collectLimitations(parsed, [
601
+ 'Zep Graphiti exports are graph projections; extracted facts may omit original conversation turns, temporal invalidations, and edge provenance not included in the export',
602
+ 'graph entities and edges are imported as candidates, not as proof that the source conversation remains complete'
603
+ ]);
604
+ const candidates = [];
605
+ const nodes = [...asArray(parsed?.nodes), ...asArray(parsed?.entities), ...asArray(parsed?.episodes)];
606
+ nodes.forEach((node, index) => addCandidate(candidates, {
607
+ importer: 'importZepGraphitiExport',
608
+ sourceType,
609
+ content: firstString(node, ['fact', 'summary', 'description', 'content', 'text', 'name']) || stringValue(node),
610
+ source_refs: [sourceRef(sourceType, `nodes/${index}`, node?.uuid ?? node?.id ?? node?.name, node)],
611
+ limitations,
612
+ confidence: node?.confidence ?? scoreConfidence(node?.score),
613
+ kind: node?.type ?? 'graph_node',
614
+ metadata: { uuid: node?.uuid, group_id: node?.group_id, valid_at: node?.valid_at }
615
+ }));
616
+ const edges = [...asArray(parsed?.edges), ...asArray(parsed?.relations), ...asArray(parsed?.facts)];
617
+ edges.forEach((edge, index) => addCandidate(candidates, {
618
+ importer: 'importZepGraphitiExport',
619
+ sourceType,
620
+ content: firstString(edge, ['fact', 'summary', 'description', 'content', 'text', 'name']) || graphEdgeText(edge),
621
+ source_refs: [sourceRef(sourceType, `edges/${index}`, edge?.uuid ?? edge?.id, edge)],
622
+ limitations,
623
+ confidence: edge?.confidence ?? scoreConfidence(edge?.score),
624
+ kind: edge?.type ?? 'graph_edge',
625
+ metadata: { uuid: edge?.uuid, source: edge?.source, target: edge?.target }
626
+ }));
627
+ return reportFrom('importZepGraphitiExport', sourceType, parsed, candidates, limitations, options);
628
+ }
629
+
630
+ function graphEdgeText(edge) {
631
+ if (!isRecord(edge)) return '';
632
+ const source = stringValue(edge.source ?? edge.source_node ?? edge.from);
633
+ const relation = stringValue(edge.relation ?? edge.name ?? edge.predicate);
634
+ const target = stringValue(edge.target ?? edge.target_node ?? edge.to);
635
+ return [source, relation, target].filter(Boolean).join(' ');
636
+ }
637
+
638
+ function uniqueCandidates(candidates) {
639
+ const seen = new Set();
640
+ const out = [];
641
+ for (const candidate of candidates.filter(isRecord)) {
642
+ const key = candidate.candidate_id ?? rootOf(candidate);
643
+ if (seen.has(key)) continue;
644
+ seen.add(key);
645
+ out.push(candidate);
646
+ }
647
+ return out;
648
+ }
649
+
650
+ function collectPayloadCandidates(payload, reports) {
651
+ return uniqueCandidates([
652
+ ...asArray(payload?.memory_candidates).filter(isRecord),
653
+ ...reports.flatMap((report) => asArray(report.memory_candidates).filter(isRecord))
654
+ ]);
655
+ }
656
+
657
+ function candidateReportVaultWrites(memoryCandidates, reports) {
658
+ const candidateIds = new Set(memoryCandidates.map((candidate) => candidate.candidate_id).filter(Boolean));
659
+ return reports
660
+ .flatMap((report) => asArray(report.vault_writes).filter(isRecord))
661
+ .filter((write) => (
662
+ candidateIds.has(write.candidate_id)
663
+ && typeof write.memory_addr === 'string'
664
+ && typeof write.receipt_hash === 'string'
665
+ ));
666
+ }
667
+
668
+ function canonicalVaultWriteReceipt(write) {
669
+ const receipt = write.receipt;
670
+ if (!isRecord(receipt)
671
+ || receipt.schema !== 'enigma.receipt.v1'
672
+ || receipt.operation !== 'create'
673
+ || receipt.memory_addr !== write.memory_addr
674
+ || typeof receipt.receipt_id !== 'string'
675
+ || typeof receipt.event_hash !== 'string'
676
+ || typeof receipt.active_set_root !== 'string'
677
+ || typeof receipt.receipt_log_root !== 'string'
678
+ || typeof receipt.previous_receipt_hash !== 'string'
679
+ || typeof receipt.timestamp !== 'string'
680
+ || !Number.isInteger(receipt.sequence)
681
+ || !isRecord(receipt.signer)
682
+ || receipt.signer.alg !== 'Ed25519'
683
+ || typeof receipt.signer.key_id !== 'string'
684
+ || !isRecord(receipt.signature)
685
+ || receipt.signature.alg !== 'Ed25519'
686
+ || typeof receipt.signature.value !== 'string'
687
+ || typeof write.receipt_public_key !== 'string'
688
+ || rootOf(receipt) !== write.receipt_hash) {
689
+ return false;
690
+ }
691
+ return verifyReceipt({
692
+ receipt,
693
+ publicKey: write.receipt_public_key,
694
+ expectedSignerKeyId: receipt.signer.key_id
695
+ }).ok;
696
+ }
697
+
698
+ function collectReportVaultWrites(reports) {
699
+ const seen = new Set();
700
+ const out = [];
701
+ for (const write of reports.flatMap((report) => asArray(report.vault_writes).filter(isRecord))) {
702
+ if (typeof write.candidate_id !== 'string' || typeof write.memory_addr !== 'string' || typeof write.receipt_hash !== 'string') continue;
703
+ if (!canonicalVaultWriteReceipt(write)) continue;
704
+ const key = `${write.candidate_id}\0${write.memory_addr}\0${write.receipt_hash}`;
705
+ if (seen.has(key)) continue;
706
+ seen.add(key);
707
+ out.push({
708
+ candidate_id: write.candidate_id,
709
+ memory_addr: write.memory_addr,
710
+ receipt_hash: write.receipt_hash,
711
+ ...(typeof write.event_id === 'string' ? { event_id: write.event_id } : {})
712
+ });
713
+ }
714
+ return out;
715
+ }
716
+
717
+ function mappedVaultWrites(memoryCandidates, reports) {
718
+ const candidateIds = new Set(memoryCandidates.map((candidate) => candidate.candidate_id).filter(Boolean));
719
+ return collectReportVaultWrites(reports).filter((write) => candidateIds.has(write.candidate_id));
720
+ }
721
+
722
+ function vaultWriteCounts(memoryCandidates, vaultWrites) {
723
+ const candidateIds = new Set(memoryCandidates.map((candidate) => candidate.candidate_id).filter(Boolean));
724
+ const canonicalIds = new Set();
725
+ for (const write of vaultWrites) {
726
+ if (candidateIds.has(write.candidate_id)) canonicalIds.add(write.candidate_id);
727
+ }
728
+ return {
729
+ canonical_candidate_count: canonicalIds.size,
730
+ candidate_only_count: Math.max(0, candidateIds.size - canonicalIds.size),
731
+ vault_write_count: vaultWrites.length
732
+ };
733
+ }
734
+
735
+ function custodyStatus(counts) {
736
+ if (counts.candidate_only_count > 0) return 'candidate_only_noncanonical';
737
+ if (counts.canonical_candidate_count > 0) return 'vault_write_backed';
738
+ return 'empty';
739
+ }
740
+
741
+ function collectCustodyLimitationCodes(memoryCandidates, reports, vaultWrites) {
742
+ const codes = [];
743
+ const counts = vaultWriteCounts(memoryCandidates, vaultWrites);
744
+ if (counts.candidate_only_count > 0) codes.push('candidate_only_without_vault_write');
745
+ for (const write of candidateReportVaultWrites(memoryCandidates, reports)) {
746
+ if (!isRecord(write.receipt)) {
747
+ codes.push('vault_write_receipt_missing');
748
+ } else if (!canonicalVaultWriteReceipt(write)) {
749
+ codes.push('vault_write_receipt_unverified');
750
+ }
751
+ }
752
+ return uniqueStrings(codes);
753
+ }
754
+
755
+ function custodyLimitationTexts(codes) {
756
+ const texts = [];
757
+ if (codes.includes('candidate_only_without_vault_write')) {
758
+ texts.push('candidate-only memories are noncanonical until written to a vault; active_set_root commits only vault memory_addr values');
759
+ }
760
+ if (codes.includes('vault_write_receipt_missing')) {
761
+ texts.push('vault_writes without embedded receipt objects are unverified; canonical roots ignore receipt_hash-only claims');
762
+ }
763
+ if (codes.includes('vault_write_receipt_unverified')) {
764
+ texts.push('vault_writes with mismatched receipt evidence are unverified; canonical roots ignore unproven custody claims');
765
+ }
766
+ return texts;
767
+ }
768
+
769
+ function sourceRefRoot(memoryCandidates) {
770
+ return new MerkleSet(memoryCandidates.flatMap((candidate) => candidate.source_refs ?? []).map((ref) => rootOf(ref))).root();
771
+ }
772
+
773
+ function sourceLimitationsRoot(reports, memoryCandidates, extraLimitations = []) {
774
+ return rootOf(uniqueStrings([
775
+ ...asArray(extraLimitations),
776
+ ...reports.flatMap((report) => asArray(report.limitations)),
777
+ ...memoryCandidates.flatMap((candidate) => asArray(candidate.limitations))
778
+ ]));
779
+ }
780
+
781
+ function completenessSummary(reports) {
782
+ return reports.length > 0 && reports.every((report) => report.complete === true && report.completeness === 'explicit_complete')
783
+ ? 'explicit_complete'
784
+ : 'partial_or_unknown';
785
+ }
786
+
787
+ function reportCompletenessConflicts(reports) {
788
+ const errors = [];
789
+ reports.forEach((report, index) => {
790
+ if (report.complete === true && report.completeness !== 'explicit_complete') errors.push(`report ${index} completeness flags conflict`);
791
+ if (report.complete === false && report.completeness === 'explicit_complete') errors.push(`report ${index} completeness flags conflict`);
792
+ if (report.completeness_evidence?.conflicting === true) errors.push(`report ${index} completeness evidence conflicts`);
793
+ });
794
+ return errors;
795
+ }
796
+
797
+ function sameStringArray(a, b) {
798
+ return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((value, index) => value === b[index]);
799
+ }
800
+
801
+ function trustAnchorFromOptions(manifest, options = {}) {
802
+ const directDescriptor = options.trustDescriptor ?? options.trustedDescriptor ?? options.issuerDescriptor ?? options.descriptor;
803
+ if (directDescriptor !== undefined) return { source: 'descriptor', trustDescriptor: directDescriptor, errors: [] };
804
+ if (options.publicKey !== undefined) return { source: 'publicKey', publicKey: options.publicKey, errors: [] };
805
+ const bundle = options.trustBundle ?? options.trustedBundle;
806
+ if (isRecord(bundle)) {
807
+ if (Array.isArray(bundle.accepted_algorithms) && !bundle.accepted_algorithms.includes('Ed25519')) {
808
+ return { source: 'none', errors: ['trusted bundle does not accept Ed25519 capsule manifests'] };
809
+ }
810
+ const keyId = manifest?.signature?.key_id;
811
+ const issuer = asArray(bundle.trusted_issuers).find((entry) => (
812
+ isRecord(entry)
813
+ && entry.issuer_id === manifest?.issuer
814
+ && entry.key_id === keyId
815
+ && typeof entry.public_key === 'string'
816
+ ));
817
+ if (issuer) {
818
+ return {
819
+ source: 'bundle',
820
+ trustDescriptor: {
821
+ node_id: issuer.issuer_id,
822
+ key: { alg: 'Ed25519', key_id: issuer.key_id, public_key: issuer.public_key },
823
+ public_key: issuer.public_key
824
+ },
825
+ bundleIssuer: issuer,
826
+ errors: []
827
+ };
828
+ }
829
+ return { source: 'none', errors: ['trusted bundle does not contain the capsule issuer key'] };
830
+ }
831
+ return { source: 'none', errors: ['trusted descriptor or trust bundle is required'] };
832
+ }
833
+
834
+ function trustAnchorMetadataErrors(anchor, embedded, manifest) {
835
+ if (!isRecord(embedded)) return [];
836
+ if (anchor.source === 'descriptor') {
837
+ return rootOf(anchor.trustDescriptor) === rootOf(embedded) ? [] : ['verifier metadata trust descriptor does not match trusted descriptor'];
838
+ }
839
+ if (anchor.source === 'bundle') {
840
+ const embeddedKey = embedded.key ?? {};
841
+ return embedded.node_id === manifest?.issuer
842
+ && embeddedKey.key_id === anchor.bundleIssuer?.key_id
843
+ && embeddedKey.public_key === anchor.bundleIssuer?.public_key
844
+ ? []
845
+ : ['verifier metadata trust descriptor does not match trusted bundle'];
846
+ }
847
+ if (anchor.source === 'publicKey') {
848
+ return embedded.key?.public_key === anchor.publicKey ? [] : ['verifier metadata trust descriptor does not match trusted public key'];
849
+ }
850
+ return [];
851
+ }
852
+
853
+ function capsuleVerificationErrors({ capsule, manifest, verifierMetadata, payload, reports, memoryCandidates, vaultWrites, active_set_root, receipt_log_root, payload_hash }) {
854
+ const errors = [];
855
+ if (!isRecord(payload)) errors.push('capsule payload must be an object');
856
+ if (!isRecord(verifierMetadata)) errors.push('verifier metadata must be an object');
857
+ if (payload_hash === undefined) return errors;
858
+ if (capsule.payload_hash !== undefined && capsule.payload_hash !== payload_hash) errors.push('capsule payload hash mismatch');
859
+ if (verifierMetadata.payload_hash !== payload_hash) errors.push('verifier metadata payload hash mismatch');
860
+ if (manifest?.encrypted_payload_hash !== payload_hash) errors.push('manifest payload commitment mismatch');
861
+ if (verifierMetadata.candidate_count !== memoryCandidates.length) errors.push('candidate count mismatch');
862
+ if (!sameStringArray(verifierMetadata.import_report_hashes, reports.map((report) => rootOf(report)))) errors.push('import report hashes mismatch');
863
+ if (verifierMetadata.source_ref_root !== sourceRefRoot(memoryCandidates)) errors.push('source ref root mismatch');
864
+ const counts = vaultWriteCounts(memoryCandidates, vaultWrites);
865
+ const custodyLimitationCodes = collectCustodyLimitationCodes(memoryCandidates, reports, vaultWrites);
866
+ const custodyLimitations = custodyLimitationTexts(custodyLimitationCodes);
867
+ if (verifierMetadata.limitations_root !== sourceLimitationsRoot(reports, memoryCandidates, custodyLimitations)) errors.push('limitations root mismatch');
868
+ if (verifierMetadata.active_set_root !== active_set_root) errors.push('active set root mismatch');
869
+ if (verifierMetadata.receipt_log_root !== receipt_log_root) errors.push('receipt log root mismatch');
870
+ if (verifierMetadata.canonical_candidate_count !== undefined && verifierMetadata.canonical_candidate_count !== counts.canonical_candidate_count) errors.push('canonical candidate count mismatch');
871
+ if (verifierMetadata.candidate_only_count !== undefined && verifierMetadata.candidate_only_count !== counts.candidate_only_count) errors.push('candidate-only count mismatch');
872
+ const status = custodyStatus(counts);
873
+ if (verifierMetadata.custody_status !== status) errors.push('custody status mismatch');
874
+ if (!sameStringArray(verifierMetadata.custody_limitation_codes, custodyLimitationCodes)) errors.push('custody limitation codes mismatch');
875
+ const completeness = completenessSummary(reports);
876
+ if (verifierMetadata.completeness !== completeness) errors.push('completeness summary mismatch');
877
+ errors.push(...reportCompletenessConflicts(reports));
878
+ return errors;
879
+ }
880
+
881
+ export function exportEnigmaCapsule(args = {}) {
882
+ const reports = asArray(args.reports ?? args.report).filter(isRecord);
883
+ const memoryCandidates = uniqueCandidates([
884
+ ...asArray(args.memory_candidates ?? args.memoryCandidates ?? args.candidates).filter(isRecord),
885
+ ...reports.flatMap((report) => asArray(report.memory_candidates).filter(isRecord))
886
+ ]);
887
+ const exportedAt = nowFrom(args);
888
+ const vaultWrites = mappedVaultWrites(memoryCandidates, reports);
889
+ const active_set_root = new MerkleSet(vaultWrites.map((write) => write.memory_addr)).root();
890
+ const receipt_log_root = new MerkleSet(vaultWrites.map((write) => write.receipt_hash)).root();
891
+ const counts = vaultWriteCounts(memoryCandidates, vaultWrites);
892
+ const status = custodyStatus(counts);
893
+ const custodyLimitationCodes = collectCustodyLimitationCodes(memoryCandidates, reports, vaultWrites);
894
+ const custodyLimitations = custodyLimitationTexts(custodyLimitationCodes);
895
+ const payload = {
896
+ schema: CAPSULE_PAYLOAD_SCHEMA,
897
+ exported_at: exportedAt,
898
+ private_plaintext_boundary: {
899
+ contains_private_plaintext: memoryCandidates.length > 0,
900
+ private_fields: ['memory_candidates.content', 'reports.memory_candidates.content'],
901
+ public_artifact_policy: 'manifest_and_verifier_metadata_only'
902
+ },
903
+ reports,
904
+ memory_candidates: memoryCandidates
905
+ };
906
+ const payload_hash = rootOf(payload);
907
+ const node = args.node ?? createMeshNode({ created_at: exportedAt });
908
+ const holder = args.holder ?? args.holder_id ?? args.holderId ?? 'holder:importer';
909
+ const limitations_root = sourceLimitationsRoot(reports, memoryCandidates, custodyLimitations);
910
+ const manifest = createCapsuleManifest({
911
+ node,
912
+ issuer: args.issuer ?? node.node_id,
913
+ holder,
914
+ encrypted_payload_hash: payload_hash,
915
+ receipt_log_root,
916
+ active_set_root,
917
+ owner_scope_hash: rootOf({ holder, importer_reports: reports.map((report) => report.report_id), candidates: memoryCandidates.map((candidate) => candidate.candidate_id) }),
918
+ issued_at: exportedAt,
919
+ expires_at: args.expires_at ?? args.expiresAt ?? DEFAULT_EXPIRY
920
+ });
921
+ const verifier_metadata = {
922
+ schema: VERIFIER_METADATA_SCHEMA,
923
+ capsule_id: manifest.capsule_id,
924
+ payload_schema: CAPSULE_PAYLOAD_SCHEMA,
925
+ payload_hash,
926
+ import_report_hashes: reports.map((report) => rootOf(report)),
927
+ candidate_count: memoryCandidates.length,
928
+ source_ref_root: sourceRefRoot(memoryCandidates),
929
+ limitations_root,
930
+ completeness: completenessSummary(reports),
931
+ custody_status: status,
932
+ custody_limitation_codes: custodyLimitationCodes,
933
+ canonical_candidate_count: counts.canonical_candidate_count,
934
+ candidate_only_count: counts.candidate_only_count,
935
+ vault_write_count: counts.vault_write_count,
936
+ vault_writes: vaultWrites,
937
+ private_plaintext_boundary: {
938
+ payload_contains_private_plaintext: memoryCandidates.length > 0,
939
+ public_artifacts_contain_plaintext: false,
940
+ public_artifact_policy: 'hashes_roots_counts_and_trusted_descriptor_only'
941
+ },
942
+ active_set_root,
943
+ receipt_log_root,
944
+ trust_descriptor: node.trust_descriptor
945
+ };
946
+ return {
947
+ schema: CAPSULE_SCHEMA,
948
+ capsule_id: manifest.capsule_id,
949
+ exported_at: exportedAt,
950
+ manifest,
951
+ verifier_metadata,
952
+ payload_hash,
953
+ payload,
954
+ public_artifacts: { manifest, verifier_metadata }
955
+ };
956
+ }
957
+
958
+ export function importEnigmaCapsule(capsule, options = {}) {
959
+ if (!isRecord(capsule)) throw new TypeError('capsule must be an object');
960
+ const manifest = capsule.manifest;
961
+ const verifierMetadata = capsule.verifier_metadata ?? capsule.verifierMetadata ?? {};
962
+ const payload = capsule.payload;
963
+ const reports = asArray(payload?.reports).filter(isRecord);
964
+ const memoryCandidates = collectPayloadCandidates(payload, reports);
965
+ const vaultWritesFromReports = mappedVaultWrites(memoryCandidates, reports);
966
+ const payload_hash = isRecord(payload) ? rootOf(payload) : undefined;
967
+ const active_set_root = new MerkleSet(vaultWritesFromReports.map((write) => write.memory_addr)).root();
968
+ const receipt_log_root = new MerkleSet(vaultWritesFromReports.map((write) => write.receipt_hash)).root();
969
+ const trustAnchor = trustAnchorFromOptions(manifest, options);
970
+ const verification = verifyCapsuleManifest(manifest, {
971
+ trustDescriptor: trustAnchor.trustDescriptor,
972
+ publicKey: trustAnchor.publicKey,
973
+ expectedReceiptLogRoot: receipt_log_root,
974
+ expectedActiveSetRoot: active_set_root,
975
+ now: options.now ?? DEFAULT_NOW
976
+ });
977
+ const metadataErrors = [
978
+ ...trustAnchor.errors,
979
+ ...trustAnchorMetadataErrors(trustAnchor, verifierMetadata.trust_descriptor, manifest),
980
+ ...capsuleVerificationErrors({
981
+ capsule,
982
+ manifest,
983
+ verifierMetadata,
984
+ payload,
985
+ reports,
986
+ memoryCandidates,
987
+ vaultWrites: vaultWritesFromReports,
988
+ active_set_root,
989
+ receipt_log_root,
990
+ payload_hash
991
+ })
992
+ ];
993
+ const ok = verification.ok && metadataErrors.length === 0;
994
+ const vault_writes = ok ? writeCandidatesToVault(memoryCandidates, options) : [];
995
+ const limitations = uniqueStrings([
996
+ ...metadataErrors,
997
+ ...verification.errors,
998
+ ...reports.flatMap((report) => asArray(report.limitations)),
999
+ ...memoryCandidates.flatMap((candidate) => asArray(candidate.limitations))
1000
+ ]);
1001
+ const completeness = completenessSummary(reports);
1002
+ return {
1003
+ schema: 'enigma.import_capsule_result.v1',
1004
+ ok,
1005
+ capsule_id: manifest?.capsule_id ?? capsule.capsule_id,
1006
+ manifest_ok: verification.ok,
1007
+ payload_hash_ok: payload_hash !== undefined && verifierMetadata.payload_hash === payload_hash && (capsule.payload_hash === undefined || capsule.payload_hash === payload_hash),
1008
+ manifest_payload_hash_ok: payload_hash !== undefined && manifest?.encrypted_payload_hash === payload_hash,
1009
+ verifier_metadata_ok: metadataErrors.length === 0,
1010
+ verification: { ...verification, metadata_errors: metadataErrors },
1011
+ complete: completeness === 'explicit_complete',
1012
+ completeness,
1013
+ limitations,
1014
+ memory_candidates: memoryCandidates,
1015
+ vault_writes
1016
+ };
1017
+ }
1018
+
1019
+ export function runImporterDemo(options = {}) {
1020
+ const shared = { ...options, now: options.now ?? DEFAULT_NOW };
1021
+ const chatgpt = importChatGptExport({
1022
+ export_complete: false,
1023
+ limitations: ['demo ChatGPT source omits deleted chats'],
1024
+ memories: [{ id: 'gpt-memory-1', memory: 'Prefers concise answers for release notes.', confidence: 'high' }]
1025
+ }, shared);
1026
+ const claude = importClaudeMemory({
1027
+ is_complete: false,
1028
+ limitations: ['demo Claude source is project scoped'],
1029
+ memories: [{ id: 'claude-memory-1', text: 'Uses formal tone for board updates.' }]
1030
+ }, shared);
1031
+ const mem0 = importMem0Export({
1032
+ complete: false,
1033
+ limitations: ['demo Mem0 source is filtered to one user'],
1034
+ memories: [{ id: 'mem0-1', memory: 'Likes architecture diagrams before implementation.', score: 0.84 }]
1035
+ }, shared);
1036
+ const letta = importLettaAgentFile({
1037
+ export_complete: false,
1038
+ limitations: ['demo Letta file excludes tool execution history'],
1039
+ agent_id: 'agent-demo',
1040
+ memory: { blocks: [{ label: 'human', value: 'User wants security caveats called out explicitly.' }] }
1041
+ }, shared);
1042
+ const langgraph = importLangGraphStore({
1043
+ is_complete: false,
1044
+ limitations: ['demo LangGraph export includes only the memory namespace'],
1045
+ items: [{ namespace: ['memories'], key: 'one', value: { text: 'Escalate uncertain claims with evidence.' } }]
1046
+ }, shared);
1047
+ const graphiti = importZepGraphitiExport({
1048
+ complete: false,
1049
+ limitations: ['demo Graphiti graph omits source episodes'],
1050
+ edges: [{ uuid: 'edge-1', source: 'user', relation: 'prefers', target: 'offline verification', score: 0.7 }]
1051
+ }, shared);
1052
+ const reports = { chatgpt, claude, mem0, letta, langgraph, graphiti };
1053
+ const capsule = exportEnigmaCapsule({ reports: Object.values(reports), now: shared.now });
1054
+ const capsuleImport = importEnigmaCapsule(capsule, { now: shared.now, trustDescriptor: capsule.verifier_metadata.trust_descriptor });
1055
+ const importer_ok = Object.values(reports).every((report) => report.memory_candidates.length > 0 && report.limitations.length > 0 && report.complete === false);
1056
+ const preserved_limitations = Object.values(reports).every((report) => report.memory_candidates.every((candidate) => candidate.limitations.length >= report.limitations.length));
1057
+ return {
1058
+ ok: importer_ok && preserved_limitations && capsuleImport.ok,
1059
+ importers: Object.fromEntries(Object.entries(reports).map(([name, report]) => [name, {
1060
+ ok: report.memory_candidates.length > 0,
1061
+ complete: report.complete,
1062
+ limitations: report.limitations,
1063
+ candidate_count: report.memory_candidates.length
1064
+ }])),
1065
+ capsule: {
1066
+ export_ok: capsule.manifest?.schema === 'enigma.capsule_manifest.v1' && capsule.payload_hash === capsule.verifier_metadata.payload_hash,
1067
+ import_ok: capsuleImport.ok,
1068
+ capsule_id: capsule.capsule_id
1069
+ }
1070
+ };
1071
+ }
1072
+
1073
+ export default {
1074
+ importChatGptExport,
1075
+ importClaudeMemory,
1076
+ importMem0Export,
1077
+ importLettaAgentFile,
1078
+ importLangGraphStore,
1079
+ importZepGraphitiExport,
1080
+ exportEnigmaCapsule,
1081
+ importEnigmaCapsule,
1082
+ runImporterDemo
1083
+ };