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,1796 @@
|
|
|
1
|
+
// Enigma Sovereign Agent SDK.
|
|
2
|
+
// Unified entrypoint for local-first encrypted vaults, client-side semantic vector search,
|
|
3
|
+
// GhostMesh P2P gossip, Sealed Sale (ZKCP) marketplace, and memory intelligence.
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import crypto from 'node:crypto';
|
|
7
|
+
import { EncryptedVectorStore } from '../../rag/src/index.js';
|
|
8
|
+
import { MerkleSet } from '../../core/src/index.js';
|
|
9
|
+
import { deriveDestination } from '../../mesh/src/index.js';
|
|
10
|
+
import {
|
|
11
|
+
createVault,
|
|
12
|
+
remember as vaultRemember,
|
|
13
|
+
recall as vaultRecall,
|
|
14
|
+
deriveKeysFromPassphrase,
|
|
15
|
+
DEFAULT_KDF_SPEC,
|
|
16
|
+
createMemoryReceipt,
|
|
17
|
+
verifyMemoryReceipt,
|
|
18
|
+
computeHidingCommitment,
|
|
19
|
+
verifyCommitmentOpening,
|
|
20
|
+
MEMORY_RECEIPT_SCHEMA,
|
|
21
|
+
createErasureReceipt,
|
|
22
|
+
verifyErasureReceipt,
|
|
23
|
+
createErasureAttestation,
|
|
24
|
+
verifyErasureAttestation,
|
|
25
|
+
secureZeroizeBuffer,
|
|
26
|
+
ERASURE_RECEIPT_SCHEMA,
|
|
27
|
+
ERASURE_ATTESTATION_SCHEMA,
|
|
28
|
+
} from '../../vault/src/index.js';
|
|
29
|
+
import { createContextCarrier } from '../../snapcompact/src/index.js';
|
|
30
|
+
import {
|
|
31
|
+
createBlindedMemoryGrant,
|
|
32
|
+
verifyBlindedMemoryGrant,
|
|
33
|
+
EnigmaFederationRouter,
|
|
34
|
+
BLINDED_GRANT_SCHEMA,
|
|
35
|
+
createFederatedQueryRequest,
|
|
36
|
+
verifyFederatedQueryRequest,
|
|
37
|
+
FEDERATED_QUERY_REQUEST_SCHEMA,
|
|
38
|
+
} from './federation/index.js';
|
|
39
|
+
import { EnigmaRagClient } from './rag-client.js';
|
|
40
|
+
import { EnigmaMeshClient } from './mesh-client.js';
|
|
41
|
+
import { EnigmaMarketClient } from './market-client.js';
|
|
42
|
+
import {
|
|
43
|
+
normalizeMemoryType,
|
|
44
|
+
inferMemoryType,
|
|
45
|
+
createTemporalMetadata,
|
|
46
|
+
parseTimestamp,
|
|
47
|
+
isTemporallyActive,
|
|
48
|
+
computeTemporalDecay,
|
|
49
|
+
ContradictionGraph,
|
|
50
|
+
detectContradictions,
|
|
51
|
+
MemoryDecisionEngine,
|
|
52
|
+
} from './intelligence/index.js';
|
|
53
|
+
|
|
54
|
+
export const AGENT_TURN_RECEIPT_REF_SCHEMA = 'enigma.agent_turn_receipt_ref.v1';
|
|
55
|
+
const VAULT_BUNDLE_AAD = Buffer.from('enigma.vault_bundle.v1', 'utf8');
|
|
56
|
+
const MAX_RECALL_OVERFETCH = 256;
|
|
57
|
+
|
|
58
|
+
function cloneStateValue(value) {
|
|
59
|
+
if (value === undefined) return undefined;
|
|
60
|
+
return structuredClone(value);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function decodeEnvelopeSalt(value) {
|
|
64
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
65
|
+
throw new Error('Vault envelope KDF salt is missing');
|
|
66
|
+
}
|
|
67
|
+
if (/^[0-9a-fA-F]+$/.test(value) && value.length % 2 === 0) {
|
|
68
|
+
return Buffer.from(value, 'hex');
|
|
69
|
+
}
|
|
70
|
+
return Buffer.from(value, 'base64');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function serializeMeshIdentity(identity) {
|
|
74
|
+
if (!identity) throw new Error('Mesh identity is required for vault persistence');
|
|
75
|
+
return {
|
|
76
|
+
schema: 'enigma.mesh_identity.v1',
|
|
77
|
+
scope: identity.scope,
|
|
78
|
+
publicKey: Buffer.from(identity.publicKey).toString('hex'),
|
|
79
|
+
secretKey: Buffer.from(identity.secretKey).toString('hex'),
|
|
80
|
+
encryptionPublicKey: Buffer.from(identity.encryptionPublicKey).toString('hex'),
|
|
81
|
+
encryptionSecretKey: Buffer.from(identity.encryptionSecretKey).toString('hex'),
|
|
82
|
+
destination: Buffer.from(identity.destination).toString('hex'),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function deserializeMeshIdentity(value, expectedScope) {
|
|
87
|
+
if (!value || value.schema !== 'enigma.mesh_identity.v1' || value.scope !== expectedScope) {
|
|
88
|
+
throw new Error('Decrypted vault payload has an invalid mesh identity');
|
|
89
|
+
}
|
|
90
|
+
for (const field of ['publicKey', 'secretKey', 'encryptionPublicKey', 'encryptionSecretKey', 'destination']) {
|
|
91
|
+
if (typeof value[field] !== 'string' || !/^[0-9a-fA-F]{64}$/.test(value[field])) {
|
|
92
|
+
throw new Error(`Decrypted vault payload has an invalid mesh identity ${field}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const publicKey = Buffer.from(value.publicKey, 'hex');
|
|
96
|
+
const secretKey = Buffer.from(value.secretKey, 'hex');
|
|
97
|
+
const encryptionPublicKey = Buffer.from(value.encryptionPublicKey, 'hex');
|
|
98
|
+
const encryptionSecretKey = Buffer.from(value.encryptionSecretKey, 'hex');
|
|
99
|
+
const destination = deriveDestination(publicKey, encryptionPublicKey, expectedScope);
|
|
100
|
+
if (!crypto.timingSafeEqual(destination, Buffer.from(value.destination, 'hex'))) {
|
|
101
|
+
throw new Error('Decrypted vault payload mesh destination does not match its public keys');
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
publicKey,
|
|
105
|
+
secretKey,
|
|
106
|
+
encryptionPublicKey,
|
|
107
|
+
encryptionSecretKey,
|
|
108
|
+
destination,
|
|
109
|
+
destinationHex: destination.toString('hex'),
|
|
110
|
+
scope: expectedScope,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
function sha256(value) {
|
|
116
|
+
return crypto.createHash('sha256').update(String(value), 'utf8').digest('hex');
|
|
117
|
+
}
|
|
118
|
+
function createBlindedValueCommitment(domain, value) {
|
|
119
|
+
const blindingFactor = crypto.randomBytes(32).toString('hex');
|
|
120
|
+
const digest = sha256(value);
|
|
121
|
+
const commitment = sha256([
|
|
122
|
+
'enigma.blinded_value_commitment.v1',
|
|
123
|
+
domain,
|
|
124
|
+
digest,
|
|
125
|
+
blindingFactor,
|
|
126
|
+
].join('\0'));
|
|
127
|
+
return {
|
|
128
|
+
public: Object.freeze({
|
|
129
|
+
scheme: 'sha256-blinded-v1',
|
|
130
|
+
domain,
|
|
131
|
+
value: commitment,
|
|
132
|
+
}),
|
|
133
|
+
opening: Object.freeze({
|
|
134
|
+
schema: 'enigma.blinded_value_commitment.opening.v1',
|
|
135
|
+
domain,
|
|
136
|
+
commitment,
|
|
137
|
+
blindingFactor,
|
|
138
|
+
digest,
|
|
139
|
+
}),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
function extractModelText(result) {
|
|
145
|
+
if (typeof result === 'string') return result;
|
|
146
|
+
if (!result || typeof result !== 'object') return null;
|
|
147
|
+
for (const key of ['text', 'outputText', 'output_text', 'content']) {
|
|
148
|
+
if (typeof result[key] === 'string') return result[key];
|
|
149
|
+
}
|
|
150
|
+
if (Array.isArray(result.content)) {
|
|
151
|
+
const text = result.content
|
|
152
|
+
.map(part => typeof part === 'string' ? part : part?.text)
|
|
153
|
+
.filter(part => typeof part === 'string')
|
|
154
|
+
.join('');
|
|
155
|
+
return text || null;
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function createReceiptRef(receipt) {
|
|
161
|
+
return {
|
|
162
|
+
schema: AGENT_TURN_RECEIPT_REF_SCHEMA,
|
|
163
|
+
receiptId: receipt.receipt_id,
|
|
164
|
+
memoryId: receipt.memory_id,
|
|
165
|
+
operation: receipt.operation,
|
|
166
|
+
commitment: receipt.commitment,
|
|
167
|
+
previousRoot: receipt.previous_root,
|
|
168
|
+
newRoot: receipt.new_root,
|
|
169
|
+
agentPubkey: receipt.agent_pubkey,
|
|
170
|
+
receiptDigest: sha256(`${receipt.receipt_id}:${receipt.signature}`),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
const CONTEXT_CARRIER_MODES = new Set(['text', 'bitmap-png', 'auto']);
|
|
174
|
+
|
|
175
|
+
function publicContextCarrierDecision(decision, requested, reasonOverride = null) {
|
|
176
|
+
const selected = decision.selected === 'bitmap-png' ? 'bitmap-png' : 'text';
|
|
177
|
+
const safe = {
|
|
178
|
+
requested,
|
|
179
|
+
selected,
|
|
180
|
+
eligible: decision.eligible === true,
|
|
181
|
+
reason: reasonOverride || String(decision.reason || 'text-fallback'),
|
|
182
|
+
profile: typeof decision.profile === 'string' ? decision.profile : null,
|
|
183
|
+
preparationStatus: selected === 'bitmap-png' ? 'prepared' : 'text-fallback',
|
|
184
|
+
deliveryConfirmed: false,
|
|
185
|
+
};
|
|
186
|
+
if (Number.isInteger(decision.expectedReuse)) safe.expectedReuse = decision.expectedReuse;
|
|
187
|
+
if (Number.isInteger(decision.frameCount)) safe.frameCount = decision.frameCount;
|
|
188
|
+
if (decision.estimates && typeof decision.estimates === 'object') {
|
|
189
|
+
safe.estimates = {
|
|
190
|
+
textTokens: decision.estimates.textTokens,
|
|
191
|
+
imageTokens: decision.estimates.imageTokens,
|
|
192
|
+
textUsd: decision.estimates.textUsd,
|
|
193
|
+
imageInputUsd: decision.estimates.imageInputUsd,
|
|
194
|
+
oneTimeDecodeUsd: decision.estimates.oneTimeDecodeUsd,
|
|
195
|
+
imageUsd: decision.estimates.imageUsd,
|
|
196
|
+
assumptions: decision.estimates.assumptions,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
return Object.freeze(safe);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function createCanonicalTextCarrier(source, provider, model) {
|
|
203
|
+
return createContextCarrier({
|
|
204
|
+
source,
|
|
205
|
+
provider: String(provider || ''),
|
|
206
|
+
model: String(model || ''),
|
|
207
|
+
vision: false,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function prepareAgentContextCarrier(source, options) {
|
|
212
|
+
const requested = options.mode === undefined ? 'text' : String(options.mode);
|
|
213
|
+
if (!CONTEXT_CARRIER_MODES.has(requested)) {
|
|
214
|
+
throw new TypeError('contextCarrier.mode must be "text", "bitmap-png", or "auto"');
|
|
215
|
+
}
|
|
216
|
+
const provider = String(options.provider || '');
|
|
217
|
+
const model = String(options.model || '');
|
|
218
|
+
if (requested === 'text') {
|
|
219
|
+
return { carrier: createCanonicalTextCarrier(source, provider, model), requested, reasonOverride: null };
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const limits = options.limits ?? options.renderLimits;
|
|
223
|
+
if (
|
|
224
|
+
limits
|
|
225
|
+
&& Number.isInteger(limits.maxCharacters)
|
|
226
|
+
&& limits.maxCharacters < source.length
|
|
227
|
+
) {
|
|
228
|
+
return {
|
|
229
|
+
carrier: createCanonicalTextCarrier(source, provider, model),
|
|
230
|
+
requested,
|
|
231
|
+
reasonOverride: 'render-limit-would-truncate-canonical-context',
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
return {
|
|
237
|
+
carrier: createContextCarrier({
|
|
238
|
+
source,
|
|
239
|
+
provider,
|
|
240
|
+
model,
|
|
241
|
+
vision: options.vision === true,
|
|
242
|
+
costs: options.costs ?? options.costAssumptions,
|
|
243
|
+
expectedReuse: options.expectedReuse,
|
|
244
|
+
tokenEstimates: options.tokenEstimates,
|
|
245
|
+
limits,
|
|
246
|
+
tabWidth: options.tabWidth,
|
|
247
|
+
collapseRepeatedLines: options.collapseRepeatedLines,
|
|
248
|
+
repetition: options.repetition,
|
|
249
|
+
}),
|
|
250
|
+
requested,
|
|
251
|
+
reasonOverride: null,
|
|
252
|
+
};
|
|
253
|
+
} catch {
|
|
254
|
+
return {
|
|
255
|
+
carrier: createCanonicalTextCarrier(source, provider, model),
|
|
256
|
+
requested,
|
|
257
|
+
reasonOverride: 'carrier-preparation-failed-closed',
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function cloneEphemeralProviderBlock(block) {
|
|
263
|
+
if (block?.source?.type === 'base64') {
|
|
264
|
+
return {
|
|
265
|
+
type: 'image',
|
|
266
|
+
source: {
|
|
267
|
+
type: 'base64',
|
|
268
|
+
media_type: 'image/png',
|
|
269
|
+
data: block.source.data,
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
if (block?.inlineData?.mimeType === 'image/png') {
|
|
274
|
+
return { inlineData: { mimeType: 'image/png', data: block.inlineData.data } };
|
|
275
|
+
}
|
|
276
|
+
return {
|
|
277
|
+
type: 'input_image',
|
|
278
|
+
image_url: block?.image_url || '',
|
|
279
|
+
detail: block?.detail || 'original',
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function clearEphemeralProviderBlocks(blocks) {
|
|
284
|
+
for (const block of blocks) {
|
|
285
|
+
if (block?.source?.type === 'base64') block.source.data = '';
|
|
286
|
+
if (block?.inlineData?.mimeType === 'image/png') block.inlineData.data = '';
|
|
287
|
+
if (block?.type === 'input_image') block.image_url = '';
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function zeroizeContextCarrier(carrier, invocationCarrier) {
|
|
292
|
+
if (invocationCarrier) {
|
|
293
|
+
invocationCarrier.textFallback = '';
|
|
294
|
+
clearEphemeralProviderBlocks(invocationCarrier.blocks);
|
|
295
|
+
}
|
|
296
|
+
for (const page of carrier?.pages || []) {
|
|
297
|
+
if (Buffer.isBuffer(page.png)) page.png.fill(0);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
function sanitizeCarrierEchoes(value, invocationCarrier, disclosure, seen = new WeakMap()) {
|
|
301
|
+
if (typeof value === 'string' && (
|
|
302
|
+
value === invocationCarrier?.descriptor?.contextDigest
|
|
303
|
+
|| value === invocationCarrier?.descriptor?.payloadDigest
|
|
304
|
+
)) return undefined;
|
|
305
|
+
if (!value || typeof value !== 'object') return value;
|
|
306
|
+
if (Buffer.isBuffer(value) || value instanceof Uint8Array) return value;
|
|
307
|
+
if (seen.has(value)) return seen.get(value);
|
|
308
|
+
if (
|
|
309
|
+
value === invocationCarrier
|
|
310
|
+
|| (
|
|
311
|
+
typeof value.selected === 'string'
|
|
312
|
+
&& typeof value.textFallback === 'string'
|
|
313
|
+
&& Array.isArray(value.blocks)
|
|
314
|
+
&& value.descriptor
|
|
315
|
+
&& value.decision
|
|
316
|
+
)
|
|
317
|
+
) {
|
|
318
|
+
return disclosure;
|
|
319
|
+
}
|
|
320
|
+
if (
|
|
321
|
+
value?.source?.type === 'base64'
|
|
322
|
+
|| value?.inlineData?.mimeType === 'image/png'
|
|
323
|
+
|| value?.type === 'input_image'
|
|
324
|
+
) {
|
|
325
|
+
return Object.freeze({ omittedCarrierImage: true });
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const entries = Array.isArray(value)
|
|
329
|
+
? value.map((entry, index) => [index, entry])
|
|
330
|
+
: Object.entries(value);
|
|
331
|
+
let replacement = value;
|
|
332
|
+
seen.set(value, value);
|
|
333
|
+
for (const [entryKey, entryValue] of entries) {
|
|
334
|
+
const sanitized = sanitizeCarrierEchoes(entryValue, invocationCarrier, disclosure, seen);
|
|
335
|
+
if (sanitized === entryValue) continue;
|
|
336
|
+
if (replacement === value) {
|
|
337
|
+
replacement = Array.isArray(value) ? [...value] : { ...value };
|
|
338
|
+
seen.set(value, replacement);
|
|
339
|
+
}
|
|
340
|
+
replacement[entryKey] = sanitized;
|
|
341
|
+
}
|
|
342
|
+
return replacement;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
function publicContextCarrier(carrier, requested, reasonOverride) {
|
|
347
|
+
const descriptor = carrier.descriptor;
|
|
348
|
+
const blindingFactor = crypto.randomBytes(32).toString('hex');
|
|
349
|
+
const commitment = sha256([
|
|
350
|
+
'enigma.context_carrier.hiding.v1',
|
|
351
|
+
descriptor.version,
|
|
352
|
+
descriptor.codec,
|
|
353
|
+
descriptor.profile || '',
|
|
354
|
+
descriptor.contextDigest,
|
|
355
|
+
descriptor.payloadDigest,
|
|
356
|
+
blindingFactor,
|
|
357
|
+
].join('\0'));
|
|
358
|
+
return Object.freeze({
|
|
359
|
+
disclosure: Object.freeze({
|
|
360
|
+
descriptor: Object.freeze({
|
|
361
|
+
codec: descriptor.codec,
|
|
362
|
+
version: descriptor.version,
|
|
363
|
+
profile: descriptor.profile,
|
|
364
|
+
commitment: Object.freeze({
|
|
365
|
+
scheme: 'sha256-blinded-v1',
|
|
366
|
+
value: commitment,
|
|
367
|
+
}),
|
|
368
|
+
dimensions: descriptor.dimensions,
|
|
369
|
+
bytes: descriptor.bytes,
|
|
370
|
+
counts: descriptor.counts,
|
|
371
|
+
}),
|
|
372
|
+
decision: publicContextCarrierDecision(carrier.decision, requested, reasonOverride),
|
|
373
|
+
}),
|
|
374
|
+
opening: Object.freeze({
|
|
375
|
+
schema: 'enigma.context_carrier.opening.v1',
|
|
376
|
+
commitment,
|
|
377
|
+
blindingFactor,
|
|
378
|
+
contextDigest: descriptor.contextDigest,
|
|
379
|
+
payloadDigest: descriptor.payloadDigest,
|
|
380
|
+
}),
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
export class Enigma {
|
|
386
|
+
/**
|
|
387
|
+
* @param {Object} [options]
|
|
388
|
+
* @param {string} [options.scope='enigma.global'] - Memory namespace scope
|
|
389
|
+
* @param {string} [options.agentId] - Unique agent identifier
|
|
390
|
+
* @param {Buffer|string} [options.vaultKey] - 32-byte master encryption key
|
|
391
|
+
* @param {string} [options.passphrase] - Passphrase for automatic PBKDF2/scrypt key derivation
|
|
392
|
+
* @param {string} [options.storagePath] - Optional local file path for disk-backed encrypted persistence
|
|
393
|
+
* @param {string} [options.apiBase] - Local daemon URL
|
|
394
|
+
* @param {string} [options.authToken] - Local IPC authorization token
|
|
395
|
+
* @param {boolean} [options.ownRagClient=false] - Dispose a supplied RAG client with this Enigma instance
|
|
396
|
+
* @param {boolean} [options.ownMeshClient=false] - Stop a supplied mesh client with this Enigma instance
|
|
397
|
+
* @param {boolean} [options.ownMarketClient=false] - Close a supplied market client with this Enigma instance
|
|
398
|
+
*/
|
|
399
|
+
constructor(options = {}) {
|
|
400
|
+
this.scope = options.scope || 'enigma.global';
|
|
401
|
+
this.agentId = options.agentId || `agent_${crypto.randomBytes(4).toString('hex')}`;
|
|
402
|
+
this.storagePath = options.storagePath ? path.resolve(options.storagePath) : null;
|
|
403
|
+
this.passphrase = options.passphrase ? String(options.passphrase) : null;
|
|
404
|
+
this.apiBase = options.apiBase || 'http://127.0.0.1:8787';
|
|
405
|
+
this.authToken = options.authToken || null;
|
|
406
|
+
this._kdfSalt = crypto.createHash('sha256')
|
|
407
|
+
.update(`enigma.vault.salt.${this.scope}`)
|
|
408
|
+
.digest()
|
|
409
|
+
.subarray(0, 16);
|
|
410
|
+
this._meshIdentitySupplied = Boolean(options.identity || options.meshClient);
|
|
411
|
+
|
|
412
|
+
if (this.storagePath && !options.vaultKey && !this.passphrase) {
|
|
413
|
+
throw new Error('Explicit passphrase or vaultKey is required when persistent storagePath is configured.');
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (options.vaultKey) {
|
|
417
|
+
this.vaultKey = Buffer.isBuffer(options.vaultKey) ? options.vaultKey : Buffer.from(options.vaultKey, 'hex');
|
|
418
|
+
} else if (this.passphrase) {
|
|
419
|
+
const derived = deriveKeysFromPassphrase(this.passphrase, this._kdfSalt, DEFAULT_KDF_SPEC);
|
|
420
|
+
this.vaultKey = derived.vaultKey;
|
|
421
|
+
} else {
|
|
422
|
+
// Ephemeral in-memory session key
|
|
423
|
+
this.vaultKey = crypto.randomBytes(32);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
this.rag = options.ragClient || new EnigmaRagClient();
|
|
427
|
+
this.mesh = options.meshClient || new EnigmaMeshClient({ scope: this.scope, identity: options.identity, nodeId: this.agentId });
|
|
428
|
+
this.market = options.marketClient || new EnigmaMarketClient({ apiBase: this.apiBase, authToken: this.authToken });
|
|
429
|
+
this._ownsRagClient = !options.ragClient || options.ownRagClient === true;
|
|
430
|
+
this._ownsMeshClient = !options.meshClient || options.ownMeshClient === true;
|
|
431
|
+
this._ownsMarketClient = !options.marketClient || options.ownMarketClient === true;
|
|
432
|
+
this._disposePromise = null;
|
|
433
|
+
// Intelligence Engine components
|
|
434
|
+
this.contradictionGraph = new ContradictionGraph();
|
|
435
|
+
this.decisionEngine = new MemoryDecisionEngine();
|
|
436
|
+
this.federation = new EnigmaFederationRouter({
|
|
437
|
+
enigma: this,
|
|
438
|
+
onStateMutation: async () => {
|
|
439
|
+
if (this.storagePath) await this.save();
|
|
440
|
+
},
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
/** @type {Map<string, { id: string, text: string, scope: string, type: string, importance: number, createdAt: number, temporal: Object, metadata: Object }>} */
|
|
444
|
+
this._memories = new Map();
|
|
445
|
+
/** Receipt/context hiding-commitment openings. Encrypted at rest and never projected through RAG/recall/federation metadata. */
|
|
446
|
+
this._privateOpenings = new Map();
|
|
447
|
+
/** @type {string[]} */
|
|
448
|
+
this._activeCommitments = [];
|
|
449
|
+
/** @type {Set<string>} */
|
|
450
|
+
this._activeCommitmentSet = new Set();
|
|
451
|
+
this._currentRoot = new MerkleSet().root();
|
|
452
|
+
/** @type {number} */
|
|
453
|
+
this._sessionEpoch = 0;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
_snapshotMutableState() {
|
|
457
|
+
const store = this.rag?.store;
|
|
458
|
+
const vectorState = store?.documents instanceof Map && store?.vectors instanceof Map
|
|
459
|
+
? {
|
|
460
|
+
epoch: store.epoch,
|
|
461
|
+
documents: new Map(Array.from(store.documents, ([id, document]) => [id, cloneStateValue(document)])),
|
|
462
|
+
vectors: new Map(Array.from(store.vectors, ([id, vector]) => [
|
|
463
|
+
id,
|
|
464
|
+
ArrayBuffer.isView(vector) ? vector.slice() : cloneStateValue(vector),
|
|
465
|
+
])),
|
|
466
|
+
}
|
|
467
|
+
: null;
|
|
468
|
+
return {
|
|
469
|
+
epoch: this._sessionEpoch,
|
|
470
|
+
memories: new Map(Array.from(this._memories, ([id, memory]) => [id, cloneStateValue(memory)])),
|
|
471
|
+
privateOpenings: new Map(Array.from(this._privateOpenings, ([id, opening]) => [id, cloneStateValue(opening)])),
|
|
472
|
+
activeCommitments: [...this._activeCommitments],
|
|
473
|
+
currentRoot: this._currentRoot,
|
|
474
|
+
graph: this.contradictionGraph.exportState(),
|
|
475
|
+
vectorState,
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
_restoreMutableState(snapshot) {
|
|
480
|
+
if (!snapshot || this._sessionEpoch !== snapshot.epoch) return false;
|
|
481
|
+
this._memories = new Map(Array.from(snapshot.memories, ([id, memory]) => [id, cloneStateValue(memory)]));
|
|
482
|
+
this._privateOpenings = new Map(Array.from(snapshot.privateOpenings || [], ([id, opening]) => [id, cloneStateValue(opening)]));
|
|
483
|
+
this._activeCommitments = [...snapshot.activeCommitments];
|
|
484
|
+
this._activeCommitmentSet = new Set(snapshot.activeCommitments);
|
|
485
|
+
this._currentRoot = snapshot.currentRoot;
|
|
486
|
+
const graph = new ContradictionGraph();
|
|
487
|
+
graph.importState(snapshot.graph);
|
|
488
|
+
this.contradictionGraph = graph;
|
|
489
|
+
|
|
490
|
+
if (snapshot.vectorState) {
|
|
491
|
+
const store = this.rag?.store;
|
|
492
|
+
if (!(store?.documents instanceof Map) || !(store?.vectors instanceof Map) || !store.bm25) {
|
|
493
|
+
throw new Error('Cannot restore vector state: incompatible RAG store');
|
|
494
|
+
}
|
|
495
|
+
store.documents.clear();
|
|
496
|
+
store.vectors.clear();
|
|
497
|
+
store.bm25.clear();
|
|
498
|
+
for (const [id, document] of snapshot.vectorState.documents) {
|
|
499
|
+
const restored = cloneStateValue(document);
|
|
500
|
+
store.documents.set(id, restored);
|
|
501
|
+
store.bm25.add(id, restored.text, restored.scope);
|
|
502
|
+
}
|
|
503
|
+
for (const [id, vector] of snapshot.vectorState.vectors) {
|
|
504
|
+
store.vectors.set(id, ArrayBuffer.isView(vector) ? vector.slice() : cloneStateValue(vector));
|
|
505
|
+
}
|
|
506
|
+
store.epoch = snapshot.vectorState.epoch;
|
|
507
|
+
}
|
|
508
|
+
return true;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* 3-Line Quickstart Factory: Connects an autonomous agent to Enigma in a single call.
|
|
513
|
+
* Automatically rehydrates encrypted memories from disk if storagePath is provided.
|
|
514
|
+
* @param {Object} [options]
|
|
515
|
+
* @returns {Promise<Enigma>} Connected Enigma instance
|
|
516
|
+
*/
|
|
517
|
+
static async connect(options = {}) {
|
|
518
|
+
const instance = new Enigma(options);
|
|
519
|
+
try {
|
|
520
|
+
if (instance.storagePath && fs.existsSync(instance.storagePath)) {
|
|
521
|
+
await instance.load();
|
|
522
|
+
}
|
|
523
|
+
if (options.autoStartMesh) {
|
|
524
|
+
await instance.mesh.start();
|
|
525
|
+
}
|
|
526
|
+
return instance;
|
|
527
|
+
} catch (error) {
|
|
528
|
+
try {
|
|
529
|
+
await instance.dispose();
|
|
530
|
+
} catch {
|
|
531
|
+
// Preserve the connection failure that triggered cleanup.
|
|
532
|
+
}
|
|
533
|
+
throw error;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Releases runtime resources owned by this SDK instance without changing
|
|
539
|
+
* in-memory state or persistent storage. Repeated calls share one cleanup.
|
|
540
|
+
* Supplied clients remain caller-owned unless their own*Client option is true.
|
|
541
|
+
* @returns {Promise<void>}
|
|
542
|
+
*/
|
|
543
|
+
async dispose() {
|
|
544
|
+
if (this._disposePromise) return this._disposePromise;
|
|
545
|
+
this._disposePromise = (async () => {
|
|
546
|
+
const cleanup = [];
|
|
547
|
+
if (this._ownsRagClient && typeof this.rag?.dispose === 'function') {
|
|
548
|
+
cleanup.push(Promise.resolve().then(() => this.rag.dispose()));
|
|
549
|
+
}
|
|
550
|
+
if (this._ownsMeshClient && typeof this.mesh?.stop === 'function') {
|
|
551
|
+
cleanup.push(Promise.resolve().then(() => this.mesh.stop()));
|
|
552
|
+
}
|
|
553
|
+
if (this._ownsMarketClient) {
|
|
554
|
+
if (typeof this.market?.dispose === 'function') {
|
|
555
|
+
cleanup.push(Promise.resolve().then(() => this.market.dispose()));
|
|
556
|
+
} else if (typeof this.market?.close === 'function') {
|
|
557
|
+
cleanup.push(Promise.resolve().then(() => this.market.close()));
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
const results = await Promise.allSettled(cleanup);
|
|
561
|
+
const failures = results
|
|
562
|
+
.filter(result => result.status === 'rejected')
|
|
563
|
+
.map(result => result.reason);
|
|
564
|
+
if (failures.length === 1) throw failures[0];
|
|
565
|
+
if (failures.length > 1) {
|
|
566
|
+
throw new AggregateError(failures, 'Failed to dispose Enigma runtime resources');
|
|
567
|
+
}
|
|
568
|
+
})();
|
|
569
|
+
return this._disposePromise;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Standalone offline verifier: Verifies cryptographic validity of a signed MemoryReceipt.
|
|
574
|
+
* @param {Object} receipt
|
|
575
|
+
* @param {Object} [options]
|
|
576
|
+
* @returns {Object}
|
|
577
|
+
*/
|
|
578
|
+
static verifyReceipt(receipt, options = {}) {
|
|
579
|
+
return verifyMemoryReceipt(receipt, options);
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Standalone offline verifier: Verifies cryptographic validity of an ErasureAttestation receipt.
|
|
583
|
+
* @param {Object} receipt
|
|
584
|
+
* @param {Object} [options]
|
|
585
|
+
* @returns {Object}
|
|
586
|
+
*/
|
|
587
|
+
static verifyErasure(receipt, options = {}) {
|
|
588
|
+
return verifyErasureReceipt(receipt, options);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
static verifyAttestation(receipt, options = {}) {
|
|
592
|
+
return verifyErasureReceipt(receipt, options);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Standalone offline verifier: Verifies cryptographic validity of a BlindedMemoryGrant.
|
|
597
|
+
* @param {Object} grant
|
|
598
|
+
* @param {Object} [options]
|
|
599
|
+
* @returns {Object}
|
|
600
|
+
*/
|
|
601
|
+
static verifyCapability(grant, options = {}) {
|
|
602
|
+
return verifyBlindedMemoryGrant(grant, options);
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* Generates a cryptographically signed MemoryReceipt for a stored memory atom
|
|
606
|
+
* with a cryptographically hiding commitment and Merkle state transition proof.
|
|
607
|
+
* @param {string} memoryId
|
|
608
|
+
* @param {Object} [options]
|
|
609
|
+
* @param {'append'|'update'|'revoke'} [options.operation='append']
|
|
610
|
+
* @param {string|Buffer} [options.blindingFactor] - Optional custom blinding factor
|
|
611
|
+
* @returns {Promise<Object>} Signed MemoryReceipt
|
|
612
|
+
*/
|
|
613
|
+
async createReceipt(memoryId, options = {}) {
|
|
614
|
+
const opEpoch = this._sessionEpoch;
|
|
615
|
+
if (Buffer.isBuffer(this.vaultKey) && this.vaultKey.every(b => b === 0)) {
|
|
616
|
+
throw new Error('Cannot create receipt: session has been reset or zeroized');
|
|
617
|
+
}
|
|
618
|
+
const meshIdentity = this.mesh.identity;
|
|
619
|
+
if (!meshIdentity) throw new Error('Mesh identity is required to sign memory receipts');
|
|
620
|
+
const mem = this._memories.get(memoryId);
|
|
621
|
+
if (!mem) throw new Error(`Memory atom ${memoryId} not found`);
|
|
622
|
+
|
|
623
|
+
const operation = options.operation ?? 'append';
|
|
624
|
+
if (!['append', 'update', 'revoke'].includes(operation)) {
|
|
625
|
+
throw new Error(`Unsupported memory receipt operation "${operation}"`);
|
|
626
|
+
}
|
|
627
|
+
const transaction = this._snapshotMutableState();
|
|
628
|
+
try {
|
|
629
|
+
let previousRoot = this._currentRoot;
|
|
630
|
+
let priorCommitments = [];
|
|
631
|
+
let priorCommitment;
|
|
632
|
+
|
|
633
|
+
if (operation === 'append') {
|
|
634
|
+
if (this._activeCommitmentSet.has(mem.commitment)) {
|
|
635
|
+
priorCommitments = this._activeCommitments.filter(c => c !== mem.commitment);
|
|
636
|
+
previousRoot = new MerkleSet(priorCommitments).root();
|
|
637
|
+
} else {
|
|
638
|
+
priorCommitments = [...this._activeCommitments];
|
|
639
|
+
previousRoot = this._currentRoot;
|
|
640
|
+
}
|
|
641
|
+
} else if (operation === 'revoke') {
|
|
642
|
+
if (mem.revoked || !this._activeCommitmentSet.has(mem.commitment)) {
|
|
643
|
+
throw new Error(`Cannot revoke inactive or already revoked memory atom "${memoryId}" (commitment ${mem.commitment}).`);
|
|
644
|
+
}
|
|
645
|
+
priorCommitments = [...this._activeCommitments];
|
|
646
|
+
previousRoot = this._currentRoot;
|
|
647
|
+
} else {
|
|
648
|
+
priorCommitment = options.priorCommitment || mem.commitment;
|
|
649
|
+
if (!this._activeCommitmentSet.has(priorCommitment)) {
|
|
650
|
+
throw new Error(`Cannot update inactive or already revoked target commitment "${priorCommitment}".`);
|
|
651
|
+
}
|
|
652
|
+
priorCommitments = [...this._activeCommitments];
|
|
653
|
+
previousRoot = this._currentRoot;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
const { receipt, opening } = createMemoryReceipt({
|
|
657
|
+
memoryId,
|
|
658
|
+
operation,
|
|
659
|
+
plaintext: mem.text,
|
|
660
|
+
blindingFactor: options.blindingFactor ?? mem.blindingFactor,
|
|
661
|
+
previousRoot,
|
|
662
|
+
priorCommitments,
|
|
663
|
+
priorCommitment,
|
|
664
|
+
signingSecretKey: meshIdentity.secretKey,
|
|
665
|
+
signingPublicKey: meshIdentity.publicKey,
|
|
666
|
+
metadata: {
|
|
667
|
+
...options.metadata,
|
|
668
|
+
scope: mem.scope,
|
|
669
|
+
type: mem.type,
|
|
670
|
+
sessionEpoch: mem.sessionEpoch,
|
|
671
|
+
},
|
|
672
|
+
});
|
|
673
|
+
if (operation === 'update' && receipt.commitment === priorCommitment) {
|
|
674
|
+
throw new Error('Memory update must produce a fresh commitment');
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
mem.commitment = receipt.commitment;
|
|
678
|
+
mem.blindingFactor = opening.blindingFactor;
|
|
679
|
+
this._currentRoot = receipt.new_root;
|
|
680
|
+
|
|
681
|
+
if (operation === 'append') {
|
|
682
|
+
if (!this._activeCommitmentSet.has(receipt.commitment)) {
|
|
683
|
+
this._activeCommitments.push(receipt.commitment);
|
|
684
|
+
this._activeCommitmentSet.add(receipt.commitment);
|
|
685
|
+
}
|
|
686
|
+
} else if (operation === 'revoke') {
|
|
687
|
+
this._activeCommitments = this._activeCommitments.filter(c => c !== receipt.commitment);
|
|
688
|
+
this._activeCommitmentSet.delete(receipt.commitment);
|
|
689
|
+
mem.revoked = true;
|
|
690
|
+
this.rag.store.delete(memoryId);
|
|
691
|
+
} else {
|
|
692
|
+
this._activeCommitments = this._activeCommitments
|
|
693
|
+
.filter(c => c !== priorCommitment)
|
|
694
|
+
.concat(receipt.commitment);
|
|
695
|
+
this._activeCommitmentSet.delete(priorCommitment);
|
|
696
|
+
this._activeCommitmentSet.add(receipt.commitment);
|
|
697
|
+
}
|
|
698
|
+
if (operation !== 'revoke') {
|
|
699
|
+
this.rag.updateMetadata(memoryId, {
|
|
700
|
+
...mem.metadata,
|
|
701
|
+
scope: mem.scope,
|
|
702
|
+
type: mem.type,
|
|
703
|
+
importance: mem.importance,
|
|
704
|
+
createdAt: mem.createdAt,
|
|
705
|
+
updatedAt: mem.updatedAt,
|
|
706
|
+
temporal: mem.temporal,
|
|
707
|
+
commitment: mem.commitment,
|
|
708
|
+
sessionEpoch: mem.sessionEpoch,
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
if (this._sessionEpoch !== opEpoch) {
|
|
713
|
+
throw new Error('Operation aborted: session was reset concurrently');
|
|
714
|
+
}
|
|
715
|
+
if (options.persist !== false && this.storagePath) await this.save();
|
|
716
|
+
if (this._sessionEpoch !== opEpoch) {
|
|
717
|
+
throw new Error('Operation aborted: session was reset concurrently');
|
|
718
|
+
}
|
|
719
|
+
return receipt;
|
|
720
|
+
} catch (error) {
|
|
721
|
+
this._restoreMutableState(transaction);
|
|
722
|
+
throw error;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* Stores a persistent, encrypted memory atom in the local sovereign vault
|
|
728
|
+
* and indexes it in the local semantic vector store.
|
|
729
|
+
* Supports ontology types, temporal validity, and contradiction detection.
|
|
730
|
+
* @param {string} text - Memory content or rule
|
|
731
|
+
* @param {Object} [options]
|
|
732
|
+
* @param {string} [options.id] - Optional custom ID (auto-generated if omitted)
|
|
733
|
+
* @param {string} [options.scope] - Override scope
|
|
734
|
+
* @param {string} [options.type] - Memory ontology type ('episodic', 'semantic', 'preference', 'policy', etc.)
|
|
735
|
+
* @param {number} [options.importance=1.0] - Importance multiplier
|
|
736
|
+
* @param {number|string|Date} [options.validFrom] - Start of validity window
|
|
737
|
+
* @param {number|string|Date|null} [options.validUntil] - End of validity window
|
|
738
|
+
* @param {number|string|Date} [options.observedAt] - Time observation was recorded
|
|
739
|
+
* @param {number} [options.ttlMs] - Time-to-live in milliseconds
|
|
740
|
+
* @param {string[]} [options.supersedes] - Explicit list of prior memory IDs this atom supersedes
|
|
741
|
+
* @param {Object} [options.metadata={}] - Custom structured metadata
|
|
742
|
+
* @returns {Promise<{ id: string, scope: string, text: string, type: string, temporal: Object, createdAt: number }>}
|
|
743
|
+
*/
|
|
744
|
+
async remember(text, options = {}) {
|
|
745
|
+
const opEpoch = this._sessionEpoch;
|
|
746
|
+
if (Buffer.isBuffer(this.vaultKey) && this.vaultKey.every(b => b === 0)) {
|
|
747
|
+
throw new Error('Cannot store memory: session has been reset or zeroized');
|
|
748
|
+
}
|
|
749
|
+
if (!text || typeof text !== 'string' || text.trim().length === 0) {
|
|
750
|
+
throw new Error('Memory text must be a non-empty string');
|
|
751
|
+
}
|
|
752
|
+
const id = options.id || `atom_${Date.now()}_${crypto.randomBytes(3).toString('hex')}`;
|
|
753
|
+
if (this._memories.has(id)) throw new Error(`Memory atom ${id} already exists`);
|
|
754
|
+
const scope = options.scope || this.scope;
|
|
755
|
+
const importance = typeof options.importance === 'number' ? options.importance : 1.0;
|
|
756
|
+
const createdAt = Date.now();
|
|
757
|
+
const type = normalizeMemoryType(options.type || inferMemoryType(text));
|
|
758
|
+
const temporal = createTemporalMetadata({
|
|
759
|
+
validFrom: options.validFrom,
|
|
760
|
+
validUntil: options.validUntil,
|
|
761
|
+
observedAt: options.observedAt,
|
|
762
|
+
ttlMs: options.ttlMs,
|
|
763
|
+
});
|
|
764
|
+
const explicitSupersedes = Array.isArray(options.supersedes)
|
|
765
|
+
? Array.from(new Set(options.supersedes))
|
|
766
|
+
: [];
|
|
767
|
+
for (const priorId of explicitSupersedes) {
|
|
768
|
+
if (priorId === id) throw new Error('A memory atom cannot supersede itself');
|
|
769
|
+
const prior = this._memories.get(priorId);
|
|
770
|
+
if (!prior) throw new Error(`Unknown supersession node: ${priorId}`);
|
|
771
|
+
if (prior.scope !== scope) {
|
|
772
|
+
throw new Error(`Cross-scope supersession is not allowed ("${scope}" -> "${prior.scope}")`);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
const { commitment, blindingFactor } = computeHidingCommitment(text, options.blindingFactor);
|
|
777
|
+
const record = {
|
|
778
|
+
id,
|
|
779
|
+
text,
|
|
780
|
+
scope,
|
|
781
|
+
type,
|
|
782
|
+
importance,
|
|
783
|
+
commitment,
|
|
784
|
+
blindingFactor,
|
|
785
|
+
createdAt,
|
|
786
|
+
temporal,
|
|
787
|
+
sessionEpoch: opEpoch,
|
|
788
|
+
metadata: { ...options.metadata },
|
|
789
|
+
};
|
|
790
|
+
|
|
791
|
+
const candidates = Array.from(this._memories.values())
|
|
792
|
+
.filter(m => m.scope === scope && !m.revoked && !this.contradictionGraph.isSuperseded(m.id))
|
|
793
|
+
.map(m => ({ id: m.id, text: m.text }));
|
|
794
|
+
const conflicts = detectContradictions(text, candidates);
|
|
795
|
+
const transaction = this._snapshotMutableState();
|
|
796
|
+
try {
|
|
797
|
+
this.contradictionGraph.registerNode(id, { scope });
|
|
798
|
+
for (const conflict of conflicts) {
|
|
799
|
+
this.contradictionGraph.recordSupersession(id, conflict.priorId, conflict.reason);
|
|
800
|
+
}
|
|
801
|
+
for (const priorId of explicitSupersedes) {
|
|
802
|
+
this.contradictionGraph.recordSupersession(id, priorId, 'explicit_supersession');
|
|
803
|
+
}
|
|
804
|
+
} catch (error) {
|
|
805
|
+
this._restoreMutableState(transaction);
|
|
806
|
+
throw error;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
this._memories.set(id, record);
|
|
810
|
+
this._activeCommitments.push(commitment);
|
|
811
|
+
this._activeCommitmentSet.add(commitment);
|
|
812
|
+
this._currentRoot = new MerkleSet(this._activeCommitments).root();
|
|
813
|
+
|
|
814
|
+
let indexRes;
|
|
815
|
+
try {
|
|
816
|
+
indexRes = await this.rag.index(
|
|
817
|
+
id,
|
|
818
|
+
text,
|
|
819
|
+
{
|
|
820
|
+
...options.metadata,
|
|
821
|
+
scope,
|
|
822
|
+
type,
|
|
823
|
+
importance,
|
|
824
|
+
createdAt,
|
|
825
|
+
temporal,
|
|
826
|
+
commitment,
|
|
827
|
+
sessionEpoch: opEpoch,
|
|
828
|
+
},
|
|
829
|
+
{ epoch: this.rag.store.epoch }
|
|
830
|
+
);
|
|
831
|
+
} catch (error) {
|
|
832
|
+
this._restoreMutableState(transaction);
|
|
833
|
+
throw error;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
if (this._sessionEpoch !== opEpoch || !indexRes) {
|
|
837
|
+
this._restoreMutableState(transaction);
|
|
838
|
+
throw new Error('Operation aborted: session was reset concurrently');
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
try {
|
|
842
|
+
if (options.persist !== false && this.storagePath) await this.save();
|
|
843
|
+
if (this._sessionEpoch !== opEpoch) {
|
|
844
|
+
throw new Error('Operation aborted: session was reset concurrently');
|
|
845
|
+
}
|
|
846
|
+
} catch (error) {
|
|
847
|
+
this._restoreMutableState(transaction);
|
|
848
|
+
throw error;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
return { id, scope, text, type, temporal, createdAt };
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
* Performs client-side semantic vector search and retrieves top-k relevant memory atoms.
|
|
856
|
+
* Supports active-state resolution, temporal filtering, and ontology type filtering.
|
|
857
|
+
* @param {string} query - Agent query or current prompt
|
|
858
|
+
* @param {Object} [options]
|
|
859
|
+
* @param {number} [options.topK=5] - Number of results
|
|
860
|
+
* @param {number} [options.threshold=-1.0] - Minimum similarity threshold
|
|
861
|
+
* @param {string} [options.scope] - Filter by scope
|
|
862
|
+
* @param {string} [options.type] - Filter by memory type ('policy', 'preference', 'semantic', etc.)
|
|
863
|
+
* @param {boolean} [options.filterSuperseded=true] - When true, omits superseded historical facts
|
|
864
|
+
* @param {number|string|Date} [options.asOfTime] - Target point in time for temporal validity
|
|
865
|
+
* @returns {Promise<Array<{ id: string, text: string, score: number, scope: string, type: string, superseded: boolean }>>}
|
|
866
|
+
*/
|
|
867
|
+
async recall(query, options = {}) {
|
|
868
|
+
if (!query || typeof query !== 'string') return [];
|
|
869
|
+
const opEpoch = this._sessionEpoch;
|
|
870
|
+
const topK = options.topK ?? 5;
|
|
871
|
+
if (typeof topK !== 'number' || !Number.isFinite(topK) || topK < 0) {
|
|
872
|
+
throw new TypeError('topK must be a finite non-negative number');
|
|
873
|
+
}
|
|
874
|
+
if (topK === 0) return [];
|
|
875
|
+
const requestedCount = Math.ceil(topK);
|
|
876
|
+
const now = options.asOfTime === undefined
|
|
877
|
+
? Date.now()
|
|
878
|
+
: parseTimestamp(options.asOfTime, null);
|
|
879
|
+
if (now === null) throw new TypeError('asOfTime must be a valid timestamp');
|
|
880
|
+
const overfetch = Math.min(
|
|
881
|
+
Math.max(requestedCount * 4, requestedCount + 16),
|
|
882
|
+
Math.max(requestedCount, MAX_RECALL_OVERFETCH),
|
|
883
|
+
);
|
|
884
|
+
const requestedScope = options.scope || this.scope;
|
|
885
|
+
const rawResults = await this.rag.search(query, {
|
|
886
|
+
scope: requestedScope,
|
|
887
|
+
topK: overfetch,
|
|
888
|
+
threshold: options.threshold,
|
|
889
|
+
signal: options.signal,
|
|
890
|
+
});
|
|
891
|
+
if (this._sessionEpoch !== opEpoch || options.signal?.aborted) return [];
|
|
892
|
+
|
|
893
|
+
const filterSuperseded = options.filterSuperseded !== false;
|
|
894
|
+
const typeFilter = options.type ? normalizeMemoryType(options.type) : null;
|
|
895
|
+
const filtered = [];
|
|
896
|
+
let examined = 0;
|
|
897
|
+
for (const res of rawResults) {
|
|
898
|
+
if (examined >= overfetch) break;
|
|
899
|
+
examined += 1;
|
|
900
|
+
const mem = this._memories.get(res.id);
|
|
901
|
+
if (!mem) continue;
|
|
902
|
+
if (mem.sessionEpoch !== undefined && mem.sessionEpoch !== opEpoch) continue;
|
|
903
|
+
if (mem.scope !== requestedScope && mem.scope !== 'enigma.global') continue;
|
|
904
|
+
if (mem.revoked || !mem.commitment || !this._activeCommitmentSet.has(mem.commitment)) continue;
|
|
905
|
+
const isSup = this.contradictionGraph.isSuperseded(mem.id);
|
|
906
|
+
if (filterSuperseded && isSup) continue;
|
|
907
|
+
if (typeFilter && mem.type !== typeFilter) continue;
|
|
908
|
+
if (!isTemporallyActive(mem, now)) continue;
|
|
909
|
+
|
|
910
|
+
const decay = computeTemporalDecay(mem, now);
|
|
911
|
+
filtered.push({
|
|
912
|
+
id: mem.id,
|
|
913
|
+
text: mem.text,
|
|
914
|
+
scope: mem.scope,
|
|
915
|
+
type: mem.type,
|
|
916
|
+
importance: mem.importance,
|
|
917
|
+
createdAt: mem.createdAt,
|
|
918
|
+
commitment: mem.commitment,
|
|
919
|
+
temporal: mem.temporal,
|
|
920
|
+
metadata: {
|
|
921
|
+
...mem.metadata,
|
|
922
|
+
scope: mem.scope,
|
|
923
|
+
type: mem.type,
|
|
924
|
+
importance: mem.importance,
|
|
925
|
+
createdAt: mem.createdAt,
|
|
926
|
+
temporal: mem.temporal,
|
|
927
|
+
commitment: mem.commitment,
|
|
928
|
+
sessionEpoch: mem.sessionEpoch,
|
|
929
|
+
},
|
|
930
|
+
score: Math.round(res.score * decay * 1000) / 1000,
|
|
931
|
+
superseded: isSup,
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
if (this._sessionEpoch !== opEpoch || options.signal?.aborted) return [];
|
|
936
|
+
filtered.sort((a, b) => b.score - a.score);
|
|
937
|
+
return filtered.slice(0, requestedCount);
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
/**
|
|
941
|
+
* Evaluates an incoming observation for novelty, importance, and contradiction.
|
|
942
|
+
* @param {string} text
|
|
943
|
+
* @param {Object} [metadata={}]
|
|
944
|
+
*/
|
|
945
|
+
evaluateObservation(text, metadata = {}) {
|
|
946
|
+
const candidates = Array.from(this._memories.values()).map(m => ({ id: m.id, text: m.text }));
|
|
947
|
+
return this.decisionEngine.evaluate(text, metadata, candidates);
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
/**
|
|
951
|
+
* Consolidates redundant memory atoms in the active vault.
|
|
952
|
+
*/
|
|
953
|
+
consolidate() {
|
|
954
|
+
const list = Array.from(this._memories.values());
|
|
955
|
+
return this.decisionEngine.consolidate(list);
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
/**
|
|
959
|
+
* Compiles a token-budgeted prompt context pack from sovereign memory.
|
|
960
|
+
* @param {string} query
|
|
961
|
+
* @param {Object} [options]
|
|
962
|
+
* @param {number} [options.maxTokens=2000]
|
|
963
|
+
* @param {number} [options.topK=8]
|
|
964
|
+
* @returns {Promise<{ contextText: string, items: Array, estimatedTokens: number }>}
|
|
965
|
+
*/
|
|
966
|
+
async getContextPack(query, options = {}) {
|
|
967
|
+
const opEpoch = this._sessionEpoch;
|
|
968
|
+
const maxTokens = options.maxTokens ?? 2000;
|
|
969
|
+
if (typeof maxTokens !== 'number' || !Number.isFinite(maxTokens) || maxTokens < 0) {
|
|
970
|
+
throw new TypeError('maxTokens must be a finite non-negative number');
|
|
971
|
+
}
|
|
972
|
+
const items = await this.recall(query, {
|
|
973
|
+
...options,
|
|
974
|
+
topK: options.topK ?? 8,
|
|
975
|
+
});
|
|
976
|
+
if (this._sessionEpoch !== opEpoch || options.signal?.aborted) {
|
|
977
|
+
return { contextText: '', items: [], totalChars: 0, estimatedTokens: 0 };
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
const packedItems = [];
|
|
981
|
+
const sections = [];
|
|
982
|
+
const maxChars = maxTokens * 4;
|
|
983
|
+
for (const item of items) {
|
|
984
|
+
const section = `[Context #${packedItems.length + 1} (${item.scope})]: ${item.text}`;
|
|
985
|
+
const candidateLength = sections.length === 0
|
|
986
|
+
? section.length
|
|
987
|
+
: sections.join('\n\n').length + 2 + section.length;
|
|
988
|
+
if (candidateLength > maxChars) break;
|
|
989
|
+
packedItems.push(item);
|
|
990
|
+
sections.push(section);
|
|
991
|
+
}
|
|
992
|
+
const contextText = sections.join('\n\n');
|
|
993
|
+
return {
|
|
994
|
+
contextText,
|
|
995
|
+
items: packedItems,
|
|
996
|
+
totalChars: contextText.length,
|
|
997
|
+
estimatedTokens: Math.ceil(contextText.length / 4),
|
|
998
|
+
};
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
/**
|
|
1002
|
+
* Replaces a memory atom's plaintext and emits a signed update transition.
|
|
1003
|
+
* @param {string} id
|
|
1004
|
+
* @param {string} text
|
|
1005
|
+
* @param {Object} [options]
|
|
1006
|
+
*/
|
|
1007
|
+
async updateMemory(id, text, options = {}) {
|
|
1008
|
+
const opEpoch = this._sessionEpoch;
|
|
1009
|
+
if (!id || !this._memories.has(id)) throw new Error(`Memory atom ${id} not found`);
|
|
1010
|
+
if (!text || typeof text !== 'string' || text.trim().length === 0) {
|
|
1011
|
+
throw new Error('Memory text must be a non-empty string');
|
|
1012
|
+
}
|
|
1013
|
+
const mem = this._memories.get(id);
|
|
1014
|
+
const priorCommitment = mem.commitment;
|
|
1015
|
+
if (mem.revoked || !priorCommitment || !this._activeCommitmentSet.has(priorCommitment)) {
|
|
1016
|
+
throw new Error(`Cannot update inactive or revoked memory atom "${id}"`);
|
|
1017
|
+
}
|
|
1018
|
+
const hasTemporalUpdate = ['validFrom', 'validUntil', 'observedAt', 'ttlMs']
|
|
1019
|
+
.some(key => Object.prototype.hasOwnProperty.call(options, key));
|
|
1020
|
+
const temporal = hasTemporalUpdate
|
|
1021
|
+
? createTemporalMetadata({
|
|
1022
|
+
validFrom: options.validFrom ?? mem.temporal.validFrom,
|
|
1023
|
+
validUntil: Object.prototype.hasOwnProperty.call(options, 'ttlMs')
|
|
1024
|
+
? undefined
|
|
1025
|
+
: (Object.prototype.hasOwnProperty.call(options, 'validUntil')
|
|
1026
|
+
? options.validUntil
|
|
1027
|
+
: mem.temporal.validUntil),
|
|
1028
|
+
observedAt: options.observedAt ?? mem.temporal.observedAt,
|
|
1029
|
+
ttlMs: options.ttlMs,
|
|
1030
|
+
})
|
|
1031
|
+
: mem.temporal;
|
|
1032
|
+
const type = normalizeMemoryType(options.type || mem.type);
|
|
1033
|
+
const importance = typeof options.importance === 'number' ? options.importance : mem.importance;
|
|
1034
|
+
const updatedAt = Date.now();
|
|
1035
|
+
const metadata = { ...mem.metadata, ...options.metadata };
|
|
1036
|
+
const transaction = this._snapshotMutableState();
|
|
1037
|
+
|
|
1038
|
+
try {
|
|
1039
|
+
const indexResult = await this.rag.index(id, text, {
|
|
1040
|
+
...metadata,
|
|
1041
|
+
scope: mem.scope,
|
|
1042
|
+
type,
|
|
1043
|
+
importance,
|
|
1044
|
+
createdAt: mem.createdAt,
|
|
1045
|
+
updatedAt,
|
|
1046
|
+
temporal,
|
|
1047
|
+
sessionEpoch: opEpoch,
|
|
1048
|
+
}, { epoch: this.rag.store.epoch });
|
|
1049
|
+
if (this._sessionEpoch !== opEpoch || !indexResult) {
|
|
1050
|
+
throw new Error('Memory update aborted: session was reset concurrently');
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
mem.text = text;
|
|
1054
|
+
mem.type = type;
|
|
1055
|
+
mem.importance = importance;
|
|
1056
|
+
mem.temporal = temporal;
|
|
1057
|
+
mem.metadata = metadata;
|
|
1058
|
+
mem.updatedAt = updatedAt;
|
|
1059
|
+
mem.sessionEpoch = opEpoch;
|
|
1060
|
+
|
|
1061
|
+
const receipt = await this.createReceipt(id, {
|
|
1062
|
+
operation: 'update',
|
|
1063
|
+
priorCommitment,
|
|
1064
|
+
blindingFactor: crypto.randomBytes(32),
|
|
1065
|
+
persist: false,
|
|
1066
|
+
metadata: {
|
|
1067
|
+
kind: 'content_update',
|
|
1068
|
+
updatedAt,
|
|
1069
|
+
},
|
|
1070
|
+
});
|
|
1071
|
+
const receiptRef = createReceiptRef(receipt);
|
|
1072
|
+
mem.metadata = { ...mem.metadata, updateReceiptRef: receiptRef };
|
|
1073
|
+
this.rag.updateMetadata(id, {
|
|
1074
|
+
...mem.metadata,
|
|
1075
|
+
scope: mem.scope,
|
|
1076
|
+
type: mem.type,
|
|
1077
|
+
importance: mem.importance,
|
|
1078
|
+
createdAt: mem.createdAt,
|
|
1079
|
+
updatedAt,
|
|
1080
|
+
temporal: mem.temporal,
|
|
1081
|
+
commitment: mem.commitment,
|
|
1082
|
+
sessionEpoch: opEpoch,
|
|
1083
|
+
});
|
|
1084
|
+
|
|
1085
|
+
if (this.storagePath) await this.save();
|
|
1086
|
+
if (this._sessionEpoch !== opEpoch) {
|
|
1087
|
+
throw new Error('Memory update aborted: session was reset concurrently');
|
|
1088
|
+
}
|
|
1089
|
+
return {
|
|
1090
|
+
memory: {
|
|
1091
|
+
id,
|
|
1092
|
+
scope: mem.scope,
|
|
1093
|
+
text: mem.text,
|
|
1094
|
+
type: mem.type,
|
|
1095
|
+
temporal: mem.temporal,
|
|
1096
|
+
createdAt: mem.createdAt,
|
|
1097
|
+
updatedAt,
|
|
1098
|
+
},
|
|
1099
|
+
receipt,
|
|
1100
|
+
receiptRef,
|
|
1101
|
+
};
|
|
1102
|
+
} catch (error) {
|
|
1103
|
+
this._restoreMutableState(transaction);
|
|
1104
|
+
throw error;
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
/**
|
|
1109
|
+
* Executes one provider-neutral retrieval/model/persistence turn.
|
|
1110
|
+
* @param {{ prompt: string, invoke: Function, metadata?: Object, retrieval?: Object, rememberResponse?: boolean, contextCarrier?: Object }} input
|
|
1111
|
+
*/
|
|
1112
|
+
async runAgentTurn(input = {}) {
|
|
1113
|
+
if (!input.prompt || typeof input.prompt !== 'string') {
|
|
1114
|
+
throw new TypeError('runAgentTurn prompt must be a non-empty string');
|
|
1115
|
+
}
|
|
1116
|
+
if (typeof input.invoke !== 'function') {
|
|
1117
|
+
throw new TypeError('runAgentTurn invoke must be a function');
|
|
1118
|
+
}
|
|
1119
|
+
const opEpoch = this._sessionEpoch;
|
|
1120
|
+
const suppliedMetadata = input.metadata && typeof input.metadata === 'object' ? { ...input.metadata } : {};
|
|
1121
|
+
delete suppliedMetadata.contextCarrier;
|
|
1122
|
+
const metadata = suppliedMetadata;
|
|
1123
|
+
const retrieval = input.retrieval && typeof input.retrieval === 'object' ? { ...input.retrieval } : {};
|
|
1124
|
+
const context = await this.getContextPack(input.prompt, retrieval);
|
|
1125
|
+
if (this._sessionEpoch !== opEpoch) {
|
|
1126
|
+
throw new Error('Agent turn aborted: session was reset during retrieval');
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
let carrier = null;
|
|
1130
|
+
let invocationCarrier = null;
|
|
1131
|
+
let disclosedCarrier = null;
|
|
1132
|
+
let carrierOpening = null;
|
|
1133
|
+
if (input.contextCarrier && typeof input.contextCarrier === 'object') {
|
|
1134
|
+
const prepared = prepareAgentContextCarrier(context.contextText, input.contextCarrier);
|
|
1135
|
+
carrier = prepared.carrier;
|
|
1136
|
+
const protectedCarrier = publicContextCarrier(carrier, prepared.requested, prepared.reasonOverride);
|
|
1137
|
+
disclosedCarrier = protectedCarrier.disclosure;
|
|
1138
|
+
carrierOpening = protectedCarrier.opening;
|
|
1139
|
+
invocationCarrier = {
|
|
1140
|
+
selected: carrier.selected,
|
|
1141
|
+
textFallback: carrier.textFallback,
|
|
1142
|
+
blocks: carrier.blocks.map(cloneEphemeralProviderBlock),
|
|
1143
|
+
descriptor: carrier.descriptor,
|
|
1144
|
+
decision: disclosedCarrier.decision,
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
let result;
|
|
1149
|
+
try {
|
|
1150
|
+
result = await input.invoke({
|
|
1151
|
+
prompt: input.prompt,
|
|
1152
|
+
context: context.contextText,
|
|
1153
|
+
contextPack: context,
|
|
1154
|
+
metadata,
|
|
1155
|
+
...(invocationCarrier ? { contextCarrier: invocationCarrier } : {}),
|
|
1156
|
+
});
|
|
1157
|
+
if (invocationCarrier) {
|
|
1158
|
+
result = sanitizeCarrierEchoes(result, invocationCarrier, disclosedCarrier);
|
|
1159
|
+
}
|
|
1160
|
+
} finally {
|
|
1161
|
+
zeroizeContextCarrier(carrier, invocationCarrier);
|
|
1162
|
+
carrier = null;
|
|
1163
|
+
invocationCarrier = null;
|
|
1164
|
+
}
|
|
1165
|
+
if (this._sessionEpoch !== opEpoch) {
|
|
1166
|
+
throw new Error('Agent turn aborted: session was reset during model invocation');
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
const receiptRefs = [];
|
|
1170
|
+
let receipt = null;
|
|
1171
|
+
let memory = null;
|
|
1172
|
+
if (input.rememberResponse !== false) {
|
|
1173
|
+
const responseText = extractModelText(result);
|
|
1174
|
+
if (!responseText || responseText.trim().length === 0) {
|
|
1175
|
+
throw new Error('Model result must contain textual output when rememberResponse is enabled');
|
|
1176
|
+
}
|
|
1177
|
+
const transaction = this._snapshotMutableState();
|
|
1178
|
+
try {
|
|
1179
|
+
const turnId = typeof metadata.turnId === 'string' && metadata.turnId
|
|
1180
|
+
? metadata.turnId
|
|
1181
|
+
: `turn_${Date.now()}_${crypto.randomBytes(4).toString('hex')}`;
|
|
1182
|
+
const promptBinding = createBlindedValueCommitment('agent_turn_prompt', input.prompt);
|
|
1183
|
+
const resultBinding = createBlindedValueCommitment('agent_turn_result', responseText);
|
|
1184
|
+
const contextRefs = context.items.map(item => ({
|
|
1185
|
+
memoryId: item.id,
|
|
1186
|
+
commitment: item.commitment,
|
|
1187
|
+
receiptRef: item.metadata?.agentTurn?.receiptRef || item.metadata?.updateReceiptRef || null,
|
|
1188
|
+
}));
|
|
1189
|
+
const outcomeMetadata = disclosedCarrier
|
|
1190
|
+
? { ...metadata, contextCarrier: disclosedCarrier }
|
|
1191
|
+
: metadata;
|
|
1192
|
+
memory = await this.remember(responseText, {
|
|
1193
|
+
scope: retrieval.scope || this.scope,
|
|
1194
|
+
type: metadata.memoryType || 'episodic',
|
|
1195
|
+
importance: typeof metadata.importance === 'number' ? metadata.importance : 1,
|
|
1196
|
+
persist: false,
|
|
1197
|
+
metadata: {
|
|
1198
|
+
agentTurn: {
|
|
1199
|
+
turnId,
|
|
1200
|
+
promptCommitment: promptBinding.public,
|
|
1201
|
+
resultCommitment: resultBinding.public,
|
|
1202
|
+
contextRefs,
|
|
1203
|
+
metadata: outcomeMetadata,
|
|
1204
|
+
...(disclosedCarrier ? { contextCarrier: disclosedCarrier } : {}),
|
|
1205
|
+
...(carrierOpening ? { contextCarrierCommitment: carrierOpening.commitment } : {}),
|
|
1206
|
+
},
|
|
1207
|
+
},
|
|
1208
|
+
});
|
|
1209
|
+
this._privateOpenings.set(memory.id, {
|
|
1210
|
+
prompt: promptBinding.opening,
|
|
1211
|
+
result: resultBinding.opening,
|
|
1212
|
+
...(carrierOpening ? { contextCarrier: carrierOpening } : {}),
|
|
1213
|
+
});
|
|
1214
|
+
receipt = await this.createReceipt(memory.id, {
|
|
1215
|
+
persist: false,
|
|
1216
|
+
metadata: {
|
|
1217
|
+
kind: 'agent_turn_outcome',
|
|
1218
|
+
turnId,
|
|
1219
|
+
promptCommitment: promptBinding.public,
|
|
1220
|
+
resultCommitment: resultBinding.public,
|
|
1221
|
+
contextCommitments: contextRefs.map(ref => ref.commitment),
|
|
1222
|
+
...(disclosedCarrier ? { contextCarrier: disclosedCarrier } : {}),
|
|
1223
|
+
},
|
|
1224
|
+
});
|
|
1225
|
+
const receiptRef = createReceiptRef(receipt);
|
|
1226
|
+
receiptRefs.push(receiptRef);
|
|
1227
|
+
const stored = this._memories.get(memory.id);
|
|
1228
|
+
stored.metadata.agentTurn.receiptRef = receiptRef;
|
|
1229
|
+
this.rag.updateMetadata(memory.id, {
|
|
1230
|
+
...stored.metadata,
|
|
1231
|
+
scope: stored.scope,
|
|
1232
|
+
type: stored.type,
|
|
1233
|
+
importance: stored.importance,
|
|
1234
|
+
createdAt: stored.createdAt,
|
|
1235
|
+
temporal: stored.temporal,
|
|
1236
|
+
commitment: stored.commitment,
|
|
1237
|
+
sessionEpoch: stored.sessionEpoch,
|
|
1238
|
+
});
|
|
1239
|
+
|
|
1240
|
+
if (this.storagePath) await this.save();
|
|
1241
|
+
if (this._sessionEpoch !== opEpoch) {
|
|
1242
|
+
throw new Error('Agent turn aborted: session was reset before completion');
|
|
1243
|
+
}
|
|
1244
|
+
} catch (error) {
|
|
1245
|
+
this._restoreMutableState(transaction);
|
|
1246
|
+
throw error;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
if (this._sessionEpoch !== opEpoch) {
|
|
1251
|
+
throw new Error('Agent turn aborted: session was reset before completion');
|
|
1252
|
+
}
|
|
1253
|
+
return {
|
|
1254
|
+
context,
|
|
1255
|
+
result,
|
|
1256
|
+
memory,
|
|
1257
|
+
receipts: {
|
|
1258
|
+
refs: receiptRefs,
|
|
1259
|
+
memory: receipt,
|
|
1260
|
+
},
|
|
1261
|
+
...(disclosedCarrier ? { contextCarrier: disclosedCarrier } : {}),
|
|
1262
|
+
};
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
/**
|
|
1266
|
+
* Removes / tombstones a memory atom by ID.
|
|
1267
|
+
* @param {string} id
|
|
1268
|
+
* @returns {Promise<boolean>}
|
|
1269
|
+
*/
|
|
1270
|
+
async forget(id) {
|
|
1271
|
+
const opEpoch = this._sessionEpoch;
|
|
1272
|
+
if (!id || !this._memories.has(id)) return false;
|
|
1273
|
+
const mem = this._memories.get(id);
|
|
1274
|
+
if (mem.revoked) return false;
|
|
1275
|
+
const transaction = this._snapshotMutableState();
|
|
1276
|
+
try {
|
|
1277
|
+
if (mem.commitment) {
|
|
1278
|
+
this._activeCommitments = this._activeCommitments.filter(c => c !== mem.commitment);
|
|
1279
|
+
this._activeCommitmentSet.delete(mem.commitment);
|
|
1280
|
+
this._currentRoot = new MerkleSet(this._activeCommitments).root();
|
|
1281
|
+
}
|
|
1282
|
+
mem.revoked = true;
|
|
1283
|
+
mem.revokedAt = Date.now();
|
|
1284
|
+
this.rag.store.delete(id);
|
|
1285
|
+
|
|
1286
|
+
if (this._sessionEpoch !== opEpoch) {
|
|
1287
|
+
throw new Error('Operation aborted: session was reset concurrently');
|
|
1288
|
+
}
|
|
1289
|
+
if (this.storagePath) await this.save();
|
|
1290
|
+
if (this._sessionEpoch !== opEpoch) {
|
|
1291
|
+
throw new Error('Operation aborted: session was reset concurrently');
|
|
1292
|
+
}
|
|
1293
|
+
return true;
|
|
1294
|
+
} catch (error) {
|
|
1295
|
+
this._restoreMutableState(transaction);
|
|
1296
|
+
throw error;
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
/**
|
|
1301
|
+
* Issues a cryptographically signed BlindedMemoryGrant authorizing another agent
|
|
1302
|
+
* to selectively query memory clusters under strict attribute-based access control (ABAC).
|
|
1303
|
+
* @param {Buffer|string} granteePublicKey - 32-byte Ed25519 public key of receiving agent
|
|
1304
|
+
* @param {Object} [options]
|
|
1305
|
+
* @param {string[]} [options.allowedTypes] - Allowed ontology types ('policy', 'preference', etc.)
|
|
1306
|
+
* @param {number} [options.maxAccessCount=100] - Usage limit (0 = unlimited)
|
|
1307
|
+
* @param {number|string|Date} [options.validUntil] - Expiration timestamp
|
|
1308
|
+
* @param {Object} [options.metadata]
|
|
1309
|
+
* @returns {Object} Signed BlindedMemoryGrant
|
|
1310
|
+
*/
|
|
1311
|
+
grantCapability(granteePublicKey, options = {}) {
|
|
1312
|
+
if (!this.mesh.identity) throw new Error('Mesh identity is required to issue capability grants');
|
|
1313
|
+
return createBlindedMemoryGrant({
|
|
1314
|
+
granterAgentId: this.agentId,
|
|
1315
|
+
granterSecretKey: this.mesh.identity.secretKey,
|
|
1316
|
+
granterPublicKey: this.mesh.identity.publicKey,
|
|
1317
|
+
granteePublicKey,
|
|
1318
|
+
scope: options.scope || this.scope,
|
|
1319
|
+
allowedTypes: options.allowedTypes,
|
|
1320
|
+
maxAccessCount: options.maxAccessCount,
|
|
1321
|
+
validUntil: options.validUntil,
|
|
1322
|
+
metadata: options.metadata,
|
|
1323
|
+
});
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
/**
|
|
1327
|
+
* Resets local session state, wipes volatile keyrings/vector indices, unlinks
|
|
1328
|
+
* on-disk vault storage, and emits a signed ErasureAttestation receipt.
|
|
1329
|
+
* @param {Object} [options]
|
|
1330
|
+
* @param {boolean} [options.wipeStorage=true] - Whether to scrub and unlink the on-disk storage file
|
|
1331
|
+
* @param {Object} [options.metadata]
|
|
1332
|
+
* @returns {Promise<Object>} Signed ErasureAttestation receipt
|
|
1333
|
+
*/
|
|
1334
|
+
async resetSession(options = {}) {
|
|
1335
|
+
++this._sessionEpoch;
|
|
1336
|
+
const meshIdentity = this.mesh.identity;
|
|
1337
|
+
if (!meshIdentity) throw new Error('Mesh identity is required to sign erasure attestation receipts');
|
|
1338
|
+
|
|
1339
|
+
const preErasureRoot = this._currentRoot;
|
|
1340
|
+
const memoryCount = this._memories.size;
|
|
1341
|
+
const activeCommitments = [...this._activeCommitments];
|
|
1342
|
+
const shouldWipeStorage = options.wipeStorage !== false;
|
|
1343
|
+
let storageWiped = false;
|
|
1344
|
+
if (shouldWipeStorage && this.storagePath && fs.existsSync(this.storagePath)) {
|
|
1345
|
+
try {
|
|
1346
|
+
const fileSize = fs.statSync(this.storagePath).size;
|
|
1347
|
+
fs.writeFileSync(this.storagePath, crypto.randomBytes(Math.max(fileSize, 512)));
|
|
1348
|
+
fs.unlinkSync(this.storagePath);
|
|
1349
|
+
storageWiped = true;
|
|
1350
|
+
} catch {
|
|
1351
|
+
try {
|
|
1352
|
+
fs.unlinkSync(this.storagePath);
|
|
1353
|
+
storageWiped = true;
|
|
1354
|
+
} catch {
|
|
1355
|
+
storageWiped = false;
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
// 2. Generate signed cryptographic ErasureAttestation receipt before wiping key material
|
|
1361
|
+
const erasureReceipt = createErasureReceipt({
|
|
1362
|
+
agentId: this.agentId,
|
|
1363
|
+
scope: this.scope,
|
|
1364
|
+
erasedMemoryCount: memoryCount,
|
|
1365
|
+
preErasureRoot,
|
|
1366
|
+
activeCommitments,
|
|
1367
|
+
storageWiped,
|
|
1368
|
+
signingSecretKey: meshIdentity.secretKey,
|
|
1369
|
+
signingPublicKey: meshIdentity.publicKey,
|
|
1370
|
+
metadata: options.metadata,
|
|
1371
|
+
});
|
|
1372
|
+
|
|
1373
|
+
// 3. Best-effort zeroize master key buffer in volatile memory
|
|
1374
|
+
if (Buffer.isBuffer(this.vaultKey)) {
|
|
1375
|
+
secureZeroizeBuffer(this.vaultKey);
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
// 4. Purge in-memory state collections, zero typed arrays, and reset BM25 indices
|
|
1379
|
+
this._memories.clear();
|
|
1380
|
+
this._privateOpenings.clear();
|
|
1381
|
+
this._activeCommitments = [];
|
|
1382
|
+
this._activeCommitmentSet.clear();
|
|
1383
|
+
this._currentRoot = new MerkleSet().root();
|
|
1384
|
+
this.contradictionGraph = new ContradictionGraph();
|
|
1385
|
+
this.federation = new EnigmaFederationRouter({
|
|
1386
|
+
enigma: this,
|
|
1387
|
+
onStateMutation: async () => {
|
|
1388
|
+
if (this.storagePath) await this.save();
|
|
1389
|
+
},
|
|
1390
|
+
});
|
|
1391
|
+
if (this.rag?.store && typeof this.rag.store.clear === 'function') {
|
|
1392
|
+
this.rag.store.clear();
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
return erasureReceipt;
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
async zeroizeSession(options = {}) {
|
|
1399
|
+
return await this.resetSession(options);
|
|
1400
|
+
}
|
|
1401
|
+
/**
|
|
1402
|
+
* Persists all memory atoms and vector embeddings encrypted-at-rest to disk using AES-256-GCM.
|
|
1403
|
+
* @param {string} [targetPath]
|
|
1404
|
+
*/
|
|
1405
|
+
async save(targetPath = this.storagePath) {
|
|
1406
|
+
if (!targetPath) return;
|
|
1407
|
+
if (Buffer.isBuffer(this.vaultKey) && this.vaultKey.every(b => b === 0)) {
|
|
1408
|
+
return;
|
|
1409
|
+
}
|
|
1410
|
+
const saveEpoch = this._sessionEpoch;
|
|
1411
|
+
const resolvedPath = path.resolve(targetPath);
|
|
1412
|
+
const dir = path.dirname(resolvedPath);
|
|
1413
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
1414
|
+
|
|
1415
|
+
const payload = JSON.stringify({
|
|
1416
|
+
schema: 'enigma.agent_vault.v3',
|
|
1417
|
+
version: 3,
|
|
1418
|
+
agentId: this.agentId,
|
|
1419
|
+
scope: this.scope,
|
|
1420
|
+
sessionEpoch: this._sessionEpoch,
|
|
1421
|
+
activeCommitments: this._activeCommitments,
|
|
1422
|
+
memories: Array.from(this._memories.values()),
|
|
1423
|
+
privateOpenings: Array.from(this._privateOpenings.entries()),
|
|
1424
|
+
supersessionGraph: this.contradictionGraph.exportState(),
|
|
1425
|
+
vectorBundle: this.rag.encryptAtRest(this.vaultKey),
|
|
1426
|
+
meshIdentity: serializeMeshIdentity(this.mesh.identity),
|
|
1427
|
+
federationState: this.federation.exportState(),
|
|
1428
|
+
});
|
|
1429
|
+
|
|
1430
|
+
const iv = crypto.randomBytes(12);
|
|
1431
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', this.vaultKey, iv);
|
|
1432
|
+
cipher.setAAD(VAULT_BUNDLE_AAD);
|
|
1433
|
+
const ciphertext = Buffer.concat([cipher.update(Buffer.from(payload, 'utf8')), cipher.final()]);
|
|
1434
|
+
const authTag = cipher.getAuthTag();
|
|
1435
|
+
const envelope = {
|
|
1436
|
+
format: 'enigma.vault_bundle.v1',
|
|
1437
|
+
version: 1,
|
|
1438
|
+
kdf: {
|
|
1439
|
+
algorithm: DEFAULT_KDF_SPEC.algorithm || 'scrypt',
|
|
1440
|
+
n: DEFAULT_KDF_SPEC.n || 16384,
|
|
1441
|
+
r: DEFAULT_KDF_SPEC.r || 8,
|
|
1442
|
+
p: DEFAULT_KDF_SPEC.p || 1,
|
|
1443
|
+
maxmem: DEFAULT_KDF_SPEC.maxmem,
|
|
1444
|
+
salt: this._kdfSalt.toString('hex'),
|
|
1445
|
+
},
|
|
1446
|
+
aad: 'enigma.vault_bundle.v1',
|
|
1447
|
+
ciphertext: ciphertext.toString('base64'),
|
|
1448
|
+
iv: iv.toString('hex'),
|
|
1449
|
+
authTag: authTag.toString('hex'),
|
|
1450
|
+
count: this._memories.size,
|
|
1451
|
+
};
|
|
1452
|
+
if (this._sessionEpoch !== saveEpoch) return;
|
|
1453
|
+
|
|
1454
|
+
const serialized = JSON.stringify(envelope, null, 2);
|
|
1455
|
+
let tempPath = path.join(
|
|
1456
|
+
dir,
|
|
1457
|
+
`.${path.basename(resolvedPath)}.${process.pid}.${crypto.randomBytes(8).toString('hex')}.tmp`,
|
|
1458
|
+
);
|
|
1459
|
+
let descriptor;
|
|
1460
|
+
try {
|
|
1461
|
+
descriptor = fs.openSync(tempPath, 'wx', 0o600);
|
|
1462
|
+
fs.writeFileSync(descriptor, serialized, 'utf8');
|
|
1463
|
+
fs.fsyncSync(descriptor);
|
|
1464
|
+
fs.closeSync(descriptor);
|
|
1465
|
+
descriptor = undefined;
|
|
1466
|
+
if (this._sessionEpoch !== saveEpoch) return;
|
|
1467
|
+
fs.renameSync(tempPath, resolvedPath);
|
|
1468
|
+
tempPath = null;
|
|
1469
|
+
try {
|
|
1470
|
+
const directoryDescriptor = fs.openSync(dir, 'r');
|
|
1471
|
+
try {
|
|
1472
|
+
fs.fsyncSync(directoryDescriptor);
|
|
1473
|
+
} finally {
|
|
1474
|
+
fs.closeSync(directoryDescriptor);
|
|
1475
|
+
}
|
|
1476
|
+
} catch {
|
|
1477
|
+
// Some platforms cannot open or fsync directory handles; the file itself is already synced.
|
|
1478
|
+
}
|
|
1479
|
+
} finally {
|
|
1480
|
+
if (descriptor !== undefined) {
|
|
1481
|
+
try {
|
|
1482
|
+
fs.closeSync(descriptor);
|
|
1483
|
+
} catch {
|
|
1484
|
+
// Preserve the primary persistence error.
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
if (tempPath) {
|
|
1488
|
+
try {
|
|
1489
|
+
fs.unlinkSync(tempPath);
|
|
1490
|
+
} catch (error) {
|
|
1491
|
+
if (error.code !== 'ENOENT') throw error;
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
/**
|
|
1498
|
+
* Decrypts and rehydrates memories and vector embeddings from an on-disk vault file.
|
|
1499
|
+
* @param {string} [sourcePath]
|
|
1500
|
+
*/
|
|
1501
|
+
async load(sourcePath = this.storagePath) {
|
|
1502
|
+
const loadEpoch = this._sessionEpoch;
|
|
1503
|
+
if (!sourcePath) return;
|
|
1504
|
+
const resolvedPath = path.resolve(sourcePath);
|
|
1505
|
+
if (!fs.existsSync(resolvedPath)) return;
|
|
1506
|
+
|
|
1507
|
+
let envelope;
|
|
1508
|
+
try {
|
|
1509
|
+
envelope = JSON.parse(fs.readFileSync(resolvedPath, 'utf8'));
|
|
1510
|
+
} catch (error) {
|
|
1511
|
+
throw new Error(`Failed to parse vault file at ${resolvedPath}: ${error.message}`);
|
|
1512
|
+
}
|
|
1513
|
+
if (!envelope || (!envelope.format?.startsWith('enigma') && envelope.format !== 'enigma_encrypted_vault_v2')
|
|
1514
|
+
|| !envelope.ciphertext || !envelope.iv || !envelope.authTag) {
|
|
1515
|
+
throw new Error(`Incompatible or corrupted vault format at ${resolvedPath}.`);
|
|
1516
|
+
}
|
|
1517
|
+
if (envelope.aad !== undefined && envelope.aad !== 'enigma.vault_bundle.v1') {
|
|
1518
|
+
throw new Error(`Incompatible vault envelope AAD at ${resolvedPath}.`);
|
|
1519
|
+
}
|
|
1520
|
+
if (Buffer.isBuffer(this.vaultKey) && this.vaultKey.every(byte => byte === 0)) {
|
|
1521
|
+
throw new Error(`Decryption failed for ${resolvedPath}: vault key is zeroed`);
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
const keyCandidates = [];
|
|
1525
|
+
if (this.passphrase && envelope.kdf?.salt) {
|
|
1526
|
+
const saltCandidates = [decodeEnvelopeSalt(envelope.kdf.salt)];
|
|
1527
|
+
if (/^[0-9a-fA-F]+$/.test(envelope.kdf.salt) && envelope.kdf.salt.length % 2 === 0) {
|
|
1528
|
+
const base64Salt = Buffer.from(envelope.kdf.salt, 'base64');
|
|
1529
|
+
if (base64Salt.length >= 8 && base64Salt.length <= 64
|
|
1530
|
+
&& !base64Salt.equals(saltCandidates[0])) {
|
|
1531
|
+
saltCandidates.push(base64Salt);
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
for (const salt of saltCandidates) {
|
|
1535
|
+
const derived = deriveKeysFromPassphrase(this.passphrase, salt, envelope.kdf);
|
|
1536
|
+
keyCandidates.push({ key: derived.vaultKey, salt });
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
keyCandidates.push({ key: this.vaultKey, salt: this._kdfSalt });
|
|
1540
|
+
const uniqueKeyCandidates = [];
|
|
1541
|
+
const seenKeys = new Set();
|
|
1542
|
+
for (const candidate of keyCandidates) {
|
|
1543
|
+
const keyHex = Buffer.from(candidate.key).toString('hex');
|
|
1544
|
+
if (!seenKeys.has(keyHex)) {
|
|
1545
|
+
seenKeys.add(keyHex);
|
|
1546
|
+
uniqueKeyCandidates.push(candidate);
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
const aadCandidates = envelope.aad === 'enigma.vault_bundle.v1'
|
|
1550
|
+
? [VAULT_BUNDLE_AAD]
|
|
1551
|
+
: [VAULT_BUNDLE_AAD, null];
|
|
1552
|
+
|
|
1553
|
+
let plaintextBytes;
|
|
1554
|
+
let loadedKey;
|
|
1555
|
+
let loadedSalt;
|
|
1556
|
+
let loadedWithAad = false;
|
|
1557
|
+
let decryptionError;
|
|
1558
|
+
for (const candidate of uniqueKeyCandidates) {
|
|
1559
|
+
for (const aad of aadCandidates) {
|
|
1560
|
+
try {
|
|
1561
|
+
const decipher = crypto.createDecipheriv(
|
|
1562
|
+
'aes-256-gcm',
|
|
1563
|
+
candidate.key,
|
|
1564
|
+
Buffer.from(envelope.iv, 'hex'),
|
|
1565
|
+
);
|
|
1566
|
+
if (aad) decipher.setAAD(aad);
|
|
1567
|
+
decipher.setAuthTag(Buffer.from(envelope.authTag, 'hex'));
|
|
1568
|
+
plaintextBytes = Buffer.concat([
|
|
1569
|
+
decipher.update(Buffer.from(envelope.ciphertext, 'base64')),
|
|
1570
|
+
decipher.final(),
|
|
1571
|
+
]);
|
|
1572
|
+
loadedKey = Buffer.from(candidate.key);
|
|
1573
|
+
loadedSalt = candidate.salt;
|
|
1574
|
+
loadedWithAad = Boolean(aad);
|
|
1575
|
+
break;
|
|
1576
|
+
} catch (error) {
|
|
1577
|
+
decryptionError = error;
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
if (plaintextBytes) break;
|
|
1581
|
+
}
|
|
1582
|
+
if (!plaintextBytes) {
|
|
1583
|
+
throw new Error(
|
|
1584
|
+
`Decryption failed for ${resolvedPath}: invalid passphrase/key or corrupted ciphertext. (${decryptionError?.message || 'authentication failed'})`,
|
|
1585
|
+
);
|
|
1586
|
+
}
|
|
1587
|
+
if (this._sessionEpoch !== loadEpoch) return;
|
|
1588
|
+
|
|
1589
|
+
let data;
|
|
1590
|
+
try {
|
|
1591
|
+
data = JSON.parse(plaintextBytes.toString('utf8'));
|
|
1592
|
+
} catch (error) {
|
|
1593
|
+
throw new Error(`Decrypted vault payload is invalid JSON: ${error.message}`);
|
|
1594
|
+
}
|
|
1595
|
+
if (!data || !['enigma.agent_vault.v2', 'enigma.agent_vault.v3'].includes(data.schema)
|
|
1596
|
+
|| !Array.isArray(data.memories)) {
|
|
1597
|
+
throw new Error('Decrypted vault payload has an invalid schema or memory collection');
|
|
1598
|
+
}
|
|
1599
|
+
if (data.scope !== this.scope) {
|
|
1600
|
+
throw new Error(`Decrypted vault scope "${data.scope}" does not match requested scope "${this.scope}"`);
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
const loadedEpoch = Number.isSafeInteger(data.sessionEpoch) && data.sessionEpoch >= 0
|
|
1604
|
+
? data.sessionEpoch
|
|
1605
|
+
: 0;
|
|
1606
|
+
const loadedMemories = new Map();
|
|
1607
|
+
const knownNodes = new Map();
|
|
1608
|
+
for (const value of data.memories) {
|
|
1609
|
+
if (!value || typeof value.id !== 'string' || !value.id
|
|
1610
|
+
|| typeof value.text !== 'string' || !value.text
|
|
1611
|
+
|| typeof value.scope !== 'string' || !value.scope
|
|
1612
|
+
|| typeof value.commitment !== 'string') {
|
|
1613
|
+
throw new Error('Decrypted vault payload contains an invalid memory atom');
|
|
1614
|
+
}
|
|
1615
|
+
if (loadedMemories.has(value.id)) {
|
|
1616
|
+
throw new Error(`Decrypted vault payload contains duplicate memory atom "${value.id}"`);
|
|
1617
|
+
}
|
|
1618
|
+
if (value.temporal) {
|
|
1619
|
+
createTemporalMetadata({
|
|
1620
|
+
validFrom: value.temporal.validFrom,
|
|
1621
|
+
validUntil: value.temporal.validUntil,
|
|
1622
|
+
observedAt: value.temporal.observedAt,
|
|
1623
|
+
});
|
|
1624
|
+
}
|
|
1625
|
+
const memory = {
|
|
1626
|
+
...value,
|
|
1627
|
+
sessionEpoch: loadedEpoch,
|
|
1628
|
+
metadata: value.metadata && typeof value.metadata === 'object' ? value.metadata : {},
|
|
1629
|
+
};
|
|
1630
|
+
loadedMemories.set(memory.id, memory);
|
|
1631
|
+
knownNodes.set(memory.id, memory.scope);
|
|
1632
|
+
}
|
|
1633
|
+
const loadedPrivateOpenings = new Map();
|
|
1634
|
+
if (data.privateOpenings !== undefined) {
|
|
1635
|
+
if (!Array.isArray(data.privateOpenings)) {
|
|
1636
|
+
throw new Error('Decrypted vault payload has an invalid private opening collection');
|
|
1637
|
+
}
|
|
1638
|
+
for (const entry of data.privateOpenings) {
|
|
1639
|
+
if (!Array.isArray(entry) || entry.length !== 2 || typeof entry[0] !== 'string'
|
|
1640
|
+
|| !loadedMemories.has(entry[0]) || !entry[1] || typeof entry[1] !== 'object'
|
|
1641
|
+
|| loadedPrivateOpenings.has(entry[0])) {
|
|
1642
|
+
throw new Error('Decrypted vault payload contains an invalid private opening entry');
|
|
1643
|
+
}
|
|
1644
|
+
loadedPrivateOpenings.set(entry[0], cloneStateValue(entry[1]));
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
const activeCommitments = Array.isArray(data.activeCommitments)
|
|
1649
|
+
? [...data.activeCommitments]
|
|
1650
|
+
: Array.from(loadedMemories.values())
|
|
1651
|
+
.filter(memory => !memory.revoked)
|
|
1652
|
+
.map(memory => memory.commitment);
|
|
1653
|
+
const activeCommitmentSet = new Set(activeCommitments);
|
|
1654
|
+
if (activeCommitments.some(value => typeof value !== 'string')
|
|
1655
|
+
|| activeCommitmentSet.size !== activeCommitments.length) {
|
|
1656
|
+
throw new Error('Decrypted vault payload has invalid active commitments');
|
|
1657
|
+
}
|
|
1658
|
+
const commitmentOwners = new Map();
|
|
1659
|
+
for (const memory of loadedMemories.values()) {
|
|
1660
|
+
if (commitmentOwners.has(memory.commitment)) {
|
|
1661
|
+
throw new Error(`Decrypted vault payload reuses commitment "${memory.commitment}"`);
|
|
1662
|
+
}
|
|
1663
|
+
commitmentOwners.set(memory.commitment, memory);
|
|
1664
|
+
}
|
|
1665
|
+
for (const commitment of activeCommitments) {
|
|
1666
|
+
const owner = commitmentOwners.get(commitment);
|
|
1667
|
+
if (!owner) {
|
|
1668
|
+
throw new Error(`Decrypted vault payload references unknown active commitment "${commitment}"`);
|
|
1669
|
+
}
|
|
1670
|
+
if (owner.revoked) {
|
|
1671
|
+
throw new Error(`Decrypted vault payload marks revoked commitment "${commitment}" as active`);
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
const loadedGraph = new ContradictionGraph();
|
|
1676
|
+
if (data.supersessionGraph) {
|
|
1677
|
+
loadedGraph.importState(data.supersessionGraph, knownNodes);
|
|
1678
|
+
} else {
|
|
1679
|
+
for (const [id, scope] of knownNodes) loadedGraph.registerNode(id, { scope });
|
|
1680
|
+
}
|
|
1681
|
+
const loadedRag = data.vectorBundle
|
|
1682
|
+
? EnigmaRagClient.fromEncryptedBundle(data.vectorBundle, loadedKey)
|
|
1683
|
+
: new EnigmaRagClient();
|
|
1684
|
+
for (const id of Array.from(loadedRag.store.documents.keys())) {
|
|
1685
|
+
const memory = loadedMemories.get(id);
|
|
1686
|
+
if (!memory || memory.revoked || !activeCommitmentSet.has(memory.commitment)) {
|
|
1687
|
+
loadedRag.store.delete(id);
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
for (const memory of loadedMemories.values()) {
|
|
1691
|
+
if (activeCommitmentSet.has(memory.commitment) && !loadedRag.store.documents.has(memory.id)) {
|
|
1692
|
+
throw new Error(`Decrypted vault payload is missing the vector record for active memory "${memory.id}"`);
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
let loadedMesh = this.mesh;
|
|
1697
|
+
if (data.meshIdentity) {
|
|
1698
|
+
const identity = deserializeMeshIdentity(data.meshIdentity, this.scope);
|
|
1699
|
+
if (this._meshIdentitySupplied) {
|
|
1700
|
+
const sameSigningKey = crypto.timingSafeEqual(Buffer.from(this.mesh.identity.publicKey), identity.publicKey);
|
|
1701
|
+
const sameEncryptionKey = crypto.timingSafeEqual(
|
|
1702
|
+
Buffer.from(this.mesh.identity.encryptionPublicKey),
|
|
1703
|
+
identity.encryptionPublicKey,
|
|
1704
|
+
);
|
|
1705
|
+
if (!sameSigningKey || !sameEncryptionKey) {
|
|
1706
|
+
throw new Error('Decrypted vault mesh identity does not match the supplied local mesh identity');
|
|
1707
|
+
}
|
|
1708
|
+
} else {
|
|
1709
|
+
loadedMesh = new EnigmaMeshClient({
|
|
1710
|
+
scope: this.scope,
|
|
1711
|
+
identity,
|
|
1712
|
+
nodeId: typeof data.agentId === 'string' && data.agentId ? data.agentId : this.agentId,
|
|
1713
|
+
});
|
|
1714
|
+
}
|
|
1715
|
+
} else if (data.federationState) {
|
|
1716
|
+
throw new Error('Decrypted vault federation state is missing its bound mesh identity');
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
const federationOwner = { mesh: loadedMesh };
|
|
1720
|
+
const loadedFederation = new EnigmaFederationRouter({
|
|
1721
|
+
enigma: federationOwner,
|
|
1722
|
+
state: data.federationState,
|
|
1723
|
+
onStateMutation: async () => {
|
|
1724
|
+
if (this.storagePath) await this.save();
|
|
1725
|
+
},
|
|
1726
|
+
});
|
|
1727
|
+
loadedFederation.enigma = this;
|
|
1728
|
+
|
|
1729
|
+
if (this._sessionEpoch !== loadEpoch) return;
|
|
1730
|
+
this._memories = loadedMemories;
|
|
1731
|
+
this._privateOpenings = loadedPrivateOpenings;
|
|
1732
|
+
this._activeCommitments = activeCommitments;
|
|
1733
|
+
this._activeCommitmentSet = activeCommitmentSet;
|
|
1734
|
+
this._currentRoot = new MerkleSet(activeCommitments).root();
|
|
1735
|
+
this.contradictionGraph = loadedGraph;
|
|
1736
|
+
this.rag = loadedRag;
|
|
1737
|
+
this.mesh = loadedMesh;
|
|
1738
|
+
this.federation = loadedFederation;
|
|
1739
|
+
this.vaultKey = loadedKey;
|
|
1740
|
+
if (loadedSalt) this._kdfSalt = Buffer.from(loadedSalt);
|
|
1741
|
+
if (typeof data.agentId === 'string' && data.agentId) this.agentId = data.agentId;
|
|
1742
|
+
this._sessionEpoch = loadedEpoch;
|
|
1743
|
+
|
|
1744
|
+
const needsMigration = envelope.aad !== 'enigma.vault_bundle.v1'
|
|
1745
|
+
|| !loadedWithAad
|
|
1746
|
+
|| !data.meshIdentity
|
|
1747
|
+
|| !data.federationState;
|
|
1748
|
+
if (needsMigration) await this.save(resolvedPath);
|
|
1749
|
+
}
|
|
1750
|
+
/**
|
|
1751
|
+
* Returns current local agent sovereign memory metrics and active status.
|
|
1752
|
+
* @returns {Object}
|
|
1753
|
+
*/
|
|
1754
|
+
status() {
|
|
1755
|
+
return {
|
|
1756
|
+
agentId: this.agentId,
|
|
1757
|
+
scope: this.scope,
|
|
1758
|
+
memoryCount: this._memories.size,
|
|
1759
|
+
vectorCount: this.rag.store.documents.size,
|
|
1760
|
+
storagePath: this.storagePath,
|
|
1761
|
+
meshConnected: this.mesh.status().peersCount > 0,
|
|
1762
|
+
activeRules: Array.from(this._memories.values()).filter(m => m.type === 'policy').length,
|
|
1763
|
+
contradictionsTracked: this.contradictionGraph.edges.size,
|
|
1764
|
+
};
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
// Re-export intelligence sub-modules and cryptographic receipt utilities
|
|
1769
|
+
export * from './intelligence/index.js';
|
|
1770
|
+
export * from './federation/index.js';
|
|
1771
|
+
export * from './rag-client.js';
|
|
1772
|
+
export * from './mesh-client.js';
|
|
1773
|
+
export * from './market-client.js';
|
|
1774
|
+
export * from './swarm-orchestrator.js';
|
|
1775
|
+
export * from './middleware/index.js';
|
|
1776
|
+
export * from '../../snapcompact/src/index.js';
|
|
1777
|
+
export {
|
|
1778
|
+
createMemoryReceipt,
|
|
1779
|
+
verifyMemoryReceipt,
|
|
1780
|
+
computeHidingCommitment,
|
|
1781
|
+
verifyCommitmentOpening,
|
|
1782
|
+
MEMORY_RECEIPT_SCHEMA,
|
|
1783
|
+
createErasureReceipt,
|
|
1784
|
+
verifyErasureReceipt,
|
|
1785
|
+
createErasureAttestation,
|
|
1786
|
+
verifyErasureAttestation,
|
|
1787
|
+
secureZeroizeBuffer,
|
|
1788
|
+
ERASURE_RECEIPT_SCHEMA,
|
|
1789
|
+
ERASURE_ATTESTATION_SCHEMA,
|
|
1790
|
+
createBlindedMemoryGrant,
|
|
1791
|
+
verifyBlindedMemoryGrant,
|
|
1792
|
+
BLINDED_GRANT_SCHEMA,
|
|
1793
|
+
createFederatedQueryRequest,
|
|
1794
|
+
verifyFederatedQueryRequest,
|
|
1795
|
+
FEDERATED_QUERY_REQUEST_SCHEMA,
|
|
1796
|
+
};
|