enigma-memory 0.1.18 → 0.1.22
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 +76 -24
- package/apps/cli/bin/enigma-desktop.mjs +140 -0
- package/apps/cli/bin/enigma-terminal.mjs +78 -0
- package/apps/cli/bin/enigma.mjs +1923 -285
- package/apps/desktop/electron-main.cjs +217 -0
- package/apps/desktop/package.json +12 -0
- package/apps/desktop/src/app.js +264 -7
- package/apps/desktop/src/index.html +3514 -1373
- package/apps/desktop/src/launch-electron.mjs +51 -0
- package/apps/desktop/src/server.mjs +2914 -0
- package/apps/desktop/src/styles.css +2972 -260
- package/apps/desktop/src/zk-browser-prove.mjs +53 -0
- package/apps/desktop/src/zk-state.mjs +1789 -0
- package/apps/gateway/bin/enigma-gateway.mjs +102 -5
- package/apps/gateway/src/server.mjs +271 -8
- package/apps/ios/EnigmaCore/Package.swift +12 -0
- package/apps/ios/EnigmaCore/Sources/EnigmaCore/EnigmaAPIClient.swift +227 -0
- package/apps/ios/EnigmaCore/Sources/EnigmaCore/Models.swift +278 -0
- package/apps/ios/EnigmaCore/Sources/EnigmaCore/PKCE.swift +96 -0
- package/apps/ios/EnigmaCore/Sources/EnigmaCore/PrivacyMinimizer.swift +187 -0
- package/apps/ios/EnigmaCore/Sources/EnigmaCore/ToolModels.swift +129 -0
- package/apps/ios/EnigmaCore/Tests/EnigmaCoreTests/EnigmaCoreTests.swift +42 -0
- package/apps/ios/EnigmaIOS/Enigma/AppModel.swift +346 -0
- package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AccentColor.colorset/Contents.json +12 -0
- package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AppIcon.appiconset/Contents.json +11 -0
- package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AppIcon.appiconset/EnigmaAppIcon.png +0 -0
- package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/Contents.json +3 -0
- package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/LaunchBackground.colorset/Contents.json +12 -0
- package/apps/ios/EnigmaIOS/Enigma/ChatView.swift +181 -0
- package/apps/ios/EnigmaIOS/Enigma/CouncilView.swift +78 -0
- package/apps/ios/EnigmaIOS/Enigma/CreateView.swift +152 -0
- package/apps/ios/EnigmaIOS/Enigma/EnigmaApp.swift +52 -0
- package/apps/ios/EnigmaIOS/Enigma/Info.plist +52 -0
- package/apps/ios/EnigmaIOS/Enigma/NaturalLanguagePrivacyTagger.swift +26 -0
- package/apps/ios/EnigmaIOS/Enigma/OAuthClient.swift +321 -0
- package/apps/ios/EnigmaIOS/Enigma/OnboardingView.swift +105 -0
- package/apps/ios/EnigmaIOS/Enigma/PrivateVaultView.swift +275 -0
- package/apps/ios/EnigmaIOS/Enigma/SecureStore.swift +76 -0
- package/apps/ios/EnigmaIOS/Enigma/SettingsView.swift +60 -0
- package/apps/ios/EnigmaIOS/Enigma/Theme.swift +80 -0
- package/apps/ios/EnigmaIOS/EnigmaIOS.xcodeproj/project.pbxproj +211 -0
- package/apps/ios/EnigmaIOS/EnigmaIOS.xcodeproj/xcshareddata/xcschemes/Enigma.xcscheme +23 -0
- package/apps/native-host/README.md +19 -8
- package/apps/native-host/bin/enigma-native-host.mjs +229 -13
- package/apps/relay/bin/enigma-relay.mjs +103 -5
- package/apps/relay/src/federation-runtime.mjs +618 -0
- package/apps/relay/src/server.mjs +310 -9
- package/apps/verifier/bin/enigma-verify.mjs +327 -11
- package/cortex-v3/circuits/build/intent_vk_bytes.json +35 -0
- package/cortex-v3/circuits/build/sale_vk_bytes.json +35 -0
- package/cortex-v3/circuits/build/vk_bytes.json +32 -0
- package/cortex-v3/proving-assets.json +64 -0
- package/cortex-v3/zk/BUILD-CONTRACT.md +87 -0
- package/cortex-v3/zk/action-transition-vk.json +119 -0
- package/cortex-v3/zk/alias-adversarial.test.mjs +220 -0
- package/cortex-v3/zk/groth16-verify-child.mjs +17 -0
- package/cortex-v3/zk/intent-witness.mjs +365 -0
- package/cortex-v3/zk/intent-witness.test.mjs +485 -0
- package/cortex-v3/zk/proving-assets.mjs +203 -0
- package/cortex-v3/zk/sale-witness.mjs +783 -0
- package/cortex-v3/zk/sale-witness.test.mjs +784 -0
- package/cortex-v3/zk/sealed-sale-release-vk.json +119 -0
- package/cortex-v3/zk/settlement-evidence.mjs +722 -0
- package/cortex-v3/zk/setup-intent.mjs +688 -0
- package/cortex-v3/zk/setup-sale.mjs +666 -0
- package/cortex-v3/zk/setup.mjs +594 -0
- package/cortex-v3/zk/witness.mjs +184 -0
- package/cortex-v3/zk/zk-codec.mjs +232 -0
- package/cortex-v3/zk/zk-codec.test.mjs +293 -0
- package/cortex-v3/zk/zk-settle.mjs +370 -0
- package/cortex-v3/zk/zk-tree.mjs +256 -0
- package/cortex-v3/zk/zk-tree.test.mjs +419 -0
- package/deploy/docker-compose.local-production-simulation.yml +36 -0
- package/docs/browser-extension-install.md +8 -6
- package/docs/client-connectors.md +15 -11
- package/docs/developer-ecosystem.md +15 -13
- package/docs/enigma-memory-ready-conformance.md +11 -9
- package/docs/install-anywhere.md +61 -28
- package/docs/installers-and-desktop.md +8 -7
- package/docs/novelty-invention-candidates.md +161 -161
- package/docs/proof-network-claim-boundaries.md +320 -318
- package/examples/01-quickstart-agent/index.mjs +49 -0
- package/examples/01_agent_memory_quickstart.mjs +57 -0
- package/examples/02-multi-agent-swarm/index.mjs +57 -0
- package/examples/02_cross_model_passport.mjs +64 -0
- package/examples/03-langchain-memory/index.mjs +41 -0
- package/examples/03_poseidon_commitment_verification.mjs +71 -0
- package/examples/04-python-trading-agent/trader.py +49 -0
- package/examples/README.md +27 -0
- package/examples/ci/github-actions.yml +7 -2
- package/package.json +142 -11
- package/packages/adapters/PACKAGE_CONTRACT.md +1 -1
- package/packages/connectors/src/index.js +196 -4
- package/packages/connectors/swarm-router.mjs +168 -0
- package/packages/core/src/index.js +248 -1
- package/packages/core/src/version.mjs +7 -0
- package/packages/dev-tools/package.json +19 -0
- package/packages/dev-tools/src/index.js +4 -0
- package/packages/dev-tools/src/memory-benchmark-suite.js +112 -0
- package/packages/dev-tools/src/swarm-simulator.js +101 -0
- package/packages/dev-tools/src/vault-inspector.js +114 -0
- package/packages/dev-tools/src/vector-benchmark.js +100 -0
- package/packages/developer-platform/src/access-credentials.js +341 -0
- package/packages/developer-platform/src/http.js +132 -0
- package/packages/developer-platform/src/index.js +4 -0
- package/packages/developer-platform/src/usage-http.js +60 -0
- package/packages/developer-platform/src/usage.js +295 -0
- package/packages/enclave-runtime/attestation.mjs +159 -0
- package/packages/enclave-runtime/index.mjs +47 -0
- package/packages/enclave-runtime/session-manager.mjs +253 -0
- package/packages/enclave-runtime/zeroization-proof.mjs +227 -0
- package/packages/enigma-reflex/package.json +14 -0
- package/packages/enigma-reflex/src/index.js +204 -0
- package/packages/enigma-reflex/training/generate-dataset.mjs +40 -0
- package/packages/enigma-reflex/training/requirements.txt +8 -0
- package/packages/enigma-reflex/training/train.py +314 -0
- package/packages/enigma-weave/LICENSE +22 -0
- package/packages/enigma-weave/UPSTREAM.json +21 -0
- package/packages/enigma-weave/package.json +14 -0
- package/packages/enigma-weave/src/index.js +286 -0
- package/packages/hosted-cloud/src/index.js +80 -5
- package/packages/importers/src/index.js +432 -0
- package/packages/inference-runtime/src/browser.js +401 -0
- package/packages/inference-runtime/src/chat.js +265 -0
- package/packages/inference-runtime/src/code.js +407 -0
- package/packages/inference-runtime/src/contracts.js +162 -0
- package/packages/inference-runtime/src/http.js +232 -0
- package/packages/inference-runtime/src/image.js +186 -0
- package/packages/inference-runtime/src/index.js +10 -0
- package/packages/inference-runtime/src/model-router.js +320 -0
- package/packages/inference-runtime/src/platform.js +125 -0
- package/packages/inference-runtime/src/privacy.js +400 -0
- package/packages/inference-runtime/src/video.js +253 -0
- package/packages/mcp-server/README.md +22 -6
- package/packages/mcp-server/bin/enigma-mcp.mjs +2 -1
- package/packages/mcp-server/src/index.js +1418 -105
- package/packages/mcp-server/src/oauth.js +561 -0
- package/packages/mcp-server/src/private-handoff.js +84 -0
- package/packages/mcp-server/src/remote-http.js +273 -0
- package/packages/mcp-server/src/remote-policy.js +72 -0
- package/packages/mcp-server/swarm-bridge.mjs +361 -0
- package/packages/mesh/index.d.ts +283 -0
- package/packages/mesh/package.json +23 -0
- package/packages/mesh/src/crypto.js +189 -0
- package/packages/mesh/src/federation-packets.js +353 -0
- package/packages/mesh/src/gossip.js +311 -0
- package/packages/mesh/src/index.js +6 -0
- package/packages/mesh/src/protocol.js +255 -0
- package/packages/mesh/src/router.js +279 -0
- package/packages/mesh/src/transport.js +306 -0
- package/packages/passport/src/index.js +426 -1
- package/packages/private-economy/src/credits-http.js +100 -0
- package/packages/private-economy/src/credits.js +447 -0
- package/packages/private-economy/src/index.js +5 -0
- package/packages/private-economy/src/payments-http.js +120 -0
- package/packages/private-economy/src/payments.js +509 -0
- package/packages/private-economy/src/x402.js +346 -0
- package/packages/proof-network/PACKAGE_CONTRACT.md +21 -0
- package/packages/rag/index.d.ts +182 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/THIRD_PARTY_LICENSES.txt +207 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/config.json +25 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/onnx/model_quantized.onnx +0 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/sha256-manifest.json +28 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/tokenizer.json +30686 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/tokenizer_config.json +15 -0
- package/packages/rag/package.json +27 -0
- package/packages/rag/src/blinded-search.js +109 -0
- package/packages/rag/src/bm25.js +169 -0
- package/packages/rag/src/embeddings.js +459 -0
- package/packages/rag/src/hybrid.js +76 -0
- package/packages/rag/src/index.js +38 -0
- package/packages/rag/src/reranker.js +61 -0
- package/packages/rag/src/research.js +107 -0
- package/packages/rag/src/vector-store.js +430 -0
- package/packages/rag/src/verify-model-artifacts.mjs +4 -0
- package/packages/sdk/index.d.ts +760 -0
- package/packages/sdk/package.json +33 -0
- package/packages/sdk/python/README.md +24 -0
- package/packages/sdk/python/enigma_sdk.py +250 -0
- package/packages/sdk/python/pyproject.toml +34 -0
- package/packages/sdk/python/requirements.txt +1 -0
- package/packages/sdk/python/setup.py +20 -0
- package/packages/sdk/src/federation/capability-grant.js +389 -0
- package/packages/sdk/src/federation/federation-router.js +360 -0
- package/packages/sdk/src/federation/ghostmesh-bridge.js +497 -0
- package/packages/sdk/src/federation/index.js +3 -0
- package/packages/sdk/src/index.js +1796 -0
- package/packages/sdk/src/intelligence/contradiction.js +337 -0
- package/packages/sdk/src/intelligence/decision-engine.js +155 -0
- package/packages/sdk/src/intelligence/index.js +4 -0
- package/packages/sdk/src/intelligence/ontology.js +122 -0
- package/packages/sdk/src/intelligence/temporal.js +123 -0
- package/packages/sdk/src/market-client.js +142 -0
- package/packages/sdk/src/mesh-client.js +110 -0
- package/packages/sdk/src/middleware/index.js +3 -0
- package/packages/sdk/src/middleware/langchain.js +159 -0
- package/packages/sdk/src/middleware/llamaindex.js +101 -0
- package/packages/sdk/src/middleware/vercel-ai.js +112 -0
- package/packages/sdk/src/rag-client.js +85 -0
- package/packages/sdk/src/swarm-orchestrator.js +260 -0
- package/packages/settlement/PACKAGE_CONTRACT.md +1 -1
- package/packages/snapcompact/THIRD_PARTY_LICENSES.txt +40 -0
- package/packages/snapcompact/assets/8x13-latin1.bdf +3837 -0
- package/packages/snapcompact/index.d.ts +284 -0
- package/packages/snapcompact/package.json +25 -0
- package/packages/snapcompact/src/index.js +716 -0
- package/packages/storage/PACKAGE_CONTRACT.md +1 -1
- package/packages/terminal-console/animations.mjs +240 -0
- package/packages/terminal-console/auto-anchor.mjs +220 -0
- package/packages/terminal-console/banner.mjs +91 -0
- package/packages/terminal-console/commands.mjs +459 -0
- package/packages/terminal-console/delegation.mjs +152 -0
- package/packages/terminal-console/index.mjs +5 -0
- package/packages/terminal-console/outbox.mjs +143 -0
- package/packages/terminal-console/phantom-bridge.mjs +637 -0
- package/packages/terminal-console/repl.mjs +136 -0
- package/packages/terminal-console/signer-store.mjs +130 -0
- package/packages/terminal-console/solana-rpc.mjs +214 -0
- package/packages/terminal-console/solana-transport.mjs +189 -0
- package/packages/terminal-tui/dashboard.mjs +214 -0
- package/packages/terminal-tui/index.mjs +28 -0
- package/packages/terminal-tui/merkle-tree-renderer.mjs +268 -0
- package/packages/terminal-tui/telemetry-hud.mjs +137 -0
- package/packages/vault/index.d.ts +449 -0
- package/packages/vault/package.json +27 -0
- package/packages/vault/src/e2ee.mjs +393 -0
- package/packages/vault/src/enclave.js +481 -0
- package/packages/vault/src/erasure.js +207 -0
- package/packages/vault/src/index.js +1018 -155
- package/packages/vault/src/persistence.js +307 -0
- package/packages/vault/src/poseidon.js +354 -0
- package/packages/vault/src/receipt.js +459 -0
- package/scripts/benchmark-optical-context.mjs +166 -0
- package/scripts/bootstrap-enigma.mjs +502 -0
- package/scripts/build-edge-backend-workers.mjs +20 -5
- package/scripts/build-goal-completion-audit.mjs +72 -25
- package/scripts/build-hosted-api-key-lifecycle.mjs +26 -8
- package/scripts/build-hosted-customer-lifecycle.mjs +20 -3
- package/scripts/build-hosted-probe-worker.mjs +19 -4
- package/scripts/build-installer-assets.mjs +41 -21
- package/scripts/build-operator-evidence-starter.mjs +59 -1
- package/scripts/build-production-backend-env-kit.mjs +2 -0
- package/scripts/build-production-unblocker.mjs +3 -0
- package/scripts/check.mjs +17 -3
- package/scripts/collect-hosted-backend-live-evidence.mjs +49 -12
- package/scripts/install-enigma-local.mjs +18 -5
- package/scripts/release-audit.mjs +65 -109
- package/scripts/release-provenance.mjs +6 -0
- package/scripts/run-backend-readiness-smoke.mjs +112 -10
- package/scripts/scan-secrets.mjs +1 -0
- package/scripts/simulate-production-env.mjs +7 -2
- package/scripts/validate-hosted-backend-live.mjs +112 -1
- package/specs/antibody-pack-v1.schema.json +95 -0
- package/specs/antigen-envelope-v1.schema.json +81 -0
- package/specs/boundary-manifest-v1.schema.json +35 -35
- package/specs/capsule-v1.schema.json +55 -55
- package/specs/claim-boundary-manifest-v1.schema.json +22 -22
- package/specs/claim-ledger-v1.schema.json +291 -0
- package/specs/context-passport-v1.schema.json +59 -0
- package/specs/deletion-tombstone-v1.schema.json +26 -26
- package/specs/evidence-packet-v1.schema.json +177 -0
- package/specs/hosted-backend-live-evidence-v1.schema.json +72 -3
- package/specs/immune-scan-report-v1.schema.json +112 -0
- package/specs/lifecycle-receipt-log-v1.schema.json +67 -0
- package/specs/memory-atom-v1.schema.json +59 -0
- package/specs/memory-event-v1.schema.json +42 -42
- package/specs/passport-v1.schema.json +50 -50
- package/specs/proof-of-non-use-v1.schema.json +65 -0
- package/specs/quarantine-record-v1.schema.json +126 -0
- package/specs/receipt-v1.schema.json +61 -61
- package/specs/state-checkpoint-v1.schema.json +37 -37
- package/specs/trust-bundle-v1.schema.json +56 -56
- package/specs/trust-card-v1.schema.json +119 -0
- package/docs/proof-network-launch-plan.md +0 -421
- package/packages/metering/PACKAGE_CONTRACT.md +0 -20
- package/scripts/build-ai-orchestration-plan.mjs +0 -248
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
export const ENIGMA_WEAVE_VERSION = '1.0.0';
|
|
4
|
+
export const WEAVE_POLICIES = Object.freeze({
|
|
5
|
+
lean: Object.freeze({ maxCalls: 1, maxLatencyMs: 45_000 }),
|
|
6
|
+
balanced: Object.freeze({ maxCalls: 2, maxLatencyMs: 60_000 }),
|
|
7
|
+
strict: Object.freeze({ maxCalls: 3, maxLatencyMs: 120_000 }),
|
|
8
|
+
});
|
|
9
|
+
export const WEAVE_PIPELINES = Object.freeze({
|
|
10
|
+
general: Object.freeze({ name: 'general', direct: 'Answer directly and completely. Keep the result proportionate to the request.' }),
|
|
11
|
+
research: Object.freeze({ name: 'research', direct: 'Return a complete research synthesis separating evidence, inference, unknowns, and implications.' }),
|
|
12
|
+
'code-review': Object.freeze({ name: 'code-review', direct: 'Return a senior engineering review with concrete failure modes, fixes, and regression checks.' }),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
function positiveInteger(value, fallback, field) {
|
|
16
|
+
const candidate = value === undefined ? fallback : value;
|
|
17
|
+
if (!Number.isInteger(candidate) || candidate <= 0) throw new TypeError(`${field} must be a positive integer`);
|
|
18
|
+
return candidate;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function nonNegativeNumber(value, fallback, field) {
|
|
22
|
+
const candidate = value === undefined ? fallback : value;
|
|
23
|
+
if (!Number.isFinite(candidate) || candidate < 0) throw new TypeError(`${field} must be a non-negative number`);
|
|
24
|
+
return candidate;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function normalizeWorker(worker, index) {
|
|
28
|
+
if (!worker || typeof worker !== 'object') throw new TypeError(`worker ${index} must be an object`);
|
|
29
|
+
const id = String(worker.id || '').trim();
|
|
30
|
+
const model = String(worker.model || '').trim();
|
|
31
|
+
if (!id || !model) throw new TypeError(`worker ${index} requires id and model`);
|
|
32
|
+
return Object.freeze({
|
|
33
|
+
id,
|
|
34
|
+
model,
|
|
35
|
+
role: String(worker.role || id),
|
|
36
|
+
estimatedCredits: nonNegativeNumber(worker.estimatedCredits, 0, `worker ${id} estimatedCredits`),
|
|
37
|
+
retentionPolicy: String(worker.retentionPolicy || 'unknown'),
|
|
38
|
+
retentionEvidence: worker.retentionEvidence || null,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function pickWorker(workers, preferredRoles) {
|
|
43
|
+
for (const role of preferredRoles) {
|
|
44
|
+
const match = workers.find((worker) => worker.role === role || worker.id === role);
|
|
45
|
+
if (match) return match;
|
|
46
|
+
}
|
|
47
|
+
if (!workers[0]) throw new Error('Enigma Weave has no workers');
|
|
48
|
+
return workers[0];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function assessWeaveTask(input, pipelineName = 'general') {
|
|
52
|
+
const text = `${pipelineName}\n${input}`.toLowerCase();
|
|
53
|
+
const signals = [
|
|
54
|
+
['security', /\b(security|vulnerab|exploit|attack|ssrf|xss|csrf|injection|secret|credential)\b/],
|
|
55
|
+
['identity', /\b(auth|authorization|permission|tenant|access control|token|session)\b/],
|
|
56
|
+
['money', /\b(billing|payment|credit|wallet|spend|refund|invoice|double-spend)\b/],
|
|
57
|
+
['privacy', /\b(privacy|pii|personal data|retention|redact|data leak)\b/],
|
|
58
|
+
['concurrency', /\b(race|concurren|idempot|replay|transaction|atomic)\b/],
|
|
59
|
+
['production-code', /\b(api|database|migration|deploy|production|code review|github|repo)\b/],
|
|
60
|
+
].filter(([, expression]) => expression.test(text)).map(([signal]) => signal);
|
|
61
|
+
const research = pipelineName === 'research' || /\b(research|paper|benchmark|evidence|study|compare)\b/.test(text);
|
|
62
|
+
const codeReview = pipelineName === 'code-review' || signals.length > 0;
|
|
63
|
+
const simple = input.length < 180 && pipelineName === 'general'
|
|
64
|
+
&& /^(name|list|say|give|what is|what's|define|translate|explain in one|one|short)\b/i.test(input.trim())
|
|
65
|
+
&& !/\b(review|audit|investigate|compare|debug|implement|patch|design)\b/i.test(input);
|
|
66
|
+
if (codeReview) return { kind: 'code-review', complexity: 'high', risk: 'high', riskSignals: signals };
|
|
67
|
+
if (research) return { kind: 'research', complexity: 'medium', risk: 'medium', riskSignals: signals };
|
|
68
|
+
if (simple || input.length < 180) return { kind: 'general', complexity: 'low', risk: 'low', riskSignals: signals };
|
|
69
|
+
return { kind: 'general', complexity: 'medium', risk: 'medium', riskSignals: signals };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function resolveWeaveLimits(input = {}) {
|
|
73
|
+
const policy = String(input.policy || 'balanced').toLowerCase();
|
|
74
|
+
const defaults = WEAVE_POLICIES[policy];
|
|
75
|
+
if (!defaults) throw new TypeError('policy must be lean, balanced, or strict');
|
|
76
|
+
const maxCalls = positiveInteger(input.maxCalls, defaults.maxCalls, 'maxCalls');
|
|
77
|
+
const maxLatencyMs = positiveInteger(input.maxLatencyMs, defaults.maxLatencyMs, 'maxLatencyMs');
|
|
78
|
+
const maxCredits = input.maxCredits === undefined ? null : nonNegativeNumber(input.maxCredits, 0, 'maxCredits');
|
|
79
|
+
return Object.freeze({ policy, maxCalls, maxLatencyMs, maxCredits });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function buildPlan(profile, policy, workers, pipeline) {
|
|
83
|
+
const directWorker = profile.kind === 'general'
|
|
84
|
+
? pickWorker(workers, ['finalizer', 'drafter', 'critic'])
|
|
85
|
+
: pickWorker(workers, ['drafter', 'finalizer', 'critic']);
|
|
86
|
+
const steps = [{ id: 'direct', role: 'drafter', worker: directWorker, access: [], instruction: pipeline.direct }];
|
|
87
|
+
if (policy === 'lean') return steps;
|
|
88
|
+
const critic = pickWorker(workers, ['critic', 'finalizer', 'drafter']);
|
|
89
|
+
if (policy === 'balanced') {
|
|
90
|
+
steps.push({
|
|
91
|
+
id: 'verify',
|
|
92
|
+
role: 'editor',
|
|
93
|
+
worker: critic,
|
|
94
|
+
access: ['direct'],
|
|
95
|
+
instruction: 'Audit the direct answer for critical omissions or unsupported claims, then return the complete corrected final answer.',
|
|
96
|
+
});
|
|
97
|
+
return steps;
|
|
98
|
+
}
|
|
99
|
+
const finalizer = pickWorker(workers, ['finalizer', 'drafter', 'critic']);
|
|
100
|
+
steps.push(
|
|
101
|
+
{ id: 'verify', role: 'critic', worker: critic, access: ['direct'], instruction: 'Adversarially audit the direct answer. Identify concrete errors, missing risks, and missing checks.' },
|
|
102
|
+
{ id: 'final', role: 'finalizer', worker: finalizer, access: ['direct', 'verify'], instruction: 'Return the complete final answer, incorporating only valid verifier findings and removing weak claims.' },
|
|
103
|
+
);
|
|
104
|
+
return steps;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function shouldEscalate(input, profile, answer, policy) {
|
|
108
|
+
if (policy === 'lean') return false;
|
|
109
|
+
if (policy === 'strict' || profile.risk === 'high' || profile.kind === 'research') return true;
|
|
110
|
+
if (profile.risk === 'medium' && /\b(review|audit|investigate|research|compare|verify|debug|design)\b/i.test(input)) return true;
|
|
111
|
+
const text = String(answer || '').trim();
|
|
112
|
+
return !text || /^\s*(i can't|i cannot|sorry|unable to)/i.test(text);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function retentionDisclosure(trace) {
|
|
116
|
+
const routes = trace.map((item) => ({
|
|
117
|
+
step: item.id,
|
|
118
|
+
worker: item.workerId,
|
|
119
|
+
model: item.model,
|
|
120
|
+
retentionPolicy: item.retentionPolicy,
|
|
121
|
+
retentionEvidence: item.retentionEvidence,
|
|
122
|
+
status: item.status,
|
|
123
|
+
}));
|
|
124
|
+
const policies = new Set(routes.map((route) => route.retentionPolicy || 'unknown'));
|
|
125
|
+
return {
|
|
126
|
+
policy: policies.has('unknown') ? 'unknown' : policies.size === 1 ? [...policies][0] : 'mixed',
|
|
127
|
+
routes,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function publicPlan(steps) {
|
|
132
|
+
return steps.map((step) => ({ id: step.id, role: step.role, workerId: step.worker.id, model: step.worker.model, access: step.access }));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function createEnigmaWeave(options = {}) {
|
|
136
|
+
if (typeof options.complete !== 'function') throw new TypeError('createEnigmaWeave requires complete(worker, request)');
|
|
137
|
+
const workers = (options.workers || []).map(normalizeWorker);
|
|
138
|
+
if (!workers.length) throw new TypeError('createEnigmaWeave requires workers');
|
|
139
|
+
|
|
140
|
+
return Object.freeze({
|
|
141
|
+
workers,
|
|
142
|
+
async run(input, runOptions = {}) {
|
|
143
|
+
if (typeof input !== 'string' || !input.trim()) throw new TypeError('input must be a non-empty string');
|
|
144
|
+
const pipeline = WEAVE_PIPELINES[runOptions.pipeline || 'general'];
|
|
145
|
+
if (!pipeline) throw new TypeError(`unknown pipeline: ${runOptions.pipeline}`);
|
|
146
|
+
const limits = resolveWeaveLimits(runOptions);
|
|
147
|
+
const profile = assessWeaveTask(input, pipeline.name);
|
|
148
|
+
const planned = buildPlan(profile, limits.policy, workers, pipeline);
|
|
149
|
+
const outputs = new Map();
|
|
150
|
+
const trace = [];
|
|
151
|
+
const startedAt = Date.now();
|
|
152
|
+
let estimatedCredits = 0;
|
|
153
|
+
let stopReason = null;
|
|
154
|
+
let limited = false;
|
|
155
|
+
runOptions.onEvent?.({ type: 'plan', pipeline: pipeline.name, policy: limits.policy, steps: publicPlan(planned), profile });
|
|
156
|
+
|
|
157
|
+
for (const step of planned) {
|
|
158
|
+
if (trace.length >= limits.maxCalls) { limited = true; stopReason = 'max-calls-reached'; break; }
|
|
159
|
+
if (Date.now() - startedAt >= limits.maxLatencyMs) { limited = true; stopReason = 'max-latency-reached'; break; }
|
|
160
|
+
if (limits.maxCredits !== null && estimatedCredits + step.worker.estimatedCredits > limits.maxCredits) {
|
|
161
|
+
limited = true;
|
|
162
|
+
stopReason = 'max-credits-reached';
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
const accessed = Object.fromEntries(step.access.map((id) => [id, outputs.get(id)]));
|
|
166
|
+
const messages = [
|
|
167
|
+
{ role: 'system', content: step.instruction },
|
|
168
|
+
{ role: 'user', content: input },
|
|
169
|
+
...(Object.keys(accessed).length ? [{ role: 'user', content: `Prior step outputs:\n${JSON.stringify(accessed)}` }] : []),
|
|
170
|
+
];
|
|
171
|
+
const stepStartedAt = Date.now();
|
|
172
|
+
runOptions.onEvent?.({ type: 'step.started', id: step.id, workerId: step.worker.id, model: step.worker.model });
|
|
173
|
+
let result;
|
|
174
|
+
try {
|
|
175
|
+
result = await options.complete(step.worker, { messages, input, step: { id: step.id, role: step.role }, signal: runOptions.signal });
|
|
176
|
+
if (!result || typeof result.answer !== 'string') throw new Error(`worker ${step.worker.id} returned no answer`);
|
|
177
|
+
outputs.set(step.id, result.answer);
|
|
178
|
+
estimatedCredits += step.worker.estimatedCredits;
|
|
179
|
+
trace.push({
|
|
180
|
+
id: step.id,
|
|
181
|
+
workerId: step.worker.id,
|
|
182
|
+
model: step.worker.model,
|
|
183
|
+
status: 'succeeded',
|
|
184
|
+
elapsedMs: Date.now() - stepStartedAt,
|
|
185
|
+
usage: result.usage || null,
|
|
186
|
+
spentCredits: Number(result.spentCredits || 0),
|
|
187
|
+
retentionPolicy: result.retentionPolicy || step.worker.retentionPolicy,
|
|
188
|
+
retentionEvidence: result.retentionEvidence || step.worker.retentionEvidence,
|
|
189
|
+
});
|
|
190
|
+
runOptions.onEvent?.({ type: 'step.done', id: step.id, workerId: step.worker.id });
|
|
191
|
+
} catch (error) {
|
|
192
|
+
trace.push({
|
|
193
|
+
id: step.id,
|
|
194
|
+
workerId: step.worker.id,
|
|
195
|
+
model: step.worker.model,
|
|
196
|
+
status: 'failed',
|
|
197
|
+
elapsedMs: Date.now() - stepStartedAt,
|
|
198
|
+
usage: null,
|
|
199
|
+
spentCredits: 0,
|
|
200
|
+
retentionPolicy: step.worker.retentionPolicy,
|
|
201
|
+
retentionEvidence: step.worker.retentionEvidence,
|
|
202
|
+
error: error.code || 'worker_failed',
|
|
203
|
+
});
|
|
204
|
+
runOptions.onEvent?.({ type: 'step.failed', id: step.id, workerId: step.worker.id, code: error.code || 'worker_failed' });
|
|
205
|
+
if (step.id === 'direct') throw error;
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
if (step.id === 'direct' && !shouldEscalate(input, profile, result.answer, limits.policy)) {
|
|
209
|
+
stopReason = 'direct-answer-accepted';
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const answer = outputs.get('final') || outputs.get('verify') || outputs.get('direct') || '';
|
|
215
|
+
const usage = trace.reduce((sum, item) => {
|
|
216
|
+
sum.promptTokens += Number(item.usage?.prompt_tokens || item.usage?.input_tokens || 0);
|
|
217
|
+
sum.completionTokens += Number(item.usage?.completion_tokens || item.usage?.output_tokens || 0);
|
|
218
|
+
sum.totalTokens += Number(item.usage?.total_tokens || 0);
|
|
219
|
+
sum.spentCredits += item.spentCredits;
|
|
220
|
+
return sum;
|
|
221
|
+
}, { promptTokens: 0, completionTokens: 0, totalTokens: 0, spentCredits: 0 });
|
|
222
|
+
const retention = retentionDisclosure(trace);
|
|
223
|
+
return {
|
|
224
|
+
pipeline: `${pipeline.name}:enigma-weave-${limits.policy}`,
|
|
225
|
+
answer,
|
|
226
|
+
trace,
|
|
227
|
+
metrics: { calls: trace.length, elapsedMs: Date.now() - startedAt, ...usage },
|
|
228
|
+
workflow: {
|
|
229
|
+
schema: 'enigma.weave_workflow.v1',
|
|
230
|
+
mode: 'adaptive',
|
|
231
|
+
policy: limits.policy,
|
|
232
|
+
router: 'local-policy',
|
|
233
|
+
profile,
|
|
234
|
+
plan: publicPlan(planned).slice(0, trace.length),
|
|
235
|
+
escalated: trace.length > 1,
|
|
236
|
+
budget: { limits, used: { calls: trace.length, estimatedCredits, latencyMs: Date.now() - startedAt }, limited, stopReason },
|
|
237
|
+
retention,
|
|
238
|
+
receipt: {
|
|
239
|
+
schema: 'enigma.weave_receipt.v1',
|
|
240
|
+
receiptId: randomUUID(),
|
|
241
|
+
stepCount: trace.length,
|
|
242
|
+
callCount: trace.length,
|
|
243
|
+
spentCredits: usage.spentCredits,
|
|
244
|
+
containsPromptOrOutput: false,
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export function fuseWeaveCandidates(candidates, options = {}) {
|
|
253
|
+
if (!Array.isArray(candidates) || !candidates.length) throw new TypeError('candidates must be a non-empty array');
|
|
254
|
+
const valid = candidates.filter((candidate) => candidate && typeof candidate.answer === 'string' && candidate.answer.trim());
|
|
255
|
+
if (!valid.length) throw new Error('no usable candidate answer');
|
|
256
|
+
const ranked = valid.map((candidate, index) => ({
|
|
257
|
+
...candidate,
|
|
258
|
+
index,
|
|
259
|
+
score: Number(candidate.score || 0) + Math.min(candidate.answer.length, 2_000) / 2_000,
|
|
260
|
+
})).sort((a, b) => b.score - a.score || a.index - b.index);
|
|
261
|
+
return options.returnAll ? ranked : ranked[0];
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export async function evaluateEnigmaWeave(weave, dataset, options = {}) {
|
|
265
|
+
if (!weave || typeof weave.run !== 'function') throw new TypeError('weave must expose run()');
|
|
266
|
+
if (!Array.isArray(dataset) || !dataset.length) throw new TypeError('dataset must be a non-empty array');
|
|
267
|
+
const cases = [];
|
|
268
|
+
for (const item of dataset) {
|
|
269
|
+
const id = String(item.id || `case-${cases.length + 1}`);
|
|
270
|
+
const startedAt = Date.now();
|
|
271
|
+
try {
|
|
272
|
+
const result = await weave.run(String(item.input || ''), options);
|
|
273
|
+
const passed = typeof item.validate === 'function' ? await item.validate(result.answer, result) : Boolean(result.answer);
|
|
274
|
+
cases.push({ id, passed: Boolean(passed), elapsedMs: Date.now() - startedAt, calls: result.metrics.calls, receiptId: result.workflow.receipt.receiptId });
|
|
275
|
+
} catch (error) {
|
|
276
|
+
cases.push({ id, passed: false, elapsedMs: Date.now() - startedAt, calls: 0, error: error.code || 'evaluation_failed' });
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return {
|
|
280
|
+
schema: 'enigma.weave_evaluation.v1',
|
|
281
|
+
total: cases.length,
|
|
282
|
+
passed: cases.filter((item) => item.passed).length,
|
|
283
|
+
failed: cases.filter((item) => !item.passed).length,
|
|
284
|
+
cases,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
@@ -209,6 +209,14 @@ function hashValue(value) {
|
|
|
209
209
|
return `${SHA256_PREFIX}${createHash('sha256').update(JSON.stringify(canonicalize(value))).digest('hex')}`;
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
+
function validateContractHash(contract, name = 'contract') {
|
|
213
|
+
const actual = requiredString(contract.contract_hash, `${name}.contract_hash`);
|
|
214
|
+
const { contract_hash: _contractHash, ...canonicalFields } = contract;
|
|
215
|
+
const expected = hashValue(canonicalFields);
|
|
216
|
+
if (actual !== expected) throw new TypeError(`${name}.contract_hash must match canonical contract fields`);
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
219
|
+
|
|
212
220
|
function contractId(prefix, body) {
|
|
213
221
|
return `${prefix}_${hashValue(body).slice(SHA256_PREFIX.length, SHA256_PREFIX.length + 24)}`;
|
|
214
222
|
}
|
|
@@ -316,7 +324,7 @@ function validateBase(contract, schema) {
|
|
|
316
324
|
throw new TypeError('readiness.external_blockers must match operator_evidence_refs');
|
|
317
325
|
}
|
|
318
326
|
});
|
|
319
|
-
|
|
327
|
+
validateContractHash(contract);
|
|
320
328
|
return true;
|
|
321
329
|
}
|
|
322
330
|
|
|
@@ -670,6 +678,19 @@ function lifecycleContractSource(input, aliases) {
|
|
|
670
678
|
function buildOrValidateLifecycleContract(phase, source) {
|
|
671
679
|
if (source === undefined || source === null) return null;
|
|
672
680
|
if (!isPlainObject(source)) throw new TypeError(`contracts.${phase} must be an object`);
|
|
681
|
+
if (source.schema !== undefined) {
|
|
682
|
+
const expectedSchema = {
|
|
683
|
+
account: HOSTED_CLOUD_USER_ACCOUNT_SCHEMA,
|
|
684
|
+
tenant: HOSTED_CLOUD_TENANT_SCHEMA,
|
|
685
|
+
vault: HOSTED_CLOUD_VAULT_SCHEMA,
|
|
686
|
+
api_key: HOSTED_CLOUD_API_KEY_SCHEMA,
|
|
687
|
+
billing: HOSTED_CLOUD_USAGE_BILLING_SCHEMA,
|
|
688
|
+
dashboard: HOSTED_CLOUD_DASHBOARD_SCHEMA,
|
|
689
|
+
backup: HOSTED_CLOUD_BACKUP_DRILL_SCHEMA,
|
|
690
|
+
incident_sla: HOSTED_CLOUD_INCIDENT_SLA_SCHEMA,
|
|
691
|
+
}[phase];
|
|
692
|
+
if (source.schema !== expectedSchema) throw new TypeError(`contracts.${phase}.schema must be ${expectedSchema}`);
|
|
693
|
+
}
|
|
673
694
|
switch (phase) {
|
|
674
695
|
case 'account':
|
|
675
696
|
if (source.schema === HOSTED_CLOUD_USER_ACCOUNT_SCHEMA) {
|
|
@@ -737,6 +758,29 @@ function lifecycleContractsFrom(input) {
|
|
|
737
758
|
};
|
|
738
759
|
}
|
|
739
760
|
|
|
761
|
+
function lifecycleTenantId(input, contracts) {
|
|
762
|
+
const explicitTenantId = input.tenant_id ?? input.tenantId;
|
|
763
|
+
const contractTenantIds = CUSTOMER_LIFECYCLE_CONTRACT_PHASES
|
|
764
|
+
.map((phase) => contracts[phase]?.tenant_id)
|
|
765
|
+
.filter((tenantId) => tenantId !== undefined && tenantId !== null);
|
|
766
|
+
const tenantId = requiredString(explicitTenantId ?? contractTenantIds[0], 'tenant_id');
|
|
767
|
+
for (const contractTenantId of contractTenantIds) {
|
|
768
|
+
if (requiredString(contractTenantId, 'contracts.tenant_id') !== tenantId) {
|
|
769
|
+
throw new TypeError('every tenant-bearing lifecycle contract must match packet tenant_id');
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
return tenantId;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
function validateLifecycleTenantIdentity(contracts, tenantId) {
|
|
776
|
+
for (const phase of CUSTOMER_LIFECYCLE_CONTRACT_PHASES) {
|
|
777
|
+
const contract = contracts[phase];
|
|
778
|
+
if (contract !== null && contract.tenant_id !== tenantId) {
|
|
779
|
+
throw new TypeError(`contracts.${phase}.tenant_id must match packet tenant_id`);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
740
784
|
function explicitLifecycleEvidenceRefs(input) {
|
|
741
785
|
const refs = input.required_evidence_refs ?? input.requiredEvidenceRefs ?? input.lifecycle_evidence_refs ?? input.lifecycleEvidenceRefs ?? input.evidence_refs ?? input.evidenceRefs ?? input.operator_evidence_refs ?? input.operatorEvidenceRefs;
|
|
742
786
|
if (refs === undefined || refs === null) return {};
|
|
@@ -1057,6 +1101,7 @@ export function buildCustomerLifecyclePacket(input = {}) {
|
|
|
1057
1101
|
if (!isPlainObject(input)) throw new TypeError('buildCustomerLifecyclePacket requires an options object');
|
|
1058
1102
|
assertNoForbiddenPayload(input, 'input');
|
|
1059
1103
|
const contracts = lifecycleContractsFrom(input);
|
|
1104
|
+
const tenantId = lifecycleTenantId(input, contracts);
|
|
1060
1105
|
const requiredEvidenceRefs = lifecycleEvidenceRefsFrom(input, contracts);
|
|
1061
1106
|
const operatorGoLiveRef = operatorGoLiveRefFromInput(input);
|
|
1062
1107
|
const missingSurfaceRefs = missingLifecycleSurfaceRefs(contracts);
|
|
@@ -1065,6 +1110,7 @@ export function buildCustomerLifecyclePacket(input = {}) {
|
|
|
1065
1110
|
schema: HOSTED_CLOUD_CUSTOMER_LIFECYCLE_PACKET_SCHEMA,
|
|
1066
1111
|
packet_id: stringOrDefault(input.packet_id ?? input.packetId, undefined),
|
|
1067
1112
|
generated_at: isoTimestamp(input.generated_at ?? input.generatedAt, 'generated_at'),
|
|
1113
|
+
tenant_id: tenantId,
|
|
1068
1114
|
contracts,
|
|
1069
1115
|
required_evidence_refs: requiredEvidenceRefs,
|
|
1070
1116
|
lifecycle_phases: lifecyclePhaseRows(requiredEvidenceRefs, contracts, operatorGoLiveRef),
|
|
@@ -1089,8 +1135,9 @@ export function validateCustomerLifecyclePacket(packet) {
|
|
|
1089
1135
|
if (packet.schema !== HOSTED_CLOUD_CUSTOMER_LIFECYCLE_PACKET_SCHEMA) throw new TypeError(`schema must be ${HOSTED_CLOUD_CUSTOMER_LIFECYCLE_PACKET_SCHEMA}`);
|
|
1090
1136
|
requiredString(packet.packet_id, 'packet_id');
|
|
1091
1137
|
isoTimestamp(packet.generated_at, 'generated_at');
|
|
1092
|
-
requiredString(packet.
|
|
1138
|
+
const tenantId = requiredString(packet.tenant_id, 'tenant_id');
|
|
1093
1139
|
validateCustomerLifecycleContracts(packet.contracts);
|
|
1140
|
+
validateLifecycleTenantIdentity(packet.contracts, tenantId);
|
|
1094
1141
|
if (!isPlainObject(packet.required_evidence_refs)) throw new TypeError('required_evidence_refs must be present');
|
|
1095
1142
|
const requiredEvidenceRefs = validateCustomerLifecycleEvidenceRefs({ ...packet.required_evidence_refs });
|
|
1096
1143
|
const operatorGoLiveRef = operatorGoLiveRefFromPacket(packet);
|
|
@@ -1104,6 +1151,7 @@ export function validateCustomerLifecyclePacket(packet) {
|
|
|
1104
1151
|
if (packet.hosted_cloud_sellable !== expectedReadiness.hosted_cloud_sellable) throw new TypeError('hosted_cloud_sellable must match readiness.hosted_cloud_sellable');
|
|
1105
1152
|
assertSameLifecycleObject(packet.dashboard, customerLifecycleDashboard(packet.contracts, requiredEvidenceRefs, expectedReadiness, operatorGoLiveRef), 'dashboard');
|
|
1106
1153
|
validateCustomerLifecycleSafetyGuarantees(packet.guarantees ?? packet.public_safety_guarantees);
|
|
1154
|
+
validateContractHash(packet, 'packet');
|
|
1107
1155
|
return true;
|
|
1108
1156
|
}
|
|
1109
1157
|
|
|
@@ -1179,6 +1227,9 @@ function buildOrValidateApiKeyLifecycleContract(slot, source, tenantId, subjectR
|
|
|
1179
1227
|
if (source === undefined || source === null) return null;
|
|
1180
1228
|
if (!isPlainObject(source)) throw new TypeError(`key_contracts.${slot} must be an object`);
|
|
1181
1229
|
const name = `key_contracts.${slot}`;
|
|
1230
|
+
if (source.schema !== undefined && source.schema !== HOSTED_CLOUD_API_KEY_SCHEMA) {
|
|
1231
|
+
throw new TypeError(`${name}.schema must be ${HOSTED_CLOUD_API_KEY_SCHEMA}`);
|
|
1232
|
+
}
|
|
1182
1233
|
if (source.schema === HOSTED_CLOUD_API_KEY_SCHEMA) {
|
|
1183
1234
|
validateApiKeyContract(source);
|
|
1184
1235
|
validateApiKeyLifecycleContractTenantSubject(source, tenantId, subjectRef, name);
|
|
@@ -1493,6 +1544,7 @@ export function validateApiKeyLifecyclePacket(packet) {
|
|
|
1493
1544
|
if (packet.customer_api_keys_live !== expectedReadiness.customer_api_keys_live) throw new TypeError('customer_api_keys_live must match readiness.customer_api_keys_live');
|
|
1494
1545
|
validateApiKeyLifecycleSafetyGuarantees(packet.guarantees ?? packet.public_safety_guarantees);
|
|
1495
1546
|
validateApiKeyLifecycleSafetyGuarantees(packet.public_safety_guarantees ?? packet.guarantees);
|
|
1547
|
+
validateContractHash(packet, 'packet');
|
|
1496
1548
|
return true;
|
|
1497
1549
|
}
|
|
1498
1550
|
|
|
@@ -1756,6 +1808,22 @@ function validateReadinessEvidenceRefs(packet) {
|
|
|
1756
1808
|
return requiredEvidenceRefs;
|
|
1757
1809
|
}
|
|
1758
1810
|
|
|
1811
|
+
function readinessPacketTenantId(input, customerLifecyclePacket, apiKeyLifecyclePacket) {
|
|
1812
|
+
const candidates = [
|
|
1813
|
+
input.tenant_id ?? input.tenantId,
|
|
1814
|
+
customerLifecyclePacket?.tenant_id,
|
|
1815
|
+
apiKeyLifecyclePacket?.tenant_id,
|
|
1816
|
+
].filter((tenantId) => tenantId !== undefined && tenantId !== null);
|
|
1817
|
+
if (candidates.length === 0) return null;
|
|
1818
|
+
const tenantId = requiredString(candidates[0], 'tenant_id');
|
|
1819
|
+
for (const candidate of candidates.slice(1)) {
|
|
1820
|
+
if (requiredString(candidate, 'embedded packet tenant_id') !== tenantId) {
|
|
1821
|
+
throw new TypeError('customer and API key lifecycle packets must share one tenant_id');
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
return tenantId;
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1759
1827
|
export function buildHostedCloudReadinessPacket(input = {}) {
|
|
1760
1828
|
if (!isPlainObject(input)) throw new TypeError('buildHostedCloudReadinessPacket requires an options object');
|
|
1761
1829
|
assertNoForbiddenPayload(input, 'input');
|
|
@@ -1773,6 +1841,7 @@ export function buildHostedCloudReadinessPacket(input = {}) {
|
|
|
1773
1841
|
lifecycleSellable = providedLifecyclePacket.hosted_cloud_sellable;
|
|
1774
1842
|
lifecycleEvidenceRefs = providedLifecyclePacket.required_evidence_refs;
|
|
1775
1843
|
lifecycleRef = {
|
|
1844
|
+
tenant_id: providedLifecyclePacket.tenant_id,
|
|
1776
1845
|
schema: providedLifecyclePacket.schema,
|
|
1777
1846
|
packet_id: providedLifecyclePacket.packet_id,
|
|
1778
1847
|
contract_hash: providedLifecyclePacket.contract_hash,
|
|
@@ -1785,12 +1854,14 @@ export function buildHostedCloudReadinessPacket(input = {}) {
|
|
|
1785
1854
|
validateApiKeyLifecyclePacket(providedApiKeyPacket);
|
|
1786
1855
|
apiKeyOperatorApprovalRef = providedApiKeyPacket.operator_approval_ref;
|
|
1787
1856
|
apiKeyLifecycleRef = {
|
|
1857
|
+
tenant_id: providedApiKeyPacket.tenant_id,
|
|
1788
1858
|
schema: providedApiKeyPacket.schema,
|
|
1789
1859
|
packet_id: providedApiKeyPacket.packet_id,
|
|
1790
1860
|
contract_hash: providedApiKeyPacket.contract_hash,
|
|
1791
1861
|
operator_approval_ref: providedApiKeyPacket.operator_approval_ref,
|
|
1792
1862
|
};
|
|
1793
1863
|
}
|
|
1864
|
+
const tenantId = readinessPacketTenantId(input, providedLifecyclePacket, providedApiKeyPacket);
|
|
1794
1865
|
const lifecycleExternalBlockers = (providedLifecyclePacket && isPlainObject(providedLifecyclePacket)) ? providedLifecyclePacket.external_blockers : [];
|
|
1795
1866
|
const apiKeyExternalBlockers = (providedApiKeyPacket && isPlainObject(providedApiKeyPacket)) ? providedApiKeyPacket.external_blockers : [];
|
|
1796
1867
|
const propagatedBlockers = [
|
|
@@ -1803,6 +1874,7 @@ export function buildHostedCloudReadinessPacket(input = {}) {
|
|
|
1803
1874
|
schema: HOSTED_CLOUD_READINESS_PACKET_SCHEMA,
|
|
1804
1875
|
readiness_id: stringOrDefault(input.readiness_id ?? input.readinessId, undefined),
|
|
1805
1876
|
generated_at: generatedAt,
|
|
1877
|
+
tenant_id: tenantId,
|
|
1806
1878
|
customer_lifecycle_packet: lifecycleRef,
|
|
1807
1879
|
api_key_lifecycle_packet: apiKeyLifecycleRef,
|
|
1808
1880
|
required_evidence_refs: requiredEvidenceRefs,
|
|
@@ -1828,21 +1900,24 @@ export function validateHostedCloudReadinessPacket(packet) {
|
|
|
1828
1900
|
if (packet.schema !== HOSTED_CLOUD_READINESS_PACKET_SCHEMA) throw new TypeError(`schema must be ${HOSTED_CLOUD_READINESS_PACKET_SCHEMA}`);
|
|
1829
1901
|
requiredString(packet.readiness_id, 'readiness_id');
|
|
1830
1902
|
isoTimestamp(packet.generated_at, 'generated_at');
|
|
1831
|
-
|
|
1903
|
+
validateContractHash(packet, 'packet');
|
|
1904
|
+
const tenantId = packet.tenant_id === null ? null : requiredString(packet.tenant_id, 'tenant_id');
|
|
1832
1905
|
if (!Object.prototype.hasOwnProperty.call(packet, 'customer_lifecycle_packet')) throw new TypeError('customer_lifecycle_packet must be present');
|
|
1833
1906
|
if (packet.customer_lifecycle_packet !== null) {
|
|
1834
1907
|
if (!isPlainObject(packet.customer_lifecycle_packet)) throw new TypeError('customer_lifecycle_packet must be an object or null');
|
|
1835
|
-
|
|
1908
|
+
if (packet.customer_lifecycle_packet.schema !== HOSTED_CLOUD_CUSTOMER_LIFECYCLE_PACKET_SCHEMA) throw new TypeError(`customer_lifecycle_packet.schema must be ${HOSTED_CLOUD_CUSTOMER_LIFECYCLE_PACKET_SCHEMA}`);
|
|
1836
1909
|
requiredString(packet.customer_lifecycle_packet.packet_id, 'customer_lifecycle_packet.packet_id');
|
|
1837
1910
|
requiredString(packet.customer_lifecycle_packet.contract_hash, 'customer_lifecycle_packet.contract_hash');
|
|
1838
1911
|
requiredBoolean(packet.customer_lifecycle_packet.hosted_cloud_sellable, 'customer_lifecycle_packet.hosted_cloud_sellable');
|
|
1912
|
+
if (requiredString(packet.customer_lifecycle_packet.tenant_id, 'customer_lifecycle_packet.tenant_id') !== tenantId) throw new TypeError('customer_lifecycle_packet.tenant_id must match readiness packet tenant_id');
|
|
1839
1913
|
}
|
|
1840
1914
|
if (!Object.prototype.hasOwnProperty.call(packet, 'api_key_lifecycle_packet')) throw new TypeError('api_key_lifecycle_packet must be present');
|
|
1841
1915
|
if (packet.api_key_lifecycle_packet !== null) {
|
|
1842
1916
|
if (!isPlainObject(packet.api_key_lifecycle_packet)) throw new TypeError('api_key_lifecycle_packet must be an object or null');
|
|
1843
|
-
|
|
1917
|
+
if (packet.api_key_lifecycle_packet.schema !== HOSTED_CLOUD_API_KEY_LIFECYCLE_PACKET_SCHEMA) throw new TypeError(`api_key_lifecycle_packet.schema must be ${HOSTED_CLOUD_API_KEY_LIFECYCLE_PACKET_SCHEMA}`);
|
|
1844
1918
|
requiredString(packet.api_key_lifecycle_packet.packet_id, 'api_key_lifecycle_packet.packet_id');
|
|
1845
1919
|
requiredString(packet.api_key_lifecycle_packet.contract_hash, 'api_key_lifecycle_packet.contract_hash');
|
|
1920
|
+
if (requiredString(packet.api_key_lifecycle_packet.tenant_id, 'api_key_lifecycle_packet.tenant_id') !== tenantId) throw new TypeError('api_key_lifecycle_packet.tenant_id must match readiness packet tenant_id');
|
|
1846
1921
|
if (!Object.prototype.hasOwnProperty.call(packet.api_key_lifecycle_packet, 'operator_approval_ref')) throw new TypeError('api_key_lifecycle_packet.operator_approval_ref must be present');
|
|
1847
1922
|
if (packet.api_key_lifecycle_packet.operator_approval_ref !== null) {
|
|
1848
1923
|
requirePublicSafeLifecycleRef(packet.api_key_lifecycle_packet.operator_approval_ref, 'api_key_lifecycle_packet.operator_approval_ref');
|