enigma-memory 0.1.9 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,18 +2,41 @@
2
2
 
3
3
  Enigma Memory is a local-first AI Memory Passport. It gives you one canonical local memory vault, receipt-backed proof for Enigma-controlled events, and MCP connections into assistants such as Claude Desktop, Cursor, Kimi Code, and generic MCP clients.
4
4
 
5
- Start with the package path below. You do not need to understand relay, gateway, mesh, browser, desktop, or hosted infrastructure before trying Enigma locally.
5
+ Start with the public test drive below. You do not need to understand relay, gateway, mesh, browser, desktop, or hosted infrastructure before trying Enigma locally.
6
6
 
7
7
  The package path needs no database, provider credential, cloud credential, npm publishing token, or external account. Hosted cloud and BYOC operation are separate advanced modes that require real deployment credentials, domains, TLS, durable storage, KMS/secrets, monitoring, backups, and operator acceptance.
8
8
 
9
9
  Enigma does not claim that a closed provider deleted internal data, that model weights forgot, or that provider-native memory disappeared. It proves facts about Enigma-controlled vault state, receipts, checkpoints, and declared boundary operations.
10
10
 
11
+ ## Public test drive: prove the local package path first
12
+
13
+ The fastest public test is the published npm package plus the local-only test drive:
14
+
15
+ ```sh
16
+ npm install -g enigma-memory
17
+ enigma test-drive --overwrite
18
+ ```
19
+
20
+ `enigma test-drive --overwrite` needs no database, provider API key, cloud credential, npm publishing token, package registry account, or hosted Enigma account. It runs entirely on the local filesystem, does not call OpenAI, Anthropic, Cloudflare, hosted Enigma, or other external providers, and does not write Claude, Cursor, Kimi, or other third-party client configs.
21
+
22
+ By default the command writes an isolated demo under `.enigma/test-drive`. Use `--dry-run` to print the plan without writing, or `--out-dir <path>` to choose another isolated demo directory. The default bundle and proof artifacts live inside that directory.
23
+
24
+ The test-drive output is a single public-safe JSON summary. It points to the local setup artifacts, search/status output, cross-model demo report, and benchmark pointers, and it prints exact next commands for testers without printing raw private memory plaintext. The proof is bounded to the local demo: Enigma-controlled vault state, receipts, checkpoints, committed roots, exported bundle shape, and declared boundary operations. It is not evidence that hosted SaaS is live, that a closed provider deleted internal data, that model weights forgot, or that provider-native memory disappeared.
25
+
26
+ When you are ready to connect real local clients that are already installed or already configured:
27
+
28
+ ```sh
29
+ enigma setup --connect-installed --overwrite
30
+ ```
31
+
32
+ `--connect-installed` is the explicit client-config write path. It skips missing client configs instead of creating every default client config.
33
+
11
34
  ## Install once, use everywhere
12
35
 
13
36
  Prerequisites:
14
37
 
15
38
  - Node.js `>=24`
16
- - No database, package registry account, provider credential, or cloud credential for the local setup path
39
+ - No database, package registry account, provider credential, or cloud credential for the local test-drive or setup paths
17
40
  - Git only when you choose the advanced source-checkout path
18
41
 
19
42
  Use Enigma as a one-time AI Memory Passport setup: install the package, create the local Enigma workspace once, then use the same memory/search/context/verify/connect loop from any supported client.
@@ -23,7 +46,7 @@ npm install -g enigma-memory
23
46
  enigma setup --overwrite
24
47
  ```
25
48
 
26
- `enigma setup --overwrite` is the safe default. It writes local Enigma artifacts under the workspace `.enigma` path and emits deterministic, public-safe JSON without printing raw memory plaintext. It does not write Claude, Cursor, Kimi, or other third-party app configs.
49
+ `enigma setup --overwrite` is the safe default for creating a regular local workspace after the test drive. It writes local Enigma artifacts under the workspace `.enigma` path and emits deterministic, public-safe JSON without printing raw memory plaintext. It does not write Claude, Cursor, Kimi, or other third-party app configs.
27
50
 
28
51
  To let setup detect installed or already-configured clients and show the connector plan without mutating client configs:
29
52
 
@@ -33,7 +56,7 @@ enigma setup --client auto --overwrite
33
56
 
34
57
  `--client auto` selects clients found by connector detection and falls back to the default setup client list when none are present. The setup output lists which clients were selected, which were skipped, and why.
35
58
 
36
- When you are ready to explicitly write connector entries for installed/config-present clients only:
59
+ Only when you are ready to explicitly write connector entries for installed/config-present clients:
37
60
 
38
61
  ```sh
39
62
  enigma setup --connect-installed --overwrite
@@ -53,10 +76,10 @@ enigma connect claude-desktop --dry-run
53
76
 
54
77
  The local Enigma vault remains canonical. Provider-native memory is non-canonical and should be treated as a convenience cache only. Enigma receipts prove Enigma-controlled vault state, receipts, checkpoints, and declared boundary operations; they do not prove provider deletion, provider model forgetting, provider-native memory removal, hosted availability, ROI/savings, or compliance certification.
55
78
 
56
- One-off execution without a global install:
79
+ One-off public test drive without a global install:
57
80
 
58
81
  ```sh
59
- npx --yes --package enigma-memory enigma setup --overwrite
82
+ npx --yes --package enigma-memory enigma test-drive --overwrite
60
83
  ```
61
84
 
62
85
  ## Advanced/source-only path
@@ -164,7 +187,7 @@ Supported connector profiles are:
164
187
  - `opencode`
165
188
  - `generic-mcp`
166
189
 
167
- After `enigma setup --overwrite`, preview a client connection before writing anything:
190
+ After `enigma test-drive --overwrite` proves the local package path and `enigma setup --overwrite` creates a regular local workspace, preview a client connection before writing anything:
168
191
 
169
192
  ```sh
170
193
  enigma connect claude-desktop --dry-run
@@ -3,7 +3,7 @@ import { createHash } from 'node:crypto';
3
3
  import { createServer as createHttpServer } from 'node:http';
4
4
  import { realpathSync } from 'node:fs';
5
5
  import { access, mkdir, readFile, readdir, writeFile } from 'node:fs/promises';
6
- import { dirname, resolve } from 'node:path';
6
+ import { dirname, isAbsolute, join, resolve } from 'node:path';
7
7
  import { pathToFileURL } from 'node:url';
8
8
  import { createVault, remember, recall, updateMemory, deleteMemory, exportBundle } from '../../../packages/vault/src/index.js';
9
9
  import { createPassport, compileContextPack } from '../../../packages/passport/src/index.js';
@@ -32,6 +32,9 @@ import {
32
32
  } from '../../../packages/settlement/src/index.js';
33
33
 
34
34
  const DEFAULT_BUNDLE = '.enigma/bundle.json';
35
+ const DEFAULT_TEST_DRIVE_DIR = '.enigma/test-drive';
36
+ const DEFAULT_TEST_DRIVE_BUNDLE_NAME = 'bundle.json';
37
+ const DEFAULT_TEST_DRIVE_CROSS_MODEL_REPORT_NAME = 'cross-model-report.json';
35
38
  export const DEFAULT_RELAY_PORT = 8787;
36
39
  export const DEFAULT_GATEWAY_PORT = 8797;
37
40
  const DEFAULT_QUICKSTART_MEMORY = 'Enigma quickstart demo memory: local proof bundles can be created and verified without provider or cloud credentials.';
@@ -279,10 +282,10 @@ function quickstartOutputs(bundleInput, outDirInput) {
279
282
  };
280
283
  }
281
284
 
282
- async function buildQuickstartArtifacts(flags, { bundleInput = DEFAULT_BUNDLE, outDirInput = dirname(bundleInput), overwrite = false, write = true } = {}) {
285
+ async function buildQuickstartArtifacts(flags, { bundleInput = DEFAULT_BUNDLE, outDirInput = dirname(bundleInput), overwrite = false, write = true, checkExisting = true } = {}) {
283
286
  const paths = quickstartOutputs(bundleInput, outDirInput);
284
287
  ensureDistinctOutputPaths(paths.outputs.map((output) => output.path));
285
- await assertCanWriteQuickstartOutputs(paths.outputs, overwrite);
288
+ if (checkExisting) await assertCanWriteQuickstartOutputs(paths.outputs, overwrite);
286
289
 
287
290
  const vault = createVault({
288
291
  subjectId: String(getFlag(flags, ['subject', 'subject-id'], 'local-user')),
@@ -1133,6 +1136,39 @@ export async function quickstartCommand(flags, io) {
1133
1136
  return artifacts.verifyReport.ok === true ? 0 : 1;
1134
1137
  }
1135
1138
 
1139
+ function buildCrossModelProfileSummaries({ vault, passport, demoMemoryAddr, limit }) {
1140
+ const profiles = [];
1141
+ for (const profile of CROSS_MODEL_PROFILES) {
1142
+ const pack = compileContextPack({
1143
+ vault,
1144
+ passport,
1145
+ provider: profile.provider,
1146
+ model: profile.model,
1147
+ query: 'memory follows me across models',
1148
+ purpose: `cross_model_demo:${profile.id}`,
1149
+ memory_addresses: [demoMemoryAddr],
1150
+ limit,
1151
+ });
1152
+ const contextPack = publicContextPackSummary(pack);
1153
+ profiles.push({
1154
+ profile: profile.id,
1155
+ label: profile.label,
1156
+ provider: profile.provider,
1157
+ model: profile.model,
1158
+ context_pack_ref: contextPack.context_pack_ref,
1159
+ context_pack_id: contextPack.context_pack_id,
1160
+ context_pack_digest: contextPack.context_pack_digest,
1161
+ context_pack: contextPack,
1162
+ receipt_count: contextPack.receipt_count,
1163
+ memory_count: contextPack.memory_count,
1164
+ provider_native_memory_canonical: false,
1165
+ receipts: contextPack.receipts,
1166
+ claim_boundaries: { ...CROSS_MODEL_CLAIM_BOUNDARIES },
1167
+ });
1168
+ }
1169
+ return profiles;
1170
+ }
1171
+
1136
1172
  export async function crossModelDemoCommand(flags, io) {
1137
1173
  const bundleFlag = getFlag(flags, ['bundle', 'file']);
1138
1174
  if (bundleFlag === true || bundleFlag === '') throw new Error('Missing required --bundle.');
@@ -1189,35 +1225,7 @@ export async function crossModelDemoCommand(flags, io) {
1189
1225
  const limit = integerFlag(flags, ['limit'], 'limit', 1);
1190
1226
  if (limit < 1) throw new Error('--limit must be at least 1.');
1191
1227
  const receiptCountBeforeProfiles = Array.isArray(vault.receipts) ? vault.receipts.length : 0;
1192
- const profiles = [];
1193
- for (const profile of CROSS_MODEL_PROFILES) {
1194
- const pack = compileContextPack({
1195
- vault,
1196
- passport,
1197
- provider: profile.provider,
1198
- model: profile.model,
1199
- query: 'memory follows me across models',
1200
- purpose: `cross_model_demo:${profile.id}`,
1201
- memory_addresses: [demoMemoryAddr],
1202
- limit,
1203
- });
1204
- const contextPack = publicContextPackSummary(pack);
1205
- profiles.push({
1206
- profile: profile.id,
1207
- label: profile.label,
1208
- provider: profile.provider,
1209
- model: profile.model,
1210
- context_pack_ref: contextPack.context_pack_ref,
1211
- context_pack_id: contextPack.context_pack_id,
1212
- context_pack_digest: contextPack.context_pack_digest,
1213
- context_pack: contextPack,
1214
- receipt_count: contextPack.receipt_count,
1215
- memory_count: contextPack.memory_count,
1216
- provider_native_memory_canonical: false,
1217
- receipts: contextPack.receipts,
1218
- claim_boundaries: { ...CROSS_MODEL_CLAIM_BOUNDARIES },
1219
- });
1220
- }
1228
+ const profiles = buildCrossModelProfileSummaries({ vault, passport, demoMemoryAddr, limit });
1221
1229
 
1222
1230
  const bundle = await persistState(bundlePath, vault);
1223
1231
  const report = {
@@ -1327,20 +1335,14 @@ async function contextCommand(flags, io) {
1327
1335
  }
1328
1336
 
1329
1337
 
1330
- async function searchCommand(flags, io) {
1331
- const bundlePath = resolve(String(getFlag(flags, ['bundle', 'file'], DEFAULT_BUNDLE)));
1332
- const query = String(requireFlag(flags, ['query', 'q'], 'query'));
1333
- const limit = integerFlag(flags, ['limit'], 'limit', 8);
1334
- if (limit < 0) throw new Error('--limit must be non-negative.');
1335
- const includeContent = getFlag(flags, ['include-content', 'includeContent']) === true || getFlag(flags, ['include-content', 'includeContent']) === 'true';
1336
- const { vault } = await loadState(bundlePath);
1338
+ function memorySearchReport({ bundlePath, vault, query, limit, includeContent = false, now = '2026-01-01T00:00:00.000Z' }) {
1337
1339
  const roots = vault.__computeRoots();
1338
1340
  const queryTokens = searchTokensFrom(query);
1339
1341
  const { candidates, byAddress } = searchCandidates(vault, queryTokens);
1340
1342
  const plan = createMemoryOptimizationPlan({
1341
1343
  candidates,
1342
1344
  prompt: query,
1343
- now: getFlag(flags, ['now'], '2026-01-01T00:00:00.000Z'),
1345
+ now,
1344
1346
  });
1345
1347
  const planIndex = new Map(plan.items.map((item, index) => [item.address, index]));
1346
1348
  const selectedItems = plan.items
@@ -1381,7 +1383,7 @@ async function searchCommand(flags, io) {
1381
1383
  ...(includeContent ? { content: hit.content } : {}),
1382
1384
  };
1383
1385
  });
1384
- print({
1386
+ return {
1385
1387
  ok: true,
1386
1388
  schema: 'enigma.memory_search.v1',
1387
1389
  bundle: bundlePath,
@@ -1395,18 +1397,33 @@ async function searchCommand(flags, io) {
1395
1397
  claim_boundary: includeContent
1396
1398
  ? '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.'
1397
1399
  : '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.',
1398
- }, io);
1399
- return 0;
1400
+ };
1400
1401
  }
1401
1402
 
1402
- async function statusCommand(flags, io) {
1403
+ async function searchCommand(flags, io) {
1403
1404
  const bundlePath = resolve(String(getFlag(flags, ['bundle', 'file'], DEFAULT_BUNDLE)));
1404
- const { stored, vault, passport } = await loadState(bundlePath);
1405
+ const query = String(requireFlag(flags, ['query', 'q'], 'query'));
1406
+ const limit = integerFlag(flags, ['limit'], 'limit', 8);
1407
+ if (limit < 0) throw new Error('--limit must be non-negative.');
1408
+ const includeContent = getFlag(flags, ['include-content', 'includeContent']) === true || getFlag(flags, ['include-content', 'includeContent']) === 'true';
1409
+ const { vault } = await loadState(bundlePath);
1410
+ print(memorySearchReport({
1411
+ bundlePath,
1412
+ vault,
1413
+ query,
1414
+ limit,
1415
+ includeContent,
1416
+ now: getFlag(flags, ['now'], '2026-01-01T00:00:00.000Z'),
1417
+ }), io);
1418
+ return 0;
1419
+ }
1420
+
1421
+ function passportStatusReport({ bundlePath, stored = {}, vault, passport }) {
1405
1422
  const roots = vault.__computeRoots();
1406
1423
  const activeCount = activeMemoryCount(vault);
1407
1424
  const tombstoneCount = vault.tombstones instanceof Map ? vault.tombstones.size : 0;
1408
1425
  const receiptCount = Array.isArray(vault.receipts) ? vault.receipts.length : 0;
1409
- print({
1426
+ return {
1410
1427
  ok: true,
1411
1428
  schema: 'enigma.passport_status.v1',
1412
1429
  bundle: bundlePath,
@@ -1434,10 +1451,317 @@ async function statusCommand(flags, io) {
1434
1451
  `enigma connect <client> --bundle "${bundlePath}"`,
1435
1452
  ],
1436
1453
  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.',
1437
- }, io);
1454
+ };
1455
+ }
1456
+
1457
+ async function statusCommand(flags, io) {
1458
+ const bundlePath = resolve(String(getFlag(flags, ['bundle', 'file'], DEFAULT_BUNDLE)));
1459
+ const { stored, vault, passport } = await loadState(bundlePath);
1460
+ print(passportStatusReport({ bundlePath, stored, vault, passport }), io);
1438
1461
  return 0;
1439
1462
  }
1440
1463
 
1464
+ function testDrivePathDisplay(outDirInput, name) {
1465
+ return isAbsolute(outDirInput) ? join(outDirInput, name) : quickstartPathDisplay(outDirInput, name);
1466
+ }
1467
+
1468
+ function testDriveBundleDisplay(outDirInput) {
1469
+ return testDrivePathDisplay(outDirInput, DEFAULT_TEST_DRIVE_BUNDLE_NAME);
1470
+ }
1471
+
1472
+ function testDriveOutputs(outDirInput, bundleInput = testDriveBundleDisplay(outDirInput)) {
1473
+ const quickstart = quickstartOutputs(bundleInput, outDirInput);
1474
+ const contextPackDisplay = testDrivePathDisplay(outDirInput, QUICKSTART_ARTIFACT_NAMES.contextPack);
1475
+ const exportDisplay = testDrivePathDisplay(outDirInput, QUICKSTART_ARTIFACT_NAMES.export);
1476
+ const verifyReportDisplay = testDrivePathDisplay(outDirInput, QUICKSTART_ARTIFACT_NAMES.verifyReport);
1477
+ const crossModelReportDisplay = testDrivePathDisplay(outDirInput, DEFAULT_TEST_DRIVE_CROSS_MODEL_REPORT_NAME);
1478
+ const crossModelReportPath = resolve(quickstart.outDirPath, DEFAULT_TEST_DRIVE_CROSS_MODEL_REPORT_NAME);
1479
+ const artifacts = [
1480
+ { role: 'bundle', path: quickstart.bundlePath, display: bundleInput, schema: 'enigma.bundle.v1' },
1481
+ { role: 'context_pack', path: quickstart.contextPackPath, display: contextPackDisplay, schema: 'enigma.context_pack.v1' },
1482
+ { role: 'export', path: quickstart.exportPath, display: exportDisplay, schema: 'enigma.bundle.v1' },
1483
+ { role: 'verify_report', path: quickstart.verifyReportPath, display: verifyReportDisplay, schema: 'enigma.verify_report.v1' },
1484
+ { role: 'cross_model_report', path: crossModelReportPath, display: crossModelReportDisplay, schema: 'enigma.cross_model_demo.v1' },
1485
+ ];
1486
+ return {
1487
+ ...quickstart,
1488
+ contextPackDisplay,
1489
+ exportDisplay,
1490
+ verifyReportDisplay,
1491
+ crossModelReportPath,
1492
+ crossModelReportDisplay,
1493
+ artifacts,
1494
+ outputs: artifacts.map((artifact) => ({ path: artifact.path, display: artifact.display })),
1495
+ };
1496
+ }
1497
+
1498
+ function testDriveFileSummaries(artifacts, written) {
1499
+ return artifacts.map((artifact) => ({
1500
+ role: artifact.role,
1501
+ path: artifact.display,
1502
+ schema: artifact.schema,
1503
+ written: Boolean(written),
1504
+ }));
1505
+ }
1506
+
1507
+ function firstActiveMemoryAddress(vault) {
1508
+ if (!(vault.activeAddresses instanceof Set)) throw new Error('Test drive vault did not expose an active memory set.');
1509
+ const first = vault.activeAddresses.values().next();
1510
+ if (first.done) throw new Error('Test drive vault did not create a demo memory.');
1511
+ return first.value;
1512
+ }
1513
+
1514
+ function testDriveNextCommands(bundleDisplay, crossModelReportDisplay) {
1515
+ const quotedBundle = commandPath(bundleDisplay);
1516
+ const quotedReport = commandPath(crossModelReportDisplay);
1517
+ return [
1518
+ `enigma status --bundle ${quotedBundle}`,
1519
+ `enigma search --bundle ${quotedBundle} --query "local proof bundle"`,
1520
+ `enigma demo cross-model --bundle ${quotedBundle} --out ${quotedReport}`,
1521
+ 'node scripts/run-memory-benchmarks.mjs',
1522
+ ];
1523
+ }
1524
+
1525
+ function testDriveFlowCommands({ bundleDisplay, outDirInput, crossModelReportDisplay, overwrite }) {
1526
+ const overwriteSuffix = overwrite ? ' --overwrite' : '';
1527
+ const quotedBundle = commandPath(bundleDisplay);
1528
+ const quotedOutDir = commandPath(outDirInput);
1529
+ const quotedReport = commandPath(crossModelReportDisplay);
1530
+ return [
1531
+ `enigma quickstart --bundle ${quotedBundle} --out-dir ${quotedOutDir}${overwriteSuffix}`,
1532
+ `enigma status --bundle ${quotedBundle}`,
1533
+ `enigma search --bundle ${quotedBundle} --query "local proof bundle"`,
1534
+ `enigma demo cross-model --bundle ${quotedBundle} --out ${quotedReport}`,
1535
+ ];
1536
+ }
1537
+
1538
+ function testDriveBenchmarkPointers() {
1539
+ return [
1540
+ {
1541
+ command: 'node scripts/run-memory-benchmarks.mjs',
1542
+ public_safe: true,
1543
+ planned_only: true,
1544
+ requires_repo_checkout: true,
1545
+ external_provider_calls: false,
1546
+ raw_memory_included: false,
1547
+ claim_boundary: 'Runs deterministic local fixture operations only; it is not a provider comparison, hosted service proof, benchmark leadership claim, ROI claim, provider deletion proof, or model forgetting proof.',
1548
+ },
1549
+ {
1550
+ command: 'node scripts/download-standard-benchmarks.mjs --dry-run',
1551
+ public_safe: true,
1552
+ planned_only: true,
1553
+ requires_repo_checkout: true,
1554
+ external_provider_calls: false,
1555
+ raw_memory_included: false,
1556
+ claim_boundary: 'Plans official dataset downloads without fetching by default; raw benchmark records are not included in the public plan.',
1557
+ },
1558
+ {
1559
+ command: 'node scripts/run-standard-memory-benchmarks.mjs --locomo <path> --longmemeval <path>',
1560
+ public_safe: true,
1561
+ planned_only: true,
1562
+ requires_repo_checkout: true,
1563
+ external_provider_calls: false,
1564
+ raw_memory_included: false,
1565
+ claim_boundary: 'Runs local deterministic retrieval proxies against operator-supplied dataset files; it emits no provider API calls, competitor scores, benchmark leadership claim, or model-forgetting claim.',
1566
+ },
1567
+ ];
1568
+ }
1569
+
1570
+ function testDriveClaimBoundaries() {
1571
+ return {
1572
+ local_only: true,
1573
+ credentials_required: false,
1574
+ external_provider_calls: false,
1575
+ client_config_writes_performed: false,
1576
+ plaintext_memory_echoed: false,
1577
+ hosted_saas_live_claim: false,
1578
+ provider_native_memory_canonical: false,
1579
+ provider_deletion_proof: false,
1580
+ model_forgetting_proof: false,
1581
+ benchmark_leadership_claim: false,
1582
+ compliance_certification: false,
1583
+ };
1584
+ }
1585
+
1586
+ function publicTestDriveStatusSummary(summary, bundleDisplay) {
1587
+ return {
1588
+ ...summary,
1589
+ bundle: bundleDisplay,
1590
+ connector_readiness: {
1591
+ ...summary.connector_readiness,
1592
+ bundle: bundleDisplay,
1593
+ },
1594
+ next_recommended_commands: [
1595
+ `enigma remember --bundle ${commandPath(bundleDisplay)} --text-file <path>`,
1596
+ `enigma search --bundle ${commandPath(bundleDisplay)} --query <text>`,
1597
+ `enigma context --bundle ${commandPath(bundleDisplay)} --query <text>`,
1598
+ `enigma verify --bundle ${commandPath(bundleDisplay)}`,
1599
+ `enigma connect <client> --bundle ${commandPath(bundleDisplay)}`,
1600
+ ],
1601
+ };
1602
+ }
1603
+
1604
+ function publicTestDriveSearchSummary(summary, bundleDisplay) {
1605
+ return {
1606
+ ...summary,
1607
+ bundle: bundleDisplay,
1608
+ };
1609
+ }
1610
+
1611
+ function staticSetupSelection(requestedSelection) {
1612
+ return {
1613
+ ...requestedSelection,
1614
+ fallback_used: false,
1615
+ selected: requestedSelection.clients.map((clientId) => {
1616
+ const profile = getClientProfile(clientId);
1617
+ return publicSetupClientSelectionEntry({ client_id: clientId, display_name: profile.display_name }, requestedSelection.mode === 'default' ? 'default_setup_client' : 'explicit_client');
1618
+ }),
1619
+ skipped: [],
1620
+ connectable_client_ids: null,
1621
+ };
1622
+ }
1623
+
1624
+ export async function testDriveCommand(flags, io) {
1625
+ const outDirInput = pathFlag(flags, ['out-dir', 'outDir'], DEFAULT_TEST_DRIVE_DIR);
1626
+ const bundleInput = pathFlag(flags, ['bundle', 'file'], testDriveBundleDisplay(outDirInput));
1627
+ const overwrite = booleanFlag(flags, ['overwrite'], false);
1628
+ const dryRun = booleanFlag(flags, ['dry-run', 'dryRun'], false);
1629
+ const outputs = testDriveOutputs(outDirInput, bundleInput);
1630
+ ensureDistinctOutputPaths(outputs.outputs.map((output) => output.path));
1631
+ if (!dryRun) await assertCanWriteQuickstartOutputs(outputs.outputs, overwrite);
1632
+
1633
+ const artifacts = await buildQuickstartArtifacts(flags, {
1634
+ bundleInput,
1635
+ outDirInput,
1636
+ overwrite,
1637
+ write: false,
1638
+ checkExisting: false,
1639
+ });
1640
+ const requestedSelection = setupClientIds(flags);
1641
+ const selection = requestedSelection.auto
1642
+ ? await setupAutoClientSelection(flags, artifacts, DEFAULT_SETUP_CLIENTS, 'auto')
1643
+ : staticSetupSelection(requestedSelection);
1644
+ const demoMemoryAddr = firstActiveMemoryAddress(artifacts.vault);
1645
+ const crossModelLimit = integerFlag(flags, ['limit'], 'limit', 1);
1646
+ if (crossModelLimit < 1) throw new Error('--limit must be at least 1.');
1647
+ const receiptCountBeforeProfiles = Array.isArray(artifacts.vault.receipts) ? artifacts.vault.receipts.length : 0;
1648
+ const profiles = buildCrossModelProfileSummaries({
1649
+ vault: artifacts.vault,
1650
+ passport: artifacts.passport,
1651
+ demoMemoryAddr,
1652
+ limit: crossModelLimit,
1653
+ });
1654
+ const finalExport = exportBundle({ vault: artifacts.vault, includePlaintext: false });
1655
+ const finalBundle = finalExport.bundle ?? finalExport;
1656
+ const finalVerifyReport = verifyBundle(finalBundle);
1657
+ const crossModelReport = {
1658
+ ok: true,
1659
+ schema: 'enigma.cross_model_demo.v1',
1660
+ command: 'enigma demo cross-model',
1661
+ story: 'One local Enigma memory is packaged as public-safe context pack references and receipts for ChatGPT, Claude, Kimi, Cursor, and a local LLM. No provider is called.',
1662
+ bundle_ref: bundleInput,
1663
+ bundle_supplied: true,
1664
+ bundle_created: true,
1665
+ demo_only_vault: true,
1666
+ memory_source: 'test_drive_demo',
1667
+ demo_memory_addr: demoMemoryAddr,
1668
+ profile_count: profiles.length,
1669
+ profiles,
1670
+ memory_count: activeMemoryCount(artifacts.vault),
1671
+ receipt_count: Array.isArray(finalBundle.receipts) ? finalBundle.receipts.length : 0,
1672
+ generated_receipt_count: (Array.isArray(finalBundle.receipts) ? finalBundle.receipts.length : 0) - receiptCountBeforeProfiles,
1673
+ provider_credentials_required: false,
1674
+ provider_native_memory_canonical: false,
1675
+ out_written: !dryRun,
1676
+ claim_boundaries: { ...CROSS_MODEL_CLAIM_BOUNDARIES },
1677
+ };
1678
+ const rawStatusSummary = passportStatusReport({
1679
+ bundlePath: outputs.bundlePath,
1680
+ vault: artifacts.vault,
1681
+ passport: artifacts.passport,
1682
+ stored: {
1683
+ owner: {
1684
+ subject_id: artifacts.vault.subject_id,
1685
+ display_name: artifacts.vault.display_name,
1686
+ },
1687
+ },
1688
+ });
1689
+ const statusSummary = publicTestDriveStatusSummary(rawStatusSummary, bundleInput);
1690
+ const rawSearchSummary = memorySearchReport({
1691
+ bundlePath: outputs.bundlePath,
1692
+ vault: artifacts.vault,
1693
+ query: 'local proof bundle',
1694
+ limit: 3,
1695
+ includeContent: false,
1696
+ now: getFlag(flags, ['now'], '2026-01-01T00:00:00.000Z'),
1697
+ });
1698
+ const searchSummary = publicTestDriveSearchSummary(rawSearchSummary, bundleInput);
1699
+
1700
+ if (!dryRun) {
1701
+ await writeJson(outputs.bundlePath, finalBundle);
1702
+ await writeJson(outputs.contextPackPath, publicContextPackSummary(artifacts.contextPack));
1703
+ await writeJson(outputs.exportPath, finalBundle);
1704
+ await writeJson(outputs.verifyReportPath, finalVerifyReport);
1705
+ await writeJson(outputs.crossModelReportPath, crossModelReport);
1706
+ }
1707
+
1708
+ const flowCommands = testDriveFlowCommands({
1709
+ bundleDisplay: bundleInput,
1710
+ outDirInput,
1711
+ crossModelReportDisplay: outputs.crossModelReportDisplay,
1712
+ overwrite,
1713
+ });
1714
+ const nextCommands = testDriveNextCommands(bundleInput, outputs.crossModelReportDisplay);
1715
+ const files = testDriveFileSummaries(outputs.artifacts, !dryRun);
1716
+ const packageJson = await readPackageJson();
1717
+ const ok = finalVerifyReport.ok === true && crossModelReport.ok === true && statusSummary.ok === true && searchSummary.ok === true;
1718
+ print({
1719
+ ok,
1720
+ schema: 'enigma.test_drive.v1',
1721
+ command: 'enigma test-drive',
1722
+ dry_run: dryRun,
1723
+ out_dir: outDirInput,
1724
+ bundle: bundleInput,
1725
+ install_command: `npm install -g ${packageJson.name ?? 'enigma-memory'}`,
1726
+ release_target: '0.1.11',
1727
+ artifacts_written: !dryRun,
1728
+ client_configs_written: false,
1729
+ client_config_write_required: false,
1730
+ memory_plaintext_echoed: false,
1731
+ provider_credentials_required: false,
1732
+ hosted_saas_live: false,
1733
+ files,
1734
+ files_written: dryRun ? [] : files.map((file) => file.path),
1735
+ files_planned: files.map((file) => file.path),
1736
+ commands_run: dryRun ? [] : flowCommands,
1737
+ commands_planned: dryRun ? flowCommands : [],
1738
+ next_commands: nextCommands,
1739
+ benchmark_pointers: testDriveBenchmarkPointers(),
1740
+ setup_summary: {
1741
+ schema: 'enigma.setup.v1',
1742
+ artifacts_written: !dryRun,
1743
+ bundle: bundleInput,
1744
+ context_pack: outputs.contextPackDisplay,
1745
+ export: outputs.exportDisplay,
1746
+ verify_report: outputs.verifyReportDisplay,
1747
+ selected_clients: selection.clients,
1748
+ client_selection: publicSetupClientSelection(selection),
1749
+ client_configs_written: false,
1750
+ provider_credentials_required: false,
1751
+ memory_plaintext_echoed: false,
1752
+ memory_count: Array.isArray(finalBundle.memory_objects) ? finalBundle.memory_objects.length : 0,
1753
+ receipt_count: Array.isArray(finalBundle.receipts) ? finalBundle.receipts.length : 0,
1754
+ context_item_count: Array.isArray(artifacts.contextPack.memories) ? artifacts.contextPack.memories.length : 0,
1755
+ verify_ok: finalVerifyReport.ok === true,
1756
+ },
1757
+ status_summary: statusSummary,
1758
+ search_summary: searchSummary,
1759
+ cross_model_summary: crossModelReport,
1760
+ claim_boundaries: testDriveClaimBoundaries(),
1761
+ }, io);
1762
+ return ok ? 0 : 1;
1763
+ }
1764
+
1441
1765
  async function exportCommand(flags, io) {
1442
1766
  const bundlePath = resolve(String(getFlag(flags, ['bundle', 'file'], DEFAULT_BUNDLE)));
1443
1767
  const { vault } = await loadState(bundlePath);
@@ -1945,6 +2269,7 @@ function usage() {
1945
2269
  'init',
1946
2270
  'setup',
1947
2271
  'quickstart',
2272
+ 'test-drive',
1948
2273
  'demo cross-model',
1949
2274
  'doctor',
1950
2275
  'install',
@@ -2024,6 +2349,13 @@ function usage() {
2024
2349
  '--memory-text <text>': 'Inline demo memory text for non-private demos only.',
2025
2350
  '--overwrite': 'Replace existing quickstart output files.',
2026
2351
  },
2352
+ test_drive_options: {
2353
+ '--out-dir <path>': `Isolated demo directory. Defaults to ${DEFAULT_TEST_DRIVE_DIR}.`,
2354
+ '--bundle <path>': `Bundle JSON to create. Defaults to ${DEFAULT_TEST_DRIVE_DIR}/${DEFAULT_TEST_DRIVE_BUNDLE_NAME}.`,
2355
+ '--client <id|auto>': 'Client setup planning passthrough. No client config files are written by test-drive.',
2356
+ '--overwrite': 'Replace existing test-drive artifact files.',
2357
+ '--dry-run': 'Plan the local test drive without writing artifacts.',
2358
+ },
2027
2359
  cross_model_demo_options: {
2028
2360
  '--bundle <path>': `Reuse a local Enigma bundle. If omitted, ${DEFAULT_CROSS_MODEL_DEMO_BUNDLE} is recreated as a demo-only local vault.`,
2029
2361
  '--memory-file <path>': 'Seed the demo from a local file without echoing plaintext. Alias: --text-file.',
@@ -2098,7 +2430,7 @@ export async function main(argv = process.argv.slice(2), io = { stdout: process.
2098
2430
  const twoPartCommands = ['boundary', 'mcp', 'mesh', 'enterprise', 'capsule', 'relay', 'gateway', 'connect', 'disconnect', 'import', 'native-host', 'meter', 'settlement', 'demo', 'passport'];
2099
2431
  const flags = parseArgs(twoPartCommands.includes(command) ? argv.slice(2) : argv.slice(1));
2100
2432
  const positionalFile = optionalPositional(argv[2]);
2101
- 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'))) {
2433
+ if ((flags.has('help') || argv.includes('-h')) && (command === 'setup' || command === 'test-drive' || 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'))) {
2102
2434
  print(usage(), io);
2103
2435
  return 0;
2104
2436
  }
@@ -2106,6 +2438,7 @@ export async function main(argv = process.argv.slice(2), io = { stdout: process.
2106
2438
  if (command === 'init') return await initCommand(flags, io);
2107
2439
  if (command === 'setup') return await setupCommand(flags, io);
2108
2440
  if (command === 'quickstart') return await quickstartCommand(flags, io);
2441
+ if (command === 'test-drive') return await testDriveCommand(flags, io);
2109
2442
  if (command === 'demo' && subcommand === 'cross-model') return await crossModelDemoCommand(flags, io);
2110
2443
  if (command === 'doctor') return await doctorCommand(flags, io);
2111
2444
  if (command === 'install') return await installCommand(flags, io);