enigma-memory 0.1.4 → 0.1.6

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.
@@ -11,13 +11,17 @@ import { runBoundarySimulation } from '../../../packages/boundary/src/index.js';
11
11
  import { startStdioServer } from '../../../packages/mcp-server/src/index.js';
12
12
  import { runMeshDemo } from '../../../packages/mesh/src/index.js';
13
13
  import { runEnterpriseDemo } from '../../../packages/enterprise/src/index.js';
14
- import { connectClient, disconnectClient, doctorConnectors, getClientProfile, renderMcpConfig, supportedClients } from '../../../packages/connectors/src/index.js';
14
+ import { connectClient, disconnectClient, doctorConnectors, getClientProfile, planConnectWizard, renderMcpConfig, supportedClients } from '../../../packages/connectors/src/index.js';
15
15
  import { exportEnigmaCapsule, importChatGptExport, importClaudeMemory, importEnigmaCapsule, importLangGraphStore, importLettaAgentFile, importMem0Export, importZepGraphitiExport } from '../../../packages/importers/src/index.js';
16
16
  import * as relayServer from '../../relay/src/server.mjs';
17
17
  import * as gatewayServer from '../../gateway/src/server.mjs';
18
18
  import { verifyBundle } from '../../verifier/bin/enigma-verify.mjs';
19
19
  import { createNativeHostInstallPlan, createNativeHostManifest } from '../../native-host/bin/enigma-native-host.mjs';
20
20
  import { aggregateUsageEvents, createUsageEvent } from '../../../packages/metering/src/index.js';
21
+ import {
22
+ createMemoryAccessReceipt,
23
+ createMemoryOptimizationPlan,
24
+ } from '../../../packages/optimizer/src/index.js';
21
25
  import {
22
26
  createConsumerGpuCapacityProfile,
23
27
  createOperatorServiceQuote,
@@ -33,6 +37,21 @@ export const DEFAULT_GATEWAY_PORT = 8797;
33
37
  const DEFAULT_QUICKSTART_MEMORY = 'Enigma quickstart demo memory: local proof bundles can be created and verified without provider or cloud credentials.';
34
38
  const DEFAULT_CROSS_MODEL_DEMO_BUNDLE = '.enigma/cross-model-demo-bundle.json';
35
39
  const DEFAULT_CROSS_MODEL_MEMORY = 'Enigma cross-model demo memory: a local encrypted memory can be packaged for ChatGPT, Claude, Kimi, Cursor, and a local LLM without provider credentials.';
40
+ const DEFAULT_SETUP_CLIENTS = Object.freeze(['generic-mcp', 'claude-desktop', 'cursor', 'kimi-code']);
41
+ const SETUP_CLAIM_BOUNDARIES = Object.freeze({
42
+ local_only: true,
43
+ provider_credentials_required: false,
44
+ provider_native_memory_canonical: false,
45
+ provider_deletion_proof: false,
46
+ model_forgetting_proof: false,
47
+ roi_or_savings_guarantee: false,
48
+ compliance_certification: false,
49
+ });
50
+ const QUICKSTART_ARTIFACT_NAMES = Object.freeze({
51
+ contextPack: 'context-pack.json',
52
+ export: 'export.json',
53
+ verifyReport: 'verify-report.json',
54
+ });
36
55
  const CROSS_MODEL_PROFILES = Object.freeze([
37
56
  { id: 'chatgpt', provider: 'chatgpt', model: 'chatgpt-mcp-profile', label: 'ChatGPT' },
38
57
  { id: 'claude', provider: 'claude', model: 'claude-mcp-profile', label: 'Claude' },
@@ -77,6 +96,19 @@ const IMPORTERS = Object.freeze({
77
96
 
78
97
  export const REQUIRED_PACKAGE_BINS = Object.freeze(['enigma', 'enigma-verify', 'enigma-mcp', 'enigma-relay', 'enigma-gateway', 'enigma-native-host']);
79
98
 
99
+ function setFlagValue(flags, name, value) {
100
+ if (!flags.has(name)) {
101
+ flags.set(name, value);
102
+ return;
103
+ }
104
+ const current = flags.get(name);
105
+ if (Array.isArray(current)) {
106
+ current.push(value);
107
+ } else {
108
+ flags.set(name, [current, value]);
109
+ }
110
+ }
111
+
80
112
  function parseArgs(argv) {
81
113
  const flags = new Map();
82
114
  for (let i = 0; i < argv.length; i += 1) {
@@ -84,11 +116,11 @@ function parseArgs(argv) {
84
116
  if (!arg.startsWith('--')) continue;
85
117
  const eq = arg.indexOf('=');
86
118
  if (eq !== -1) {
87
- flags.set(arg.slice(2, eq), arg.slice(eq + 1));
119
+ setFlagValue(flags, arg.slice(2, eq), arg.slice(eq + 1));
88
120
  } else if (!argv[i + 1] || argv[i + 1].startsWith('--')) {
89
- flags.set(arg.slice(2), true);
121
+ setFlagValue(flags, arg.slice(2), true);
90
122
  } else {
91
- flags.set(arg.slice(2), argv[i + 1]);
123
+ setFlagValue(flags, arg.slice(2), argv[i + 1]);
92
124
  i += 1;
93
125
  }
94
126
  }
@@ -100,6 +132,19 @@ function getFlag(flags, names, fallback = undefined) {
100
132
  return fallback;
101
133
  }
102
134
 
135
+ function lastFlagValue(value) {
136
+ return Array.isArray(value) ? value[value.length - 1] : value;
137
+ }
138
+
139
+ function booleanFlag(flags, names, fallback = false) {
140
+ const value = lastFlagValue(getFlag(flags, names, fallback));
141
+ if (value === true || value === false) return value;
142
+ if (value === undefined || value === '') return fallback;
143
+ if (String(value) === 'true') return true;
144
+ if (String(value) === 'false') return false;
145
+ throw new Error(`--${names[0]} must be true or false.`);
146
+ }
147
+
103
148
  function requireFlag(flags, names, label = names[0]) {
104
149
  const value = getFlag(flags, names);
105
150
  if (value === undefined || value === true || value === '') throw new Error(`Missing required --${label}.`);
@@ -166,7 +211,7 @@ async function fileExists(path) {
166
211
  }
167
212
 
168
213
  function pathFlag(flags, names, fallback) {
169
- const value = getFlag(flags, names, fallback);
214
+ const value = lastFlagValue(getFlag(flags, names, fallback));
170
215
  if (value === true || value === '') throw new Error(`Missing required --${names[0]}.`);
171
216
  return String(value);
172
217
  }
@@ -210,6 +255,74 @@ async function quickstartMemoryTextFromFlags(flags) {
210
255
  return DEFAULT_QUICKSTART_MEMORY;
211
256
  }
212
257
 
258
+ function quickstartOutputs(bundleInput, outDirInput) {
259
+ const bundlePath = resolve(bundleInput);
260
+ const outDirPath = resolve(outDirInput);
261
+ const contextPackPath = resolve(outDirPath, QUICKSTART_ARTIFACT_NAMES.contextPack);
262
+ const exportPath = resolve(outDirPath, QUICKSTART_ARTIFACT_NAMES.export);
263
+ const verifyReportPath = resolve(outDirPath, QUICKSTART_ARTIFACT_NAMES.verifyReport);
264
+ return {
265
+ bundlePath,
266
+ outDirPath,
267
+ contextPackPath,
268
+ exportPath,
269
+ verifyReportPath,
270
+ contextPackDisplay: quickstartPathDisplay(outDirInput, QUICKSTART_ARTIFACT_NAMES.contextPack),
271
+ exportDisplay: quickstartPathDisplay(outDirInput, QUICKSTART_ARTIFACT_NAMES.export),
272
+ verifyReportDisplay: quickstartPathDisplay(outDirInput, QUICKSTART_ARTIFACT_NAMES.verifyReport),
273
+ outputs: [
274
+ { path: bundlePath, display: bundleInput },
275
+ { path: contextPackPath, display: quickstartPathDisplay(outDirInput, QUICKSTART_ARTIFACT_NAMES.contextPack) },
276
+ { path: exportPath, display: quickstartPathDisplay(outDirInput, QUICKSTART_ARTIFACT_NAMES.export) },
277
+ { path: verifyReportPath, display: quickstartPathDisplay(outDirInput, QUICKSTART_ARTIFACT_NAMES.verifyReport) },
278
+ ],
279
+ };
280
+ }
281
+
282
+ async function buildQuickstartArtifacts(flags, { bundleInput = DEFAULT_BUNDLE, outDirInput = dirname(bundleInput), overwrite = false, write = true } = {}) {
283
+ const paths = quickstartOutputs(bundleInput, outDirInput);
284
+ ensureDistinctOutputPaths(paths.outputs.map((output) => output.path));
285
+ await assertCanWriteQuickstartOutputs(paths.outputs, overwrite);
286
+
287
+ const vault = createVault({
288
+ subjectId: String(getFlag(flags, ['subject', 'subject-id'], 'local-user')),
289
+ displayName: String(getFlag(flags, ['display-name', 'name'], 'Local user')),
290
+ passphrase: String(getFlag(flags, ['passphrase'], 'local-development-passphrase')),
291
+ });
292
+ const passport = createPassport({
293
+ vault,
294
+ subjectId: vault.subject_id,
295
+ displayName: String(getFlag(flags, ['display-name', 'name'], 'Local user')),
296
+ });
297
+ remember({
298
+ vault,
299
+ passport,
300
+ text: await quickstartMemoryTextFromFlags(flags),
301
+ purpose: 'quickstart_local_proof',
302
+ purpose_tags: ['quickstart'],
303
+ metadata: { source: 'enigma quickstart' },
304
+ });
305
+ const contextPack = compileContextPack({
306
+ vault,
307
+ passport,
308
+ query: '',
309
+ purpose: 'quickstart_local_context',
310
+ limit: 8,
311
+ });
312
+ const exported = exportBundle({ vault, includePlaintext: false });
313
+ const bundle = exported.bundle ?? exported;
314
+ const verifyReport = verifyBundle(bundle);
315
+
316
+ if (write) {
317
+ await writeJson(paths.bundlePath, bundle);
318
+ await writeJson(paths.contextPackPath, contextPack);
319
+ await writeJson(paths.exportPath, bundle);
320
+ await writeJson(paths.verifyReportPath, verifyReport);
321
+ }
322
+
323
+ return { ...paths, vault, passport, contextPack, bundle, verifyReport };
324
+ }
325
+
213
326
  async function crossModelMemoryTextFromFlags(flags) {
214
327
  const textFile = getFlag(flags, ['memory-file', 'memoryFile', 'text-file', 'textFile']);
215
328
  if (textFile === undefined) return DEFAULT_CROSS_MODEL_MEMORY;
@@ -273,6 +386,157 @@ function publicContextPackSummary(pack) {
273
386
  };
274
387
  }
275
388
 
389
+ const SEARCH_RELEVANCE_STOPWORDS = new Set([
390
+ 'about',
391
+ 'after',
392
+ 'again',
393
+ 'against',
394
+ 'also',
395
+ 'and',
396
+ 'any',
397
+ 'are',
398
+ 'assistant',
399
+ 'because',
400
+ 'been',
401
+ 'before',
402
+ 'being',
403
+ 'between',
404
+ 'can',
405
+ 'could',
406
+ 'current',
407
+ 'does',
408
+ 'from',
409
+ 'has',
410
+ 'have',
411
+ 'how',
412
+ 'into',
413
+ 'its',
414
+ 'latest',
415
+ 'more',
416
+ 'most',
417
+ 'number',
418
+ 'own',
419
+ 'owns',
420
+ 'please',
421
+ 'should',
422
+ 'that',
423
+ 'the',
424
+ 'their',
425
+ 'then',
426
+ 'there',
427
+ 'these',
428
+ 'they',
429
+ 'this',
430
+ 'use',
431
+ 'using',
432
+ 'was',
433
+ 'what',
434
+ 'when',
435
+ 'where',
436
+ 'which',
437
+ 'who',
438
+ 'whose',
439
+ 'why',
440
+ 'with',
441
+ 'would',
442
+ ]);
443
+
444
+ function addSearchToken(tokens, token) {
445
+ if (token.length < 3) return;
446
+ if (!/[a-z]/u.test(token)) return;
447
+ if (SEARCH_RELEVANCE_STOPWORDS.has(token)) return;
448
+ tokens.add(token);
449
+ }
450
+
451
+ function searchTokensFrom(value) {
452
+ const tokens = new Set();
453
+ if (value === undefined || value === null) return tokens;
454
+ for (const match of String(value).toLowerCase().matchAll(/[a-z0-9]+(?:[-_][a-z0-9]+)*/gu)) {
455
+ const token = match[0];
456
+ addSearchToken(tokens, token);
457
+ if (token.includes('-') || token.includes('_')) {
458
+ for (const part of token.split(/[-_]+/u)) addSearchToken(tokens, part);
459
+ }
460
+ }
461
+ return tokens;
462
+ }
463
+
464
+ function addSearchTokensFromValue(tokens, value) {
465
+ for (const token of searchTokensFrom(value)) tokens.add(token);
466
+ }
467
+
468
+ function recordSearchTokens(record, content) {
469
+ const tokens = new Set();
470
+ addSearchTokensFromValue(tokens, content);
471
+ addSearchTokensFromValue(tokens, record?.kind);
472
+ for (const tag of record?.purpose_tags ?? []) addSearchTokensFromValue(tokens, tag);
473
+ return tokens;
474
+ }
475
+
476
+ function searchScore(queryTokens, memoryTokens) {
477
+ if (queryTokens.size === 0) return 0;
478
+ let overlap = 0;
479
+ for (const token of queryTokens) {
480
+ if (memoryTokens.has(token)) overlap += 1;
481
+ }
482
+ return Math.round((overlap / queryTokens.size) * 1_000_000) / 1_000_000;
483
+ }
484
+
485
+ function searchResultReceiptIds(vault, memoryAddr) {
486
+ return vault.receipts
487
+ .filter((receipt) => receipt?.memory_addr === memoryAddr || receipt?.source_addr === memoryAddr)
488
+ .map((receipt) => receipt.receipt_id)
489
+ .filter((receiptId) => typeof receiptId === 'string' && receiptId.length > 0);
490
+ }
491
+
492
+ function publicAccessReceiptRef(receipt) {
493
+ return {
494
+ access_receipt_ref: `enigma://memory-access/${receipt.receipt_id}`,
495
+ receipt_id: receipt.receipt_id,
496
+ operation: receipt.operation,
497
+ memory_addr: receipt.address,
498
+ plan_hash: receipt.plan_hash,
499
+ estimated_prompt_tokens: receipt.estimated_prompt_tokens,
500
+ access_boundary: receipt.access_boundary,
501
+ };
502
+ }
503
+
504
+ function searchCandidates(vault, queryTokens) {
505
+ const candidates = [];
506
+ const byAddress = new Map();
507
+ for (const memoryAddr of [...vault.activeAddresses].sort()) {
508
+ const record = vault.__getRecord(memoryAddr);
509
+ if (!record || record.state !== 'active') continue;
510
+ const content = vault.__getPlaintext(memoryAddr);
511
+ const score = searchScore(queryTokens, recordSearchTokens(record, content));
512
+ if (queryTokens.size > 0 && score === 0) continue;
513
+ const candidate = {
514
+ address: memoryAddr,
515
+ content,
516
+ importance: typeof record.importance === 'number' ? record.importance : typeof record.confidence === 'number' ? record.confidence : undefined,
517
+ last_accessed_at: record.updated_at ?? record.created_at,
518
+ metadata: {
519
+ kind: record.kind,
520
+ sensitivity: record.sensitivity,
521
+ purpose_tags: record.purpose_tags ?? [],
522
+ },
523
+ };
524
+ candidates.push(candidate);
525
+ byAddress.set(memoryAddr, { record, content, score });
526
+ }
527
+ return { candidates, byAddress };
528
+ }
529
+
530
+ function connectorReadinessSummary(bundlePath) {
531
+ return {
532
+ ready: true,
533
+ bundle: bundlePath,
534
+ bundle_env: 'ENIGMA_BUNDLE',
535
+ mcp_command: 'enigma-mcp',
536
+ supported_clients: supportedClients,
537
+ };
538
+ }
539
+
276
540
  function demoBundleRef(bundleWasSupplied) {
277
541
  return bundleWasSupplied ? 'supplied_bundle' : DEFAULT_CROSS_MODEL_DEMO_BUNDLE;
278
542
  }
@@ -493,85 +757,275 @@ async function initCommand(flags, io) {
493
757
  return 0;
494
758
  }
495
759
 
496
- export async function quickstartCommand(flags, io) {
760
+ function setupClientIds(flags) {
761
+ const raw = getFlag(flags, ['client']);
762
+ if (raw === undefined) return [...DEFAULT_SETUP_CLIENTS];
763
+ const values = Array.isArray(raw) ? raw : [raw];
764
+ const clients = [];
765
+ for (const value of values) {
766
+ if (value === true || value === '') throw new Error('Missing required --client.');
767
+ for (const client of String(value).split(',').map((item) => item.trim()).filter(Boolean)) {
768
+ getClientProfile(client);
769
+ if (!clients.includes(client)) clients.push(client);
770
+ }
771
+ }
772
+ return clients.length > 0 ? clients : [...DEFAULT_SETUP_CLIENTS];
773
+ }
774
+
775
+ function setupMemorySource(flags) {
776
+ if (getFlag(flags, ['memory-file', 'memoryFile', 'text-file', 'textFile']) !== undefined) return 'memory_file';
777
+ if (getFlag(flags, ['memory-text', 'memoryText']) !== undefined) return 'demo_text';
778
+ return 'default_demo';
779
+ }
780
+
781
+ function commandPath(path) {
782
+ return `"${String(path).replace(/"/g, '\\"')}"`;
783
+ }
784
+
785
+ function publicPathDisplay(path, label) {
786
+ const value = String(path);
787
+ if (/^[A-Za-z]:[\\/]/.test(value) || value.startsWith('/') || value.startsWith('\\\\')) return `<${label}>`;
788
+ return value;
789
+ }
790
+
791
+ function setupPublicDisplays(bundleInput, outDirInput) {
792
+ const outDir = publicPathDisplay(outDirInput, 'out-dir');
793
+ return {
794
+ bundle: publicPathDisplay(bundleInput, 'bundle-path'),
795
+ context_pack: quickstartPathDisplay(outDir, QUICKSTART_ARTIFACT_NAMES.contextPack),
796
+ export: quickstartPathDisplay(outDir, QUICKSTART_ARTIFACT_NAMES.export),
797
+ verify_report: quickstartPathDisplay(outDir, QUICKSTART_ARTIFACT_NAMES.verifyReport),
798
+ };
799
+ }
800
+
801
+ function setupRawDisplays(bundleInput, outDirInput) {
802
+ const plan = quickstartOutputs(bundleInput, outDirInput);
803
+ return {
804
+ bundle: plan.outputs[0].display,
805
+ context_pack: plan.contextPackDisplay,
806
+ export: plan.exportDisplay,
807
+ verify_report: plan.verifyReportDisplay,
808
+ };
809
+ }
810
+
811
+ function publicSetupError(error, rawDisplays, publicDisplays) {
812
+ let message = error.message;
813
+ for (const [raw, safe] of Object.entries({
814
+ [rawDisplays.bundle]: publicDisplays.bundle,
815
+ [rawDisplays.context_pack]: publicDisplays.context_pack,
816
+ [rawDisplays.export]: publicDisplays.export,
817
+ [rawDisplays.verify_report]: publicDisplays.verify_report,
818
+ })) {
819
+ message = message.split(raw).join(safe);
820
+ }
821
+ return new Error(message);
822
+ }
823
+
824
+ function setupNextCommands(bundleInput, exportDisplay, clients, writeConnectors) {
825
+ const primaryClient = clients[0] ?? DEFAULT_SETUP_CLIENTS[0];
826
+ const commands = [
827
+ `enigma remember --bundle ${commandPath(bundleInput)} --text-file ./memory.txt`,
828
+ `enigma search --bundle ${commandPath(bundleInput)} --query "project context"`,
829
+ `enigma context --bundle ${commandPath(bundleInput)} --query "project context"`,
830
+ `enigma verify --export ${commandPath(exportDisplay)}`,
831
+ ];
832
+ if (!writeConnectors) commands.push(`enigma connect ${primaryClient} --bundle ${commandPath(bundleInput)}`);
833
+ return commands;
834
+ }
835
+
836
+ async function setupDoctorChecks(flags, artifacts, clients, displays) {
837
+ const packageJson = await readPackageJson();
838
+ const requiredNodeMajor = minimumNodeMajor(packageJson.engines?.node);
839
+ const currentNodeMajor = nodeMajor(process.versions.node);
840
+ const binMap = packageJson.bin && typeof packageJson.bin === 'object' && !Array.isArray(packageJson.bin) ? packageJson.bin : {};
841
+ const binEntries = await Promise.all(REQUIRED_PACKAGE_BINS.map(async (name) => {
842
+ const target = binMap[name];
843
+ const declared = typeof target === 'string' && target.length > 0;
844
+ return {
845
+ name,
846
+ target: declared ? target : null,
847
+ declared,
848
+ exists: declared ? await fileExists(packageFileUrl(target)) : false,
849
+ };
850
+ }));
851
+ const schemas = await schemaFiles();
852
+ const connectorBaseOptions = {
853
+ ...connectorOptions(flags),
854
+ bundlePath: artifacts.bundlePath,
855
+ redactPaths: true,
856
+ };
857
+ const connectorClients = [];
858
+ for (const client of clients) {
859
+ const doctor = await doctorConnectors({ ...connectorBaseOptions, clientId: client });
860
+ connectorClients.push(...doctor.clients);
861
+ }
862
+ const checks = {
863
+ node: {
864
+ ok: requiredNodeMajor === 0 || currentNodeMajor >= requiredNodeMajor,
865
+ current: process.versions.node,
866
+ required: packageJson.engines?.node ?? null,
867
+ },
868
+ package_bins: {
869
+ ok: binEntries.every((entry) => entry.declared && entry.exists),
870
+ required: REQUIRED_PACKAGE_BINS,
871
+ entries: binEntries,
872
+ missing: binEntries.filter((entry) => !entry.declared).map((entry) => entry.name),
873
+ missing_targets: binEntries.filter((entry) => entry.declared && !entry.exists).map((entry) => entry.name),
874
+ },
875
+ artifacts: {
876
+ ok: artifacts.verifyReport.ok === true,
877
+ bundle: displays.bundle,
878
+ context_pack: displays.context_pack,
879
+ export: displays.export,
880
+ verify_report: displays.verify_report,
881
+ },
882
+ schemas: {
883
+ ok: schemas.length > 0,
884
+ count: schemas.length,
885
+ files: schemas,
886
+ },
887
+ connectors: {
888
+ ok: connectorClients.every((client) => client.ok !== false),
889
+ clients: connectorClients,
890
+ },
891
+ };
892
+ return { ok: Object.values(checks).every((check) => check.ok !== false), checks };
893
+ }
894
+
895
+ function publicConnectPlan(plan, wizard, profile, snippet) {
896
+ const changed = plan?.changed !== false;
897
+ const dryRun = plan?.dryRun === true || plan?.dry_run === true;
898
+ const plannedWrites = changed ? [{ type: 'write', path: wizard.default_config_path }] : [];
899
+ return {
900
+ ok: plan?.ok !== false,
901
+ action: 'connect',
902
+ client_id: profile.client_id,
903
+ configPath: wizard.default_config_path,
904
+ config_path: wizard.default_config_path,
905
+ serverName: profile.server_name,
906
+ server_name: profile.server_name,
907
+ changed,
908
+ dryRun,
909
+ dry_run: dryRun,
910
+ writes_performed: changed && !dryRun,
911
+ backup_planned: Boolean(plan?.backupPath),
912
+ plannedWrites,
913
+ planned_writes: plannedWrites,
914
+ config: snippet,
915
+ };
916
+ }
917
+
918
+ async function setupConnectorPlans(flags, artifacts, clients, writeConnectors, displays) {
919
+ const publicOptions = {
920
+ ...connectorOptions(flags),
921
+ bundlePath: displays.bundle,
922
+ };
923
+ const writeOptions = {
924
+ ...connectorOptions(flags),
925
+ bundlePath: artifacts.bundlePath,
926
+ };
927
+ const connectors = [];
928
+ for (const client of clients) {
929
+ const profile = getClientProfile(client, publicOptions);
930
+ const snippet = renderMcpConfig(client, publicOptions);
931
+ const wizard = planConnectWizard(client, { platform: profile.platform }).clients[0];
932
+ const rawPlan = writeConnectors
933
+ ? await connectClient(client, { ...writeOptions, dryRun: false })
934
+ : { ok: true, changed: true, dryRun: true };
935
+ const plan = publicConnectPlan(rawPlan, wizard, profile, snippet);
936
+ connectors.push({
937
+ client_id: client,
938
+ display_name: profile.display_name,
939
+ default_config_path: wizard.default_config_path,
940
+ mcp_config_snippet: snippet,
941
+ connect_command: `enigma connect ${client} --bundle ${commandPath(displays.bundle)}`,
942
+ connect_plan: plan,
943
+ wizard,
944
+ });
945
+ }
946
+ return connectors;
947
+ }
948
+
949
+ export async function setupCommand(flags, io) {
497
950
  const bundleInput = pathFlag(flags, ['bundle', 'file'], DEFAULT_BUNDLE);
498
951
  const outDirInput = pathFlag(flags, ['out-dir', 'outDir'], dirname(bundleInput));
499
- const bundlePath = resolve(bundleInput);
500
- const outDirPath = resolve(outDirInput);
501
- const contextPackPath = resolve(outDirPath, 'context-pack.json');
502
- const exportPath = resolve(outDirPath, 'export.json');
503
- const verifyReportPath = resolve(outDirPath, 'verify-report.json');
504
- const contextPackDisplay = quickstartPathDisplay(outDirInput, 'context-pack.json');
505
- const exportDisplay = quickstartPathDisplay(outDirInput, 'export.json');
506
- const verifyReportDisplay = quickstartPathDisplay(outDirInput, 'verify-report.json');
507
- const outputs = [
508
- { path: bundlePath, display: bundleInput },
509
- { path: contextPackPath, display: contextPackDisplay },
510
- { path: exportPath, display: exportDisplay },
511
- { path: verifyReportPath, display: verifyReportDisplay },
512
- ];
513
- ensureDistinctOutputPaths(outputs.map((output) => output.path));
514
- const overwrite = getFlag(flags, ['overwrite'], false) === true || getFlag(flags, ['overwrite'], false) === 'true';
515
- await assertCanWriteQuickstartOutputs(outputs, overwrite);
952
+ const clients = setupClientIds(flags);
953
+ const overwrite = booleanFlag(flags, ['overwrite'], false);
954
+ const dryRun = booleanFlag(flags, ['dry-run', 'dryRun'], false);
955
+ const writeConnectors = booleanFlag(flags, ['write-connectors', 'writeConnectors'], false) && !dryRun;
956
+ const displays = setupPublicDisplays(bundleInput, outDirInput);
957
+ const rawDisplays = setupRawDisplays(bundleInput, outDirInput);
958
+ let artifacts;
959
+ try {
960
+ artifacts = await buildQuickstartArtifacts(flags, { bundleInput, outDirInput, overwrite, write: !dryRun });
961
+ } catch (error) {
962
+ throw publicSetupError(error, rawDisplays, displays);
963
+ }
964
+ const connectors = await setupConnectorPlans(flags, artifacts, clients, writeConnectors, displays);
965
+ const doctor = await setupDoctorChecks(flags, artifacts, clients, displays);
966
+ const ok = artifacts.verifyReport.ok === true;
516
967
 
517
- const vault = createVault({
518
- subjectId: String(getFlag(flags, ['subject', 'subject-id'], 'local-user')),
519
- displayName: String(getFlag(flags, ['display-name', 'name'], 'Local user')),
520
- passphrase: String(getFlag(flags, ['passphrase'], 'local-development-passphrase')),
521
- });
522
- const passport = createPassport({
523
- vault,
524
- subjectId: vault.subject_id,
525
- displayName: String(getFlag(flags, ['display-name', 'name'], 'Local user')),
526
- });
527
- remember({
528
- vault,
529
- passport,
530
- text: await quickstartMemoryTextFromFlags(flags),
531
- purpose: 'quickstart_local_proof',
532
- purpose_tags: ['quickstart'],
533
- metadata: { source: 'enigma quickstart' },
534
- });
535
- const contextPack = compileContextPack({
536
- vault,
537
- passport,
538
- query: '',
539
- purpose: 'quickstart_local_context',
540
- limit: 8,
541
- });
542
- const exported = exportBundle({ vault, includePlaintext: false });
543
- const bundle = exported.bundle ?? exported;
544
- const verifyReport = verifyBundle(bundle);
968
+ print({
969
+ ok,
970
+ schema: 'enigma.setup.v1',
971
+ command: 'enigma setup',
972
+ dry_run: dryRun,
973
+ artifacts_written: !dryRun,
974
+ client_configs_written: writeConnectors,
975
+ bundle: displays.bundle,
976
+ context_pack: displays.context_pack,
977
+ export: displays.export,
978
+ verify_report: displays.verify_report,
979
+ memory_source: setupMemorySource(flags),
980
+ memory_plaintext_echoed: false,
981
+ memory_count: Array.isArray(artifacts.bundle.memory_objects) ? artifacts.bundle.memory_objects.length : 0,
982
+ receipt_count: Array.isArray(artifacts.bundle.receipts) ? artifacts.bundle.receipts.length : 0,
983
+ context_item_count: Array.isArray(artifacts.contextPack.memories) ? artifacts.contextPack.memories.length : 0,
984
+ verify_ok: artifacts.verifyReport.ok === true,
985
+ provider_credentials_required: false,
986
+ provider_native_memory_canonical: false,
987
+ selected_clients: clients,
988
+ connectors,
989
+ mcp_config_snippets: Object.fromEntries(connectors.map((connector) => [connector.client_id, connector.mcp_config_snippet])),
990
+ connect_plans: Object.fromEntries(connectors.map((connector) => [connector.client_id, connector.connect_plan])),
991
+ next_commands: setupNextCommands(displays.bundle, displays.export, clients, writeConnectors),
992
+ checks: doctor.checks,
993
+ claim_boundaries: { ...SETUP_CLAIM_BOUNDARIES },
994
+ }, io);
995
+ return ok ? 0 : 1;
996
+ }
545
997
 
546
- await writeJson(bundlePath, bundle);
547
- await writeJson(contextPackPath, contextPack);
548
- await writeJson(exportPath, bundle);
549
- await writeJson(verifyReportPath, verifyReport);
998
+ export async function quickstartCommand(flags, io) {
999
+ const bundleInput = pathFlag(flags, ['bundle', 'file'], DEFAULT_BUNDLE);
1000
+ const outDirInput = pathFlag(flags, ['out-dir', 'outDir'], dirname(bundleInput));
1001
+ const overwrite = booleanFlag(flags, ['overwrite'], false);
1002
+ const artifacts = await buildQuickstartArtifacts(flags, { bundleInput, outDirInput, overwrite, write: true });
550
1003
 
551
1004
  print({
552
- ok: verifyReport.ok === true,
1005
+ ok: artifacts.verifyReport.ok === true,
553
1006
  bundle: bundleInput,
554
- context_pack: contextPackDisplay,
555
- export: exportDisplay,
556
- verify_report: verifyReportDisplay,
557
- memory_count: Array.isArray(bundle.memory_objects) ? bundle.memory_objects.length : 0,
558
- receipt_count: Array.isArray(bundle.receipts) ? bundle.receipts.length : 0,
559
- context_item_count: Array.isArray(contextPack.memories) ? contextPack.memories.length : 0,
560
- verify_ok: verifyReport.ok === true,
1007
+ context_pack: artifacts.contextPackDisplay,
1008
+ export: artifacts.exportDisplay,
1009
+ verify_report: artifacts.verifyReportDisplay,
1010
+ memory_count: Array.isArray(artifacts.bundle.memory_objects) ? artifacts.bundle.memory_objects.length : 0,
1011
+ receipt_count: Array.isArray(artifacts.bundle.receipts) ? artifacts.bundle.receipts.length : 0,
1012
+ context_item_count: Array.isArray(artifacts.contextPack.memories) ? artifacts.contextPack.memories.length : 0,
1013
+ verify_ok: artifacts.verifyReport.ok === true,
561
1014
  next_commands: [
562
- `enigma verify --export ${exportDisplay}`,
1015
+ `enigma verify --export ${artifacts.exportDisplay}`,
563
1016
  `enigma connect generic-mcp --bundle ${bundleInput} --dry-run`,
564
1017
  ],
565
1018
  claim_boundaries: {
566
1019
  local_only: true,
567
1020
  provider_credentials_required: false,
1021
+ provider_native_memory_canonical: false,
568
1022
  provider_deletion_proof: false,
569
1023
  model_forgetting_proof: false,
570
1024
  roi_or_savings_guarantee: false,
571
1025
  compliance_certification: false,
572
1026
  },
573
1027
  }, io);
574
- return verifyReport.ok === true ? 0 : 1;
1028
+ return artifacts.verifyReport.ok === true ? 0 : 1;
575
1029
  }
576
1030
 
577
1031
  export async function crossModelDemoCommand(flags, io) {
@@ -767,6 +1221,118 @@ async function contextCommand(flags, io) {
767
1221
  return 0;
768
1222
  }
769
1223
 
1224
+
1225
+ async function searchCommand(flags, io) {
1226
+ const bundlePath = resolve(String(getFlag(flags, ['bundle', 'file'], DEFAULT_BUNDLE)));
1227
+ const query = String(requireFlag(flags, ['query', 'q'], 'query'));
1228
+ const limit = integerFlag(flags, ['limit'], 'limit', 8);
1229
+ if (limit < 0) throw new Error('--limit must be non-negative.');
1230
+ const includeContent = getFlag(flags, ['include-content', 'includeContent']) === true || getFlag(flags, ['include-content', 'includeContent']) === 'true';
1231
+ const { vault } = await loadState(bundlePath);
1232
+ const roots = vault.__computeRoots();
1233
+ const queryTokens = searchTokensFrom(query);
1234
+ const { candidates, byAddress } = searchCandidates(vault, queryTokens);
1235
+ const plan = createMemoryOptimizationPlan({
1236
+ candidates,
1237
+ prompt: query,
1238
+ now: getFlag(flags, ['now'], '2026-01-01T00:00:00.000Z'),
1239
+ });
1240
+ const planIndex = new Map(plan.items.map((item, index) => [item.address, index]));
1241
+ const selectedItems = plan.items
1242
+ .filter((item) => byAddress.has(item.address))
1243
+ .sort((left, right) => {
1244
+ const scoreDiff = byAddress.get(right.address).score - byAddress.get(left.address).score;
1245
+ if (scoreDiff !== 0) return scoreDiff;
1246
+ return planIndex.get(left.address) - planIndex.get(right.address);
1247
+ })
1248
+ .slice(0, limit);
1249
+ const accessReceipts = selectedItems.map((item, index) => createMemoryAccessReceipt({
1250
+ item,
1251
+ plan,
1252
+ sequence: index,
1253
+ timestamp: null,
1254
+ pricing: plan.pricing,
1255
+ }));
1256
+ const accessReceiptByAddress = new Map(accessReceipts.map((receipt) => [receipt.address, publicAccessReceiptRef(receipt)]));
1257
+ const results = selectedItems.map((item) => {
1258
+ const hit = byAddress.get(item.address);
1259
+ const record = hit.record;
1260
+ const accessReceipt = accessReceiptByAddress.get(item.address);
1261
+ return {
1262
+ memory_ref: `enigma://memory/${item.address}`,
1263
+ memory_addr: item.address,
1264
+ address: item.address,
1265
+ kind: record.kind,
1266
+ sensitivity: record.sensitivity,
1267
+ tags: Array.isArray(record.purpose_tags) ? [...record.purpose_tags] : [],
1268
+ purpose_tags: Array.isArray(record.purpose_tags) ? [...record.purpose_tags] : [],
1269
+ score: hit.score,
1270
+ tier: item.tier,
1271
+ receipt_ids: searchResultReceiptIds(vault, item.address),
1272
+ access_receipt_ref: accessReceipt?.access_receipt_ref,
1273
+ access_receipt_id: accessReceipt?.receipt_id,
1274
+ access_receipt_refs: accessReceipt?.access_receipt_ref ? [accessReceipt.access_receipt_ref] : [],
1275
+ content_redacted: !includeContent,
1276
+ ...(includeContent ? { content: hit.content } : {}),
1277
+ };
1278
+ });
1279
+ print({
1280
+ ok: true,
1281
+ schema: 'enigma.memory_search.v1',
1282
+ bundle: bundlePath,
1283
+ query_redacted: true,
1284
+ limit,
1285
+ result_count: results.length,
1286
+ results,
1287
+ access_receipts: accessReceipts.map(publicAccessReceiptRef),
1288
+ active_set_root: roots.active_set_root,
1289
+ receipt_log_root: roots.receipt_log_root,
1290
+ claim_boundary: includeContent
1291
+ ? 'Search ran against the selected local bundle and includes plaintext only because --include-content was explicit; this does not prove provider deletion, provider-native memory state, or model forgetting.'
1292
+ : 'Search ran against the selected local bundle and redacts plaintext by default; refs, scores, tags, roots, and receipt refs are not provider deletion proof or model forgetting proof.',
1293
+ }, io);
1294
+ return 0;
1295
+ }
1296
+
1297
+ async function statusCommand(flags, io) {
1298
+ const bundlePath = resolve(String(getFlag(flags, ['bundle', 'file'], DEFAULT_BUNDLE)));
1299
+ const { stored, vault, passport } = await loadState(bundlePath);
1300
+ const roots = vault.__computeRoots();
1301
+ const activeCount = activeMemoryCount(vault);
1302
+ const tombstoneCount = vault.tombstones instanceof Map ? vault.tombstones.size : 0;
1303
+ const receiptCount = Array.isArray(vault.receipts) ? vault.receipts.length : 0;
1304
+ print({
1305
+ ok: true,
1306
+ schema: 'enigma.passport_status.v1',
1307
+ bundle: bundlePath,
1308
+ passport_ref: `enigma://passport/${passport.passport_id}`,
1309
+ owner: {
1310
+ subject_id: stored.owner?.subject_id ?? stored.passport?.owner?.subject_id ?? stored.vault?.subject_id ?? passport.owner?.subject_id ?? vault.subject_id,
1311
+ display_name: stored.owner?.display_name ?? stored.passport?.owner?.display_name ?? stored.vault?.display_name ?? passport.owner?.display_name ?? 'Local user',
1312
+ },
1313
+ counts: {
1314
+ active_memories: activeCount,
1315
+ tombstoned_memories: tombstoneCount,
1316
+ receipts: receiptCount,
1317
+ },
1318
+ active_memory_count: activeCount,
1319
+ tombstoned_memory_count: tombstoneCount,
1320
+ receipt_count: receiptCount,
1321
+ active_set_root: roots.active_set_root,
1322
+ receipt_log_root: roots.receipt_log_root,
1323
+ connector_readiness: connectorReadinessSummary(bundlePath),
1324
+ next_recommended_commands: [
1325
+ `enigma remember --bundle "${bundlePath}" --text-file <path>`,
1326
+ `enigma search --bundle "${bundlePath}" --query <text>`,
1327
+ `enigma context --bundle "${bundlePath}" --query <text>`,
1328
+ `enigma verify --bundle "${bundlePath}"`,
1329
+ `enigma connect <client> --bundle "${bundlePath}"`,
1330
+ ],
1331
+ claim_boundary: 'Status reports local bundle counters, owner display fields, connector readiness hints, and commitment roots only; it does not expose raw memory, certify compliance, prove provider deletion, or prove model forgetting.',
1332
+ }, io);
1333
+ return 0;
1334
+ }
1335
+
770
1336
  async function exportCommand(flags, io) {
771
1337
  const bundlePath = resolve(String(getFlag(flags, ['bundle', 'file'], DEFAULT_BUNDLE)));
772
1338
  const { vault } = await loadState(bundlePath);
@@ -1272,6 +1838,7 @@ function usage() {
1272
1838
  usage: 'enigma <command> [options]',
1273
1839
  commands: [
1274
1840
  'init',
1841
+ 'setup',
1275
1842
  'quickstart',
1276
1843
  'demo cross-model',
1277
1844
  'doctor',
@@ -1283,6 +1850,9 @@ function usage() {
1283
1850
  'update',
1284
1851
  'delete',
1285
1852
  'context',
1853
+ 'search',
1854
+ 'status',
1855
+ 'passport status',
1286
1856
  'export',
1287
1857
  'import <source>',
1288
1858
  'capsule export',
@@ -1318,6 +1888,27 @@ function usage() {
1318
1888
  '--text <text>': 'Inline local memory text. Avoid for private content because argv can be logged by process tooling.',
1319
1889
  '--text-file <path>': 'Read local memory text from a file so private smoke input is not exposed in shell argv. Aliases: --memory-file, --textFile, --memoryFile.',
1320
1890
  },
1891
+ search_options: {
1892
+ '--query <text>': 'Required local query. Output redacts the query and memory plaintext by default. Alias: --q.',
1893
+ '--bundle <path>': 'Bundle JSON to search. Defaults to .enigma/bundle.json.',
1894
+ '--limit <n>': 'Maximum ranked active memories to return. Defaults to 8.',
1895
+ '--json': 'Reserved for explicit JSON output; CLI output is JSON by default.',
1896
+ '--include-content': 'Opt in to returning plaintext local memory content in the JSON result.',
1897
+ },
1898
+ status_options: {
1899
+ 'enigma status --bundle <path>': 'Show local Memory Passport counts, roots, owner display fields, connector readiness, and next commands.',
1900
+ 'enigma passport status --bundle <path>': 'Alias for enigma status.',
1901
+ },
1902
+ setup_options: {
1903
+ '--bundle <path>': 'Bundle JSON to create. Defaults to .enigma/bundle.json.',
1904
+ '--out-dir <path>': 'Directory for context-pack.json, export.json, and verify-report.json. Defaults to the bundle directory.',
1905
+ '--client <id>': `Client to plan; repeat or comma-separate. Defaults to ${DEFAULT_SETUP_CLIENTS.join(', ')}.`,
1906
+ '--memory-file <path>': 'Read local memory text from a file without echoing plaintext. Alias: --text-file.',
1907
+ '--memory-text <text>': 'Inline demo-only memory text. Avoid for private content because argv can be logged.',
1908
+ '--overwrite': 'Replace existing local setup artifacts.',
1909
+ '--dry-run': 'Plan setup without writing local artifacts or client configs.',
1910
+ '--write-connectors': 'Also write selected client MCP config files. Defaults to false.',
1911
+ },
1321
1912
  quickstart_options: {
1322
1913
  '--bundle <path>': 'Bundle JSON to create. Defaults to .enigma/bundle.json.',
1323
1914
  '--out-dir <path>': 'Directory for context-pack.json, export.json, and verify-report.json. Defaults to the bundle directory.',
@@ -1398,15 +1989,16 @@ export async function main(argv = process.argv.slice(2), io = { stdout: process.
1398
1989
  print(usage(), io);
1399
1990
  return 0;
1400
1991
  }
1401
- const twoPartCommands = ['boundary', 'mcp', 'mesh', 'enterprise', 'capsule', 'relay', 'gateway', 'connect', 'disconnect', 'import', 'native-host', 'meter', 'settlement', 'demo'];
1992
+ const twoPartCommands = ['boundary', 'mcp', 'mesh', 'enterprise', 'capsule', 'relay', 'gateway', 'connect', 'disconnect', 'import', 'native-host', 'meter', 'settlement', 'demo', 'passport'];
1402
1993
  const flags = parseArgs(twoPartCommands.includes(command) ? argv.slice(2) : argv.slice(1));
1403
1994
  const positionalFile = optionalPositional(argv[2]);
1404
- if ((flags.has('help') || argv.includes('-h')) && (((command === 'relay' || command === 'gateway') && (subcommand === 'serve' || subcommand === 'demo')) || (command === 'native-host' && (subcommand === 'manifest' || subcommand === 'install-plan')) || (command === 'demo' && subcommand === 'cross-model'))) {
1995
+ if ((flags.has('help') || argv.includes('-h')) && (command === 'setup' || command === 'search' || command === 'status' || (command === 'passport' && subcommand === 'status') || ((command === 'relay' || command === 'gateway') && (subcommand === 'serve' || subcommand === 'demo')) || (command === 'native-host' && (subcommand === 'manifest' || subcommand === 'install-plan')) || (command === 'demo' && subcommand === 'cross-model'))) {
1405
1996
  print(usage(), io);
1406
1997
  return 0;
1407
1998
  }
1408
1999
  try {
1409
2000
  if (command === 'init') return await initCommand(flags, io);
2001
+ if (command === 'setup') return await setupCommand(flags, io);
1410
2002
  if (command === 'quickstart') return await quickstartCommand(flags, io);
1411
2003
  if (command === 'demo' && subcommand === 'cross-model') return await crossModelDemoCommand(flags, io);
1412
2004
  if (command === 'doctor') return await doctorCommand(flags, io);
@@ -1418,6 +2010,9 @@ export async function main(argv = process.argv.slice(2), io = { stdout: process.
1418
2010
  if (command === 'update') return await updateCommand(flags, io);
1419
2011
  if (command === 'delete') return await deleteCommand(flags, io);
1420
2012
  if (command === 'context') return await contextCommand(flags, io);
2013
+ if (command === 'search') return await searchCommand(flags, io);
2014
+ if (command === 'status') return await statusCommand(flags, io);
2015
+ if (command === 'passport' && subcommand === 'status') return await statusCommand(flags, io);
1421
2016
  if (command === 'export') return await exportCommand(flags, io);
1422
2017
  if (command === 'import') return await importCommand(subcommand, flags, io, positionalFile);
1423
2018
  if (command === 'capsule' && subcommand === 'export') return await capsuleExportCommand(flags, io, positionalFile);