enigma-memory 0.1.16 → 0.1.17

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.
@@ -5,12 +5,16 @@ export const PROOF_NETWORK_CAPABILITY_GRANT_SCHEMA = 'enigma.proof_network.capab
5
5
  export const PROOF_NETWORK_CAPABILITY_REVOCATION_SCHEMA = 'enigma.proof_network.capability_revocation.v1';
6
6
  export const PROOF_NETWORK_BENCHMARK_ATTESTATION_SCHEMA = 'enigma.proof_network.benchmark_attestation.v1';
7
7
  export const PROOF_NETWORK_PACKET_SCHEMA = 'enigma.proof_network.packet.v1';
8
+ export const PROOF_NETWORK_REGISTRY_ENTRY_SCHEMA = 'enigma.proof_network.registry_entry.v1';
9
+ export const PROOF_NETWORK_REGISTRY_BATCH_SCHEMA = 'enigma.proof_network.registry_batch.v1';
8
10
 
9
11
  export const ANCHOR_BATCH_SCHEMA = PROOF_NETWORK_ANCHOR_BATCH_SCHEMA;
10
12
  export const CAPABILITY_GRANT_SCHEMA = PROOF_NETWORK_CAPABILITY_GRANT_SCHEMA;
11
13
  export const CAPABILITY_REVOCATION_SCHEMA = PROOF_NETWORK_CAPABILITY_REVOCATION_SCHEMA;
12
14
  export const BENCHMARK_ATTESTATION_SCHEMA = PROOF_NETWORK_BENCHMARK_ATTESTATION_SCHEMA;
13
15
  export const PACKET_SCHEMA = PROOF_NETWORK_PACKET_SCHEMA;
16
+ export const REGISTRY_ENTRY_SCHEMA = PROOF_NETWORK_REGISTRY_ENTRY_SCHEMA;
17
+ export const REGISTRY_BATCH_SCHEMA = PROOF_NETWORK_REGISTRY_BATCH_SCHEMA;
14
18
 
15
19
  export const PROOF_NETWORK_SCHEMAS = Object.freeze({
16
20
  anchor_batch: PROOF_NETWORK_ANCHOR_BATCH_SCHEMA,
@@ -18,6 +22,8 @@ export const PROOF_NETWORK_SCHEMAS = Object.freeze({
18
22
  capability_revocation: PROOF_NETWORK_CAPABILITY_REVOCATION_SCHEMA,
19
23
  benchmark_attestation: PROOF_NETWORK_BENCHMARK_ATTESTATION_SCHEMA,
20
24
  packet: PROOF_NETWORK_PACKET_SCHEMA,
25
+ registry_entry: PROOF_NETWORK_REGISTRY_ENTRY_SCHEMA,
26
+ registry_batch: PROOF_NETWORK_REGISTRY_BATCH_SCHEMA,
21
27
  });
22
28
 
23
29
  const SHA256_PREFIX = 'sha256:';
@@ -36,6 +42,14 @@ const SAFE_FIELD_NAMES = new Set(Object.keys(SAFE_BOOLEAN_BOUNDARIES));
36
42
  const FORBIDDEN_KEY_RE = /(?:^|_)(?:raw|plaintext|plain_text|prompt|prompts|message|messages|text|content|document|documents|transcript|transcripts|completion|completions|embedding|embeddings|acl|acl_body|access_control_list|provider_response|provider_responses|response_body|credential|credentials|api_key|secret|password|private_key|seed|seed_phrase|mnemonic|tenant_name|customer_name|organization_name|org_name)(?:$|_)/iu;
37
43
  const SECRET_VALUE_RE = /(?:Bearer\s+[A-Za-z0-9._~+/=-]{12,}|Basic\s+[A-Za-z0-9+/=-]{12,}|-----BEGIN [A-Z ]*PRIVATE KEY-----|https?:\/\/[^\s/@]+:[^\s/@]+@|sk-[A-Za-z0-9_-]{16,}|AKIA[0-9A-Z]{16}|(?:seed phrase|mnemonic phrase|raw memory|private prompt|full transcript|provider response|embedding vector))/iu;
38
44
  const SUPPORTED_ARTIFACT_SCHEMAS = new Set(Object.values(PROOF_NETWORK_SCHEMAS));
45
+ const REGISTRY_ENTRY_TYPES = Object.freeze(new Set([
46
+ 'anchor_batch',
47
+ 'benchmark_attestation',
48
+ 'connector_conformance',
49
+ 'health_report',
50
+ 'operator_receipt',
51
+ 'settlement_job',
52
+ ]));
39
53
 
40
54
  function isPlainObject(value) {
41
55
  return value !== null && typeof value === 'object' && !Array.isArray(value);
@@ -296,6 +310,10 @@ function artifactHash(artifact, index) {
296
310
  return requiredString(artifact.benchmark_attestation_hash, `artifacts[${index}].benchmark_attestation_hash`);
297
311
  case PROOF_NETWORK_PACKET_SCHEMA:
298
312
  return requiredString(artifact.proof_network_packet_hash, `artifacts[${index}].proof_network_packet_hash`);
313
+ case PROOF_NETWORK_REGISTRY_ENTRY_SCHEMA:
314
+ return requiredString(artifact.registry_entry_hash, `artifacts[${index}].registry_entry_hash`);
315
+ case PROOF_NETWORK_REGISTRY_BATCH_SCHEMA:
316
+ return requiredString(artifact.registry_batch_hash, `artifacts[${index}].registry_batch_hash`);
299
317
  default:
300
318
  throw new TypeError(`artifacts[${index}] has unsupported schema`);
301
319
  }
@@ -313,6 +331,10 @@ function validateSupportedArtifact(artifact) {
313
331
  return validateBenchmarkAttestation(artifact);
314
332
  case PROOF_NETWORK_PACKET_SCHEMA:
315
333
  return validateProofNetworkPacket(artifact);
334
+ case PROOF_NETWORK_REGISTRY_ENTRY_SCHEMA:
335
+ return validateRegistryEntry(artifact);
336
+ case PROOF_NETWORK_REGISTRY_BATCH_SCHEMA:
337
+ return validateRegistryBatch(artifact);
316
338
  default:
317
339
  return Object.freeze({ ok: false, valid: false, errors: Object.freeze(['unsupported artifact schema']) });
318
340
  }
@@ -568,3 +590,120 @@ export function validateProofNetworkPacket(packet) {
568
590
  validateIdentity(packet, errors, 'proof_network_packet_id', 'proof_network_packet_hash', 'pnp');
569
591
  });
570
592
  }
593
+
594
+ function registryEntryType(value, field = 'entry_type') {
595
+ const type = optionalString(value, undefined, field);
596
+ if (type === undefined || !REGISTRY_ENTRY_TYPES.has(type)) {
597
+ throw new TypeError(`${field} must be one of ${[...REGISTRY_ENTRY_TYPES].join(', ')}`);
598
+ }
599
+ return type;
600
+ }
601
+
602
+ export function createRegistryEntry(input = {}) {
603
+ requiredObject(input, 'input');
604
+ assertNoPrivateProofPayload(input, 'input');
605
+ const entryType = registryEntryType(input.entry_type ?? input.entryType ?? input.type, 'entry_type');
606
+ const artifactHash = digestRef(input.artifact_hash ?? input.artifactHash ?? input.digest_ref ?? input.digestRef, 'artifact_hash');
607
+ const artifactSchemaRef = publicRef(input.artifact_schema_ref ?? input.artifactSchemaRef ?? input.schema_ref ?? input.schemaRef, 'artifact_schema_ref');
608
+ const digestRefs = digestArray(input.digest_refs ?? input.digestRefs ?? input.digest_roots ?? input.digestRoots ?? input.roots ?? artifactHash, 'digest_refs', { min: 1, max: 64 });
609
+ const signerRefs = publicRefArray(input.signer_refs ?? input.signerRefs ?? input.signer_ref ?? input.signerRef, 'signer_refs', { min: 0, max: 64 });
610
+ const entryRef = optionalPublicRef(input.entry_ref ?? input.entryRef, refFromDigest('registry-entry', artifactHash), 'entry_ref');
611
+ const body = {
612
+ schema: PROOF_NETWORK_REGISTRY_ENTRY_SCHEMA,
613
+ registered_at: isoTimestamp(input.registered_at ?? input.registeredAt ?? input.created_at ?? input.createdAt ?? input.generated_at ?? input.generatedAt, 'registered_at'),
614
+ entry_type: entryType,
615
+ entry_ref: entryRef,
616
+ registry_ref: optionalPublicRef(input.registry_ref ?? input.registryRef ?? input.marketplace_ref ?? input.marketplaceRef, 'registry:memory-drive-marketplace', 'registry_ref'),
617
+ artifact_schema_ref: artifactSchemaRef,
618
+ artifact_hash: artifactHash,
619
+ digest_root: sha256Json(digestRefs),
620
+ digest_refs: digestRefs,
621
+ signer_refs: signerRefs,
622
+ entry_count: nonNegativeInteger(input.entry_count ?? input.entryCount ?? input.count, 'entry_count', 1),
623
+ ...((input.signature_ref ?? input.signatureRef) ? { signature_ref: signatureRef(input.signature_ref ?? input.signatureRef, 'signature_ref') } : {}),
624
+ ...SAFE_BOOLEAN_BOUNDARIES,
625
+ };
626
+ return freezeArtifact(body, 'registry_entry_id', 'registry_entry_hash', 'pnrg');
627
+ }
628
+
629
+ export function validateRegistryEntry(entry) {
630
+ return collectValidation((errors) => {
631
+ requiredObject(entry, 'entry');
632
+ assertNoPrivateProofPayload(entry, 'entry');
633
+ if (entry.schema !== PROOF_NETWORK_REGISTRY_ENTRY_SCHEMA) errors.push(`schema must be ${PROOF_NETWORK_REGISTRY_ENTRY_SCHEMA}`);
634
+ requireSafeBoundaries(entry, errors, 'entry');
635
+ isoTimestamp(entry.registered_at, 'registered_at');
636
+ if (!REGISTRY_ENTRY_TYPES.has(entry.entry_type)) errors.push(`entry_type must be one of ${[...REGISTRY_ENTRY_TYPES].join(', ')}`);
637
+ publicRef(entry.entry_ref, 'entry_ref');
638
+ publicRef(entry.registry_ref, 'registry_ref');
639
+ publicRef(entry.artifact_schema_ref, 'artifact_schema_ref');
640
+ digestRef(entry.artifact_hash, 'artifact_hash');
641
+ const digestRefs = digestArray(entry.digest_refs, 'digest_refs', { min: 1, max: 64 });
642
+ if (entry.digest_root !== sha256Json(digestRefs)) errors.push('digest_root mismatch');
643
+ publicRefArray(entry.signer_refs, 'signer_refs', { min: 0, max: 64 });
644
+ nonNegativeInteger(entry.entry_count, 'entry_count');
645
+ if (entry.signature_ref !== undefined) signatureRef(entry.signature_ref, 'signature_ref');
646
+ validateIdentity(entry, errors, 'registry_entry_id', 'registry_entry_hash', 'pnrg');
647
+ });
648
+ }
649
+
650
+ export function createRegistryBatch(input = {}) {
651
+ requiredObject(input, 'input');
652
+ assertNoPrivateProofPayload(input, 'input');
653
+ const entries = Object.freeze(arrayInput(input.entries ?? input.registry_entries ?? input.registryEntries).map((entry, index) => {
654
+ requiredObject(entry, `entries[${index}]`);
655
+ if (entry.schema !== PROOF_NETWORK_REGISTRY_ENTRY_SCHEMA) throw new TypeError(`entries[${index}] must be a ${PROOF_NETWORK_REGISTRY_ENTRY_SCHEMA}`);
656
+ const validation = validateRegistryEntry(entry);
657
+ if (!validation.ok) throw new TypeError(`entries[${index}] is invalid: ${validation.errors.join('; ')}`);
658
+ return Object.freeze(entry);
659
+ }));
660
+ if (entries.length === 0) throw new TypeError('entries must be non-empty');
661
+ if (entries.length > 128) throw new TypeError('entries must contain at most 128 entries');
662
+ const entryHashes = Object.freeze(entries.map((entry) => requiredString(entry.registry_entry_hash, 'entries.registry_entry_hash')).sort());
663
+ const registryRoot = sha256Json(entryHashes);
664
+ const body = {
665
+ schema: PROOF_NETWORK_REGISTRY_BATCH_SCHEMA,
666
+ created_at: isoTimestamp(input.created_at ?? input.createdAt ?? input.generated_at ?? input.generatedAt, 'created_at'),
667
+ registry_ref: optionalPublicRef(input.registry_ref ?? input.registryRef ?? input.marketplace_ref ?? input.marketplaceRef, refFromDigest('registry-batch', registryRoot), 'registry_ref'),
668
+ entry_count: entries.length,
669
+ registry_root: registryRoot,
670
+ entry_hashes: entryHashes,
671
+ entries,
672
+ ...SAFE_BOOLEAN_BOUNDARIES,
673
+ };
674
+ return freezeArtifact(body, 'registry_batch_id', 'registry_batch_hash', 'pnrb');
675
+ }
676
+
677
+ export function validateRegistryBatch(batch) {
678
+ return collectValidation((errors) => {
679
+ requiredObject(batch, 'batch');
680
+ assertNoPrivateProofPayload(batch, 'batch');
681
+ if (batch.schema !== PROOF_NETWORK_REGISTRY_BATCH_SCHEMA) errors.push(`schema must be ${PROOF_NETWORK_REGISTRY_BATCH_SCHEMA}`);
682
+ requireSafeBoundaries(batch, errors, 'batch');
683
+ isoTimestamp(batch.created_at, 'created_at');
684
+ publicRef(batch.registry_ref, 'registry_ref');
685
+ const entries = Array.isArray(batch.entries) ? batch.entries : [];
686
+ if (entries.length === 0 || entries.length > 128) errors.push('entries must contain 1-128 entries');
687
+ const entryHashes = [];
688
+ for (const [index, entry] of entries.entries()) {
689
+ requiredObject(entry, `entries[${index}]`);
690
+ if (entry.schema !== PROOF_NETWORK_REGISTRY_ENTRY_SCHEMA) {
691
+ errors.push(`entries[${index}] must be a ${PROOF_NETWORK_REGISTRY_ENTRY_SCHEMA}`);
692
+ continue;
693
+ }
694
+ const validation = validateRegistryEntry(entry);
695
+ if (!validation.ok) errors.push(`entries[${index}] is invalid: ${validation.errors.join('; ')}`);
696
+ entryHashes.push(requiredString(entry.registry_entry_hash, `entries[${index}].registry_entry_hash`));
697
+ }
698
+ entryHashes.sort();
699
+ if (batch.entry_count !== entries.length) errors.push('entry_count mismatch');
700
+ if (batch.registry_root !== sha256Json(entryHashes)) errors.push('registry_root mismatch');
701
+ if (JSON.stringify(batch.entry_hashes) !== JSON.stringify(entryHashes)) errors.push('entry_hashes mismatch');
702
+ validateIdentity(batch, errors, 'registry_batch_id', 'registry_batch_hash', 'pnrb');
703
+ });
704
+ }
705
+
706
+ export const createProofRegistryEntry = createRegistryEntry;
707
+ export const validateProofRegistryEntry = validateRegistryEntry;
708
+ export const createProofRegistryBatch = createRegistryBatch;
709
+ export const validateProofRegistryBatch = validateRegistryBatch;
@@ -23,6 +23,7 @@ const SHA256_PREFIX = 'sha256:';
23
23
  const PUBLIC_REF_RE = /^[a-z0-9][a-z0-9._:/@+-]{2,191}$/u;
24
24
  const SCORE_KEY_RE = /^[a-z][a-z0-9_.:-]{1,63}$/u;
25
25
  const PRIVATE_REPORT_KEY_RE = /(?:^|_)(?:raw_memory|memory_plaintext|plaintext|plain_text|prompt|prompts|conversation|conversations|message|messages|content|body|payload|payloads|document|documents|transcript|transcripts|completion|completions|embedding|embeddings|provider_response|provider_responses|response_body|credential|credentials|api_key|secret|password|private_key|seed|seed_phrase|mnemonic|tenant_name|customer_name|organization_name|org_name|account_id)(?:$|_)/iu;
26
+ const RAW_ANSWER_REPORT_KEY_RE = /(?:^|_)(?:raw_answer|raw_answers|answer_text|answer_texts|answer_body|answer_content|generated_answer|generated_answers|final_answer|final_answers|model_answer|model_answers|llm_answer|llm_answers|provider_answer|provider_answers)(?:$|_)/iu;
26
27
  const ALLOWED_FALSE_REPORT_KEYS = new Set([
27
28
  'raw_private_memory_plaintext_included',
28
29
  'raw_question_text_included',
@@ -55,6 +56,9 @@ const ALLOWED_PUBLIC_REPORT_KEYS = new Set([
55
56
  'estimated_prompt_tokens',
56
57
  'baseline_prompt_tokens',
57
58
  'optimized_prompt_tokens',
59
+ 'prompt_refs',
60
+ 'same_prompts_for_all_rows',
61
+ 'prompts_fixed',
58
62
  ]);
59
63
  const SECRET_VALUE_RE = /(?:Bearer\s+[A-Za-z0-9._~+/=-]{12,}|Basic\s+[A-Za-z0-9+/=-]{12,}|-----BEGIN [A-Z ]*PRIVATE KEY-----|https?:\/\/[^\s/@]+:[^\s/@]+@|sk-[A-Za-z0-9_-]{16,}|AKIA[0-9A-Z]{16}|(?:seed phrase|mnemonic phrase|raw memory|private prompt|full transcript|provider response|embedding vector))/iu;
60
64
  const ABSOLUTE_LOCAL_PATH_RE = /^(?:[A-Za-z]:[\\/]|\\\\|\/(?:Users|home|tmp|var|etc|mnt|Volumes)\b)/u;
@@ -62,6 +66,7 @@ const CLAIM_SCORE_KEY_RE = /(?:provider|competitor|roi|profit|savings|solana|tra
62
66
  const PROOF_COMPATIBLE_REPORT_SCHEMAS = new Set([
63
67
  'enigma.memory_benchmark_suite.v1',
64
68
  'enigma.standard_memory_benchmark_suite.v1',
69
+ 'enigma.standard_memory_benchmark_protocol_plan.v1',
65
70
  ]);
66
71
  const REQUIRED_FALSE_BOUNDARY_KEYS = Object.freeze([
67
72
  'external_provider_calls',
@@ -172,7 +177,7 @@ export function parseArgs(argv = process.argv.slice(2)) {
172
177
  export function usage() {
173
178
  return `Usage: node scripts/build-benchmark-proof-release.mjs --report <path> --dataset-ref <ref> --runner-ref <ref> --package-ref <ref> [--score key=value ...] [--out-dir <dir>]
174
179
 
175
- Builds a dependency-free, local benchmark proof release from an existing public-safe benchmark report. The report must use a compatible benchmark schema and explicit offline benchmark boundaries. The report file is parsed for public-safety checks and hashed, but its body and local path are never copied into the attestation or proof packet. The generated artifacts are local benchmark attestation/proof only: no API calls, provider answer-accuracy claims, Mem0 or competitor performance claims, Solana submissions, hosted SaaS claims, ROI/profit/savings claims, raw memory, prompts, transcripts, embeddings, credentials, account ids, private keys, or provider responses are written.
180
+ Builds a dependency-free, local benchmark proof release from an existing public-safe benchmark report. The report must use a compatible benchmark schema (a scored retrieval proxy report such as enigma.standard_memory_benchmark_suite.v1, or a full-answer protocol plan such as enigma.standard_memory_benchmark_protocol_plan.v1) and explicit offline benchmark boundaries. The report file is parsed for public-safety checks and hashed, but its body and local path are never copied into the attestation or proof packet. The generated artifacts are local benchmark attestation/proof only: no API calls, provider answer-accuracy claims, Mem0 or competitor performance claims, Solana submissions, hosted SaaS claims, ROI/profit/savings claims, raw memory, prompts, transcripts, embeddings, credentials, account ids, private keys, or provider responses are written. A protocol-plan report is accepted as protocol-readiness evidence only and is rejected if it contains raw answers, prompts, provider responses, or competitor scores.
176
181
  `;
177
182
  }
178
183
 
@@ -205,7 +210,7 @@ function assertPublicReportPayload(value, path = 'report') {
205
210
  }
206
211
  if (!isPlainObject(value)) return;
207
212
  for (const [key, child] of Object.entries(value)) {
208
- if (PRIVATE_REPORT_KEY_RE.test(key) && !ALLOWED_PUBLIC_REPORT_KEYS.has(key)) {
213
+ if ((PRIVATE_REPORT_KEY_RE.test(key) || RAW_ANSWER_REPORT_KEY_RE.test(key)) && !ALLOWED_PUBLIC_REPORT_KEYS.has(key)) {
209
214
  if (!ALLOWED_FALSE_REPORT_KEYS.has(key) || child !== false) throw new UsageError(`${path}.${key} is not allowed in public benchmark proof artifacts`);
210
215
  }
211
216
  assertPublicReportPayload(child, `${path}.${key}`);
@@ -275,6 +280,13 @@ function assertProofCompatibleBenchmarkBoundaries(report) {
275
280
  if (report.schema === 'enigma.memory_benchmark_suite.v1') {
276
281
  if (boundaries.local_only !== true) throw new UsageError('local memory benchmark report must set benchmark_boundaries.local_only true');
277
282
  }
283
+ if (report.schema === 'enigma.standard_memory_benchmark_protocol_plan.v1') {
284
+ const protocolBoundaries = report.protocol_boundaries;
285
+ if (!isPlainObject(protocolBoundaries)) throw new UsageError('protocol plan report must include protocol_boundaries');
286
+ for (const key of ['network_required', 'provider_calls_made', 'answers_generated', 'judged']) {
287
+ requireFalseField(protocolBoundaries, key, 'report.protocol_boundaries');
288
+ }
289
+ }
278
290
  assertCommandBoundaries(report);
279
291
  assertExternalAdaptersUnscored(report);
280
292
  }
@@ -404,7 +416,7 @@ export async function buildBenchmarkProofRelease(args, options = {}) {
404
416
  const schema = reportSchema(report);
405
417
  const sampleCount = sampleCountFromReport(report);
406
418
  const datasetCount = Array.isArray(report.datasets) ? report.datasets.length : 0;
407
- const commitments = scoreCommitments(args.scores, reportHash);
419
+ const commitments = scoreCommitments(Array.isArray(args.scores) ? args.scores : [], reportHash);
408
420
  const metricRoots = commitments.map((score) => score.score_hash);
409
421
 
410
422
  const attestation = createBenchmarkAttestation({
@@ -13,7 +13,7 @@ import {
13
13
  } from '../packages/hosted-cloud/src/index.js';
14
14
 
15
15
  export const HOSTED_API_KEY_LIFECYCLE_PACKET_SCHEMA = HOSTED_CLOUD_API_KEY_LIFECYCLE_PACKET_SCHEMA;
16
- export const HOSTED_API_KEY_LIFECYCLE_RELEASE_TARGET = '0.1.16';
16
+ export const HOSTED_API_KEY_LIFECYCLE_RELEASE_TARGET = '0.1.17';
17
17
 
18
18
  const PROVIDED = 'provided';
19
19
  const BLOCKED = 'blocked_external_dependency';
@@ -25,10 +25,13 @@ import {
25
25
  buildUserAccountContract,
26
26
  buildCustomerLifecyclePacket,
27
27
  validateCustomerLifecyclePacket,
28
+ buildHostedCloudReadinessPacket,
29
+ HOSTED_CLOUD_READINESS_PACKET_SCHEMA,
28
30
  } from 'enigma-memory/hosted-cloud';
29
31
 
30
32
  export const HOSTED_CUSTOMER_LIFECYCLE_PACKET_SCHEMA = HOSTED_CLOUD_CUSTOMER_LIFECYCLE_PACKET_SCHEMA;
31
- export const HOSTED_CUSTOMER_LIFECYCLE_RELEASE_TARGET = '0.1.16';
33
+ export const HOSTED_CUSTOMER_LIFECYCLE_RELEASE_TARGET = '0.1.17';
34
+ export const HOSTED_CLOUD_READINESS_RELEASE_SCHEMA = HOSTED_CLOUD_READINESS_PACKET_SCHEMA;
32
35
 
33
36
  const PROVIDED = 'provided';
34
37
  const BLOCKED_MISSING = 'blocked_missing_evidence';
@@ -379,6 +382,16 @@ export function buildHostedCustomerLifecyclePacket(options = {}) {
379
382
  };
380
383
  }
381
384
 
385
+ export function buildHostedCloudReadinessPacketFromArgs(options = {}) {
386
+ const generatedAt = options.generatedAt ?? new Date().toISOString();
387
+ const lifecyclePacket = buildHostedCustomerLifecyclePacket({ ...options, generatedAt });
388
+ return buildHostedCloudReadinessPacket({
389
+ generated_at: generatedAt,
390
+ customer_lifecycle_packet: lifecyclePacket,
391
+ operator_go_live_ref: options.operatorGoLiveRef ?? null,
392
+ });
393
+ }
394
+
382
395
  export function parseArgs(argv) {
383
396
  const args = {
384
397
  tenant: undefined,
@@ -386,6 +399,7 @@ export function parseArgs(argv) {
386
399
  environment: undefined,
387
400
  operatorGoLiveRef: undefined,
388
401
  evidenceRefs: [],
402
+ readiness: false,
389
403
  out: undefined,
390
404
  help: false,
391
405
  };
@@ -395,6 +409,7 @@ export function parseArgs(argv) {
395
409
  args.help = true;
396
410
  continue;
397
411
  }
412
+ if (arg === '--readiness') { args.readiness = true; continue; }
398
413
  const readValue = (name) => {
399
414
  index += 1;
400
415
  if (index >= argv.length || argv[index].startsWith('--')) throw new Error(`${name} requires a value`);
@@ -414,7 +429,7 @@ export function parseArgs(argv) {
414
429
  export function usage() {
415
430
  return `Usage: node scripts/build-hosted-customer-lifecycle.mjs [options]
416
431
 
417
- Build a public-safe hosted customer lifecycle packet. This script validates local contract fragments only; it does not deploy, create accounts, call providers, or write secrets.
432
+ Build a public-safe hosted customer lifecycle packet, or with --readiness a hosted cloud readiness aggregator packet. This script validates local contract fragments only; it does not deploy, create accounts, call providers, or write secrets.
418
433
 
419
434
  Options:
420
435
  --tenant <id> Tenant id. Defaults to blocked:tenant.
@@ -424,6 +439,9 @@ Options:
424
439
  --evidence-ref <key=status:ref> Repeatable evidence ref. key=<ref> implies provided.
425
440
  Status: provided, blocked_missing_evidence, blocked_external_dependency.
426
441
  Keys: ${HOSTED_CUSTOMER_LIFECYCLE_EVIDENCE_KEYS.join(', ')}
442
+ --readiness Emit a hosted cloud readiness aggregator packet (enigma.hosted_cloud.readiness_packet.v1)
443
+ that wraps the lifecycle packet and rolls all readiness surfaces plus
444
+ propagated lifecycle blockers into one public-safe readiness assessment.
427
445
  --out <file> Also write the packet JSON to a file.
428
446
  --help Show this help.
429
447
  `;
@@ -435,7 +453,9 @@ export async function main(argv = process.argv.slice(2)) {
435
453
  process.stdout.write(usage());
436
454
  return 0;
437
455
  }
438
- const packet = buildHostedCustomerLifecyclePacket(args);
456
+ const packet = args.readiness
457
+ ? buildHostedCloudReadinessPacketFromArgs(args)
458
+ : buildHostedCustomerLifecyclePacket(args);
439
459
  const json = `${JSON.stringify(packet, null, 2)}\n`;
440
460
  if (args.out) {
441
461
  try {
@@ -6,7 +6,7 @@ import { fileURLToPath } from 'node:url';
6
6
 
7
7
  export const INSTALLER_ASSET_SCHEMA = 'enigma.installer_assets.v1';
8
8
  export const INSTALLER_ASSET_PACKAGE = 'enigma-memory';
9
- export const INSTALLER_ASSET_VERSION = '0.1.16';
9
+ export const INSTALLER_ASSET_VERSION = '0.1.17';
10
10
  export const INSTALLER_ASSET_GENERATED_AT = '1970-01-01T00:00:00.000Z';
11
11
 
12
12
  const SCRIPT_PATH = fileURLToPath(import.meta.url);