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.
Files changed (276) hide show
  1. package/README.md +76 -24
  2. package/apps/cli/bin/enigma-desktop.mjs +140 -0
  3. package/apps/cli/bin/enigma-terminal.mjs +78 -0
  4. package/apps/cli/bin/enigma.mjs +1923 -285
  5. package/apps/desktop/electron-main.cjs +217 -0
  6. package/apps/desktop/package.json +12 -0
  7. package/apps/desktop/src/app.js +264 -7
  8. package/apps/desktop/src/index.html +3514 -1373
  9. package/apps/desktop/src/launch-electron.mjs +51 -0
  10. package/apps/desktop/src/server.mjs +2914 -0
  11. package/apps/desktop/src/styles.css +2972 -260
  12. package/apps/desktop/src/zk-browser-prove.mjs +53 -0
  13. package/apps/desktop/src/zk-state.mjs +1789 -0
  14. package/apps/gateway/bin/enigma-gateway.mjs +102 -5
  15. package/apps/gateway/src/server.mjs +271 -8
  16. package/apps/ios/EnigmaCore/Package.swift +12 -0
  17. package/apps/ios/EnigmaCore/Sources/EnigmaCore/EnigmaAPIClient.swift +227 -0
  18. package/apps/ios/EnigmaCore/Sources/EnigmaCore/Models.swift +278 -0
  19. package/apps/ios/EnigmaCore/Sources/EnigmaCore/PKCE.swift +96 -0
  20. package/apps/ios/EnigmaCore/Sources/EnigmaCore/PrivacyMinimizer.swift +187 -0
  21. package/apps/ios/EnigmaCore/Sources/EnigmaCore/ToolModels.swift +129 -0
  22. package/apps/ios/EnigmaCore/Tests/EnigmaCoreTests/EnigmaCoreTests.swift +42 -0
  23. package/apps/ios/EnigmaIOS/Enigma/AppModel.swift +346 -0
  24. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AccentColor.colorset/Contents.json +12 -0
  25. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AppIcon.appiconset/Contents.json +11 -0
  26. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AppIcon.appiconset/EnigmaAppIcon.png +0 -0
  27. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/Contents.json +3 -0
  28. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/LaunchBackground.colorset/Contents.json +12 -0
  29. package/apps/ios/EnigmaIOS/Enigma/ChatView.swift +181 -0
  30. package/apps/ios/EnigmaIOS/Enigma/CouncilView.swift +78 -0
  31. package/apps/ios/EnigmaIOS/Enigma/CreateView.swift +152 -0
  32. package/apps/ios/EnigmaIOS/Enigma/EnigmaApp.swift +52 -0
  33. package/apps/ios/EnigmaIOS/Enigma/Info.plist +52 -0
  34. package/apps/ios/EnigmaIOS/Enigma/NaturalLanguagePrivacyTagger.swift +26 -0
  35. package/apps/ios/EnigmaIOS/Enigma/OAuthClient.swift +321 -0
  36. package/apps/ios/EnigmaIOS/Enigma/OnboardingView.swift +105 -0
  37. package/apps/ios/EnigmaIOS/Enigma/PrivateVaultView.swift +275 -0
  38. package/apps/ios/EnigmaIOS/Enigma/SecureStore.swift +76 -0
  39. package/apps/ios/EnigmaIOS/Enigma/SettingsView.swift +60 -0
  40. package/apps/ios/EnigmaIOS/Enigma/Theme.swift +80 -0
  41. package/apps/ios/EnigmaIOS/EnigmaIOS.xcodeproj/project.pbxproj +211 -0
  42. package/apps/ios/EnigmaIOS/EnigmaIOS.xcodeproj/xcshareddata/xcschemes/Enigma.xcscheme +23 -0
  43. package/apps/native-host/README.md +19 -8
  44. package/apps/native-host/bin/enigma-native-host.mjs +229 -13
  45. package/apps/relay/bin/enigma-relay.mjs +103 -5
  46. package/apps/relay/src/federation-runtime.mjs +618 -0
  47. package/apps/relay/src/server.mjs +310 -9
  48. package/apps/verifier/bin/enigma-verify.mjs +327 -11
  49. package/cortex-v3/circuits/build/intent_vk_bytes.json +35 -0
  50. package/cortex-v3/circuits/build/sale_vk_bytes.json +35 -0
  51. package/cortex-v3/circuits/build/vk_bytes.json +32 -0
  52. package/cortex-v3/proving-assets.json +64 -0
  53. package/cortex-v3/zk/BUILD-CONTRACT.md +87 -0
  54. package/cortex-v3/zk/action-transition-vk.json +119 -0
  55. package/cortex-v3/zk/alias-adversarial.test.mjs +220 -0
  56. package/cortex-v3/zk/groth16-verify-child.mjs +17 -0
  57. package/cortex-v3/zk/intent-witness.mjs +365 -0
  58. package/cortex-v3/zk/intent-witness.test.mjs +485 -0
  59. package/cortex-v3/zk/proving-assets.mjs +203 -0
  60. package/cortex-v3/zk/sale-witness.mjs +783 -0
  61. package/cortex-v3/zk/sale-witness.test.mjs +784 -0
  62. package/cortex-v3/zk/sealed-sale-release-vk.json +119 -0
  63. package/cortex-v3/zk/settlement-evidence.mjs +722 -0
  64. package/cortex-v3/zk/setup-intent.mjs +688 -0
  65. package/cortex-v3/zk/setup-sale.mjs +666 -0
  66. package/cortex-v3/zk/setup.mjs +594 -0
  67. package/cortex-v3/zk/witness.mjs +184 -0
  68. package/cortex-v3/zk/zk-codec.mjs +232 -0
  69. package/cortex-v3/zk/zk-codec.test.mjs +293 -0
  70. package/cortex-v3/zk/zk-settle.mjs +370 -0
  71. package/cortex-v3/zk/zk-tree.mjs +256 -0
  72. package/cortex-v3/zk/zk-tree.test.mjs +419 -0
  73. package/deploy/docker-compose.local-production-simulation.yml +36 -0
  74. package/docs/browser-extension-install.md +8 -6
  75. package/docs/client-connectors.md +15 -11
  76. package/docs/developer-ecosystem.md +15 -13
  77. package/docs/enigma-memory-ready-conformance.md +11 -9
  78. package/docs/install-anywhere.md +61 -28
  79. package/docs/installers-and-desktop.md +8 -7
  80. package/docs/novelty-invention-candidates.md +161 -161
  81. package/docs/proof-network-claim-boundaries.md +320 -318
  82. package/examples/01-quickstart-agent/index.mjs +49 -0
  83. package/examples/01_agent_memory_quickstart.mjs +57 -0
  84. package/examples/02-multi-agent-swarm/index.mjs +57 -0
  85. package/examples/02_cross_model_passport.mjs +64 -0
  86. package/examples/03-langchain-memory/index.mjs +41 -0
  87. package/examples/03_poseidon_commitment_verification.mjs +71 -0
  88. package/examples/04-python-trading-agent/trader.py +49 -0
  89. package/examples/README.md +27 -0
  90. package/examples/ci/github-actions.yml +7 -2
  91. package/package.json +142 -11
  92. package/packages/adapters/PACKAGE_CONTRACT.md +1 -1
  93. package/packages/connectors/src/index.js +196 -4
  94. package/packages/connectors/swarm-router.mjs +168 -0
  95. package/packages/core/src/index.js +248 -1
  96. package/packages/core/src/version.mjs +7 -0
  97. package/packages/dev-tools/package.json +19 -0
  98. package/packages/dev-tools/src/index.js +4 -0
  99. package/packages/dev-tools/src/memory-benchmark-suite.js +112 -0
  100. package/packages/dev-tools/src/swarm-simulator.js +101 -0
  101. package/packages/dev-tools/src/vault-inspector.js +114 -0
  102. package/packages/dev-tools/src/vector-benchmark.js +100 -0
  103. package/packages/developer-platform/src/access-credentials.js +341 -0
  104. package/packages/developer-platform/src/http.js +132 -0
  105. package/packages/developer-platform/src/index.js +4 -0
  106. package/packages/developer-platform/src/usage-http.js +60 -0
  107. package/packages/developer-platform/src/usage.js +295 -0
  108. package/packages/enclave-runtime/attestation.mjs +159 -0
  109. package/packages/enclave-runtime/index.mjs +47 -0
  110. package/packages/enclave-runtime/session-manager.mjs +253 -0
  111. package/packages/enclave-runtime/zeroization-proof.mjs +227 -0
  112. package/packages/enigma-reflex/package.json +14 -0
  113. package/packages/enigma-reflex/src/index.js +204 -0
  114. package/packages/enigma-reflex/training/generate-dataset.mjs +40 -0
  115. package/packages/enigma-reflex/training/requirements.txt +8 -0
  116. package/packages/enigma-reflex/training/train.py +314 -0
  117. package/packages/enigma-weave/LICENSE +22 -0
  118. package/packages/enigma-weave/UPSTREAM.json +21 -0
  119. package/packages/enigma-weave/package.json +14 -0
  120. package/packages/enigma-weave/src/index.js +286 -0
  121. package/packages/hosted-cloud/src/index.js +80 -5
  122. package/packages/importers/src/index.js +432 -0
  123. package/packages/inference-runtime/src/browser.js +401 -0
  124. package/packages/inference-runtime/src/chat.js +265 -0
  125. package/packages/inference-runtime/src/code.js +407 -0
  126. package/packages/inference-runtime/src/contracts.js +162 -0
  127. package/packages/inference-runtime/src/http.js +232 -0
  128. package/packages/inference-runtime/src/image.js +186 -0
  129. package/packages/inference-runtime/src/index.js +10 -0
  130. package/packages/inference-runtime/src/model-router.js +320 -0
  131. package/packages/inference-runtime/src/platform.js +125 -0
  132. package/packages/inference-runtime/src/privacy.js +400 -0
  133. package/packages/inference-runtime/src/video.js +253 -0
  134. package/packages/mcp-server/README.md +22 -6
  135. package/packages/mcp-server/bin/enigma-mcp.mjs +2 -1
  136. package/packages/mcp-server/src/index.js +1418 -105
  137. package/packages/mcp-server/src/oauth.js +561 -0
  138. package/packages/mcp-server/src/private-handoff.js +84 -0
  139. package/packages/mcp-server/src/remote-http.js +273 -0
  140. package/packages/mcp-server/src/remote-policy.js +72 -0
  141. package/packages/mcp-server/swarm-bridge.mjs +361 -0
  142. package/packages/mesh/index.d.ts +283 -0
  143. package/packages/mesh/package.json +23 -0
  144. package/packages/mesh/src/crypto.js +189 -0
  145. package/packages/mesh/src/federation-packets.js +353 -0
  146. package/packages/mesh/src/gossip.js +311 -0
  147. package/packages/mesh/src/index.js +6 -0
  148. package/packages/mesh/src/protocol.js +255 -0
  149. package/packages/mesh/src/router.js +279 -0
  150. package/packages/mesh/src/transport.js +306 -0
  151. package/packages/passport/src/index.js +426 -1
  152. package/packages/private-economy/src/credits-http.js +100 -0
  153. package/packages/private-economy/src/credits.js +447 -0
  154. package/packages/private-economy/src/index.js +5 -0
  155. package/packages/private-economy/src/payments-http.js +120 -0
  156. package/packages/private-economy/src/payments.js +509 -0
  157. package/packages/private-economy/src/x402.js +346 -0
  158. package/packages/proof-network/PACKAGE_CONTRACT.md +21 -0
  159. package/packages/rag/index.d.ts +182 -0
  160. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/THIRD_PARTY_LICENSES.txt +207 -0
  161. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/config.json +25 -0
  162. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/onnx/model_quantized.onnx +0 -0
  163. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/sha256-manifest.json +28 -0
  164. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/tokenizer.json +30686 -0
  165. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/tokenizer_config.json +15 -0
  166. package/packages/rag/package.json +27 -0
  167. package/packages/rag/src/blinded-search.js +109 -0
  168. package/packages/rag/src/bm25.js +169 -0
  169. package/packages/rag/src/embeddings.js +459 -0
  170. package/packages/rag/src/hybrid.js +76 -0
  171. package/packages/rag/src/index.js +38 -0
  172. package/packages/rag/src/reranker.js +61 -0
  173. package/packages/rag/src/research.js +107 -0
  174. package/packages/rag/src/vector-store.js +430 -0
  175. package/packages/rag/src/verify-model-artifacts.mjs +4 -0
  176. package/packages/sdk/index.d.ts +760 -0
  177. package/packages/sdk/package.json +33 -0
  178. package/packages/sdk/python/README.md +24 -0
  179. package/packages/sdk/python/enigma_sdk.py +250 -0
  180. package/packages/sdk/python/pyproject.toml +34 -0
  181. package/packages/sdk/python/requirements.txt +1 -0
  182. package/packages/sdk/python/setup.py +20 -0
  183. package/packages/sdk/src/federation/capability-grant.js +389 -0
  184. package/packages/sdk/src/federation/federation-router.js +360 -0
  185. package/packages/sdk/src/federation/ghostmesh-bridge.js +497 -0
  186. package/packages/sdk/src/federation/index.js +3 -0
  187. package/packages/sdk/src/index.js +1796 -0
  188. package/packages/sdk/src/intelligence/contradiction.js +337 -0
  189. package/packages/sdk/src/intelligence/decision-engine.js +155 -0
  190. package/packages/sdk/src/intelligence/index.js +4 -0
  191. package/packages/sdk/src/intelligence/ontology.js +122 -0
  192. package/packages/sdk/src/intelligence/temporal.js +123 -0
  193. package/packages/sdk/src/market-client.js +142 -0
  194. package/packages/sdk/src/mesh-client.js +110 -0
  195. package/packages/sdk/src/middleware/index.js +3 -0
  196. package/packages/sdk/src/middleware/langchain.js +159 -0
  197. package/packages/sdk/src/middleware/llamaindex.js +101 -0
  198. package/packages/sdk/src/middleware/vercel-ai.js +112 -0
  199. package/packages/sdk/src/rag-client.js +85 -0
  200. package/packages/sdk/src/swarm-orchestrator.js +260 -0
  201. package/packages/settlement/PACKAGE_CONTRACT.md +1 -1
  202. package/packages/snapcompact/THIRD_PARTY_LICENSES.txt +40 -0
  203. package/packages/snapcompact/assets/8x13-latin1.bdf +3837 -0
  204. package/packages/snapcompact/index.d.ts +284 -0
  205. package/packages/snapcompact/package.json +25 -0
  206. package/packages/snapcompact/src/index.js +716 -0
  207. package/packages/storage/PACKAGE_CONTRACT.md +1 -1
  208. package/packages/terminal-console/animations.mjs +240 -0
  209. package/packages/terminal-console/auto-anchor.mjs +220 -0
  210. package/packages/terminal-console/banner.mjs +91 -0
  211. package/packages/terminal-console/commands.mjs +459 -0
  212. package/packages/terminal-console/delegation.mjs +152 -0
  213. package/packages/terminal-console/index.mjs +5 -0
  214. package/packages/terminal-console/outbox.mjs +143 -0
  215. package/packages/terminal-console/phantom-bridge.mjs +637 -0
  216. package/packages/terminal-console/repl.mjs +136 -0
  217. package/packages/terminal-console/signer-store.mjs +130 -0
  218. package/packages/terminal-console/solana-rpc.mjs +214 -0
  219. package/packages/terminal-console/solana-transport.mjs +189 -0
  220. package/packages/terminal-tui/dashboard.mjs +214 -0
  221. package/packages/terminal-tui/index.mjs +28 -0
  222. package/packages/terminal-tui/merkle-tree-renderer.mjs +268 -0
  223. package/packages/terminal-tui/telemetry-hud.mjs +137 -0
  224. package/packages/vault/index.d.ts +449 -0
  225. package/packages/vault/package.json +27 -0
  226. package/packages/vault/src/e2ee.mjs +393 -0
  227. package/packages/vault/src/enclave.js +481 -0
  228. package/packages/vault/src/erasure.js +207 -0
  229. package/packages/vault/src/index.js +1018 -155
  230. package/packages/vault/src/persistence.js +307 -0
  231. package/packages/vault/src/poseidon.js +354 -0
  232. package/packages/vault/src/receipt.js +459 -0
  233. package/scripts/benchmark-optical-context.mjs +166 -0
  234. package/scripts/bootstrap-enigma.mjs +502 -0
  235. package/scripts/build-edge-backend-workers.mjs +20 -5
  236. package/scripts/build-goal-completion-audit.mjs +72 -25
  237. package/scripts/build-hosted-api-key-lifecycle.mjs +26 -8
  238. package/scripts/build-hosted-customer-lifecycle.mjs +20 -3
  239. package/scripts/build-hosted-probe-worker.mjs +19 -4
  240. package/scripts/build-installer-assets.mjs +41 -21
  241. package/scripts/build-operator-evidence-starter.mjs +59 -1
  242. package/scripts/build-production-backend-env-kit.mjs +2 -0
  243. package/scripts/build-production-unblocker.mjs +3 -0
  244. package/scripts/check.mjs +17 -3
  245. package/scripts/collect-hosted-backend-live-evidence.mjs +49 -12
  246. package/scripts/install-enigma-local.mjs +18 -5
  247. package/scripts/release-audit.mjs +65 -109
  248. package/scripts/release-provenance.mjs +6 -0
  249. package/scripts/run-backend-readiness-smoke.mjs +112 -10
  250. package/scripts/scan-secrets.mjs +1 -0
  251. package/scripts/simulate-production-env.mjs +7 -2
  252. package/scripts/validate-hosted-backend-live.mjs +112 -1
  253. package/specs/antibody-pack-v1.schema.json +95 -0
  254. package/specs/antigen-envelope-v1.schema.json +81 -0
  255. package/specs/boundary-manifest-v1.schema.json +35 -35
  256. package/specs/capsule-v1.schema.json +55 -55
  257. package/specs/claim-boundary-manifest-v1.schema.json +22 -22
  258. package/specs/claim-ledger-v1.schema.json +291 -0
  259. package/specs/context-passport-v1.schema.json +59 -0
  260. package/specs/deletion-tombstone-v1.schema.json +26 -26
  261. package/specs/evidence-packet-v1.schema.json +177 -0
  262. package/specs/hosted-backend-live-evidence-v1.schema.json +72 -3
  263. package/specs/immune-scan-report-v1.schema.json +112 -0
  264. package/specs/lifecycle-receipt-log-v1.schema.json +67 -0
  265. package/specs/memory-atom-v1.schema.json +59 -0
  266. package/specs/memory-event-v1.schema.json +42 -42
  267. package/specs/passport-v1.schema.json +50 -50
  268. package/specs/proof-of-non-use-v1.schema.json +65 -0
  269. package/specs/quarantine-record-v1.schema.json +126 -0
  270. package/specs/receipt-v1.schema.json +61 -61
  271. package/specs/state-checkpoint-v1.schema.json +37 -37
  272. package/specs/trust-bundle-v1.schema.json +56 -56
  273. package/specs/trust-card-v1.schema.json +119 -0
  274. package/docs/proof-network-launch-plan.md +0 -421
  275. package/packages/metering/PACKAGE_CONTRACT.md +0 -20
  276. package/scripts/build-ai-orchestration-plan.mjs +0 -248
@@ -1,7 +1,28 @@
1
- import { access, mkdir, readFile, writeFile } from 'node:fs/promises';
1
+ import { createHmac, randomBytes } from 'node:crypto';
2
+ import { access, readFile } from 'node:fs/promises';
3
+ import { ENIGMA_VERSION } from '../../core/src/version.mjs';
2
4
  import { createInterface } from 'node:readline';
3
- import { dirname, resolve } from 'node:path';
4
- import { createVault, remember, recall, deleteMemory, exportBundle } from '../../vault/src/index.js';
5
+ import { resolve } from 'node:path';
6
+ import {
7
+ createVault,
8
+ remember,
9
+ recall,
10
+ deleteMemory,
11
+ exportBundle,
12
+ importBundle,
13
+ rotateContentKey,
14
+ sealContentKeyEpoch,
15
+ resetVaultContentKeys,
16
+ SimulatedEnclaveProvider,
17
+ AttestationVerifier,
18
+ createPoseidonCommitment,
19
+ verifyPoseidonCommitment,
20
+ } from '../../vault/src/index.js';
21
+ import {
22
+ readJsonFileWithRevision,
23
+ withBundleFileLock,
24
+ writeJsonFileAtomic,
25
+ } from '../../vault/src/persistence.js';
5
26
  import { createPassport, compileContextPack } from '../../passport/src/index.js';
6
27
  import { verifyBundle } from '../../../apps/verifier/bin/enigma-verify.mjs';
7
28
  import { aggregateUsageEvents, createUsageEvent } from '../../metering/src/index.js';
@@ -13,11 +34,126 @@ import {
13
34
  createSettlementBatch,
14
35
  verifyServiceSettlementReceipt,
15
36
  } from '../../settlement/src/index.js';
37
+ import {
38
+ assertImmuneIngressPublicSafe,
39
+ createImmuneIngressReport,
40
+ } from '../../importers/src/index.js';
41
+ import {
42
+ enigma_cluster_memories as routeClusterMemories,
43
+ enigma_graph_query as routeGraphQuery,
44
+ enigma_swarm_register_agent as routeSwarmRegisterAgent,
45
+ enigma_swarm_sync as routeSwarmSync,
46
+ } from '../../connectors/swarm-router.mjs';
47
+ import {
48
+ DEFAULT_DESKTOP_ORIGIN,
49
+ PRIVATE_SESSION_RESOURCE_URI,
50
+ createPrivateSessionHttpUrl,
51
+ createPrivateSessionProtocolUrl,
52
+ validateLoopbackDesktopOrigin,
53
+ validatePrivateHandoffIntent,
54
+ validatePrivateHandoffModel,
55
+ } from './private-handoff.js';
56
+ import { assertRemoteToolPolicyCoverage, remoteToolPolicy, requiredScopesForMcpPayload } from './remote-policy.js';
57
+
58
+
59
+ const defaultEnclave = new SimulatedEnclaveProvider({ simulationLabel: 'enigma_mcp_runtime' });
16
60
 
17
61
  const DEFAULT_BUNDLE = '.enigma/bundle.json';
18
62
  const JSONRPC_VERSION = '2.0';
19
63
  const MCP_PROTOCOL_VERSION = '2024-11-05';
20
- const SERVER_INFO = Object.freeze({ name: 'enigma-mcp-server', version: '0.1.18' });
64
+ const SERVER_INFO = Object.freeze({ name: 'enigma-mcp-server', version: ENIGMA_VERSION });
65
+ const UNLOCKED_VAULT_CACHE_LIMIT = 8;
66
+ const unlockedVaultCache = new Map();
67
+ const cacheAuthenticationKey = randomBytes(32);
68
+ let configuredCheckpointProvider = null;
69
+ let configuredInferenceRuntime = null;
70
+ let configuredX402Client = null;
71
+ const REMOTE_SECURE_VAULT = Symbol('enigma.remote_secure_vault');
72
+
73
+ function enigmaDesktopOrigin() {
74
+ return validateLoopbackDesktopOrigin(process.env.ENIGMA_DESKTOP_URL || DEFAULT_DESKTOP_ORIGIN);
75
+ }
76
+
77
+ function privateSessionUrl(input = {}) {
78
+ return process.env.ENIGMA_DESKTOP_URL
79
+ ? createPrivateSessionHttpUrl(input, enigmaDesktopOrigin())
80
+ : createPrivateSessionProtocolUrl(input);
81
+ }
82
+
83
+ function rejectSensitiveHandoffIntent(intent) {
84
+ try {
85
+ validatePrivateHandoffIntent(intent);
86
+ } catch (error) {
87
+ throw invalidParams(error.message);
88
+ }
89
+ }
90
+
91
+ export function configureVaultCheckpointProvider(provider = null) {
92
+ if (provider !== null && (typeof provider?.load !== 'function' || typeof provider?.advance !== 'function')) {
93
+ throw new TypeError('checkpoint provider requires load and advance functions');
94
+ }
95
+ configuredCheckpointProvider = provider;
96
+ }
97
+
98
+ export function configureMcpInferenceRuntime(runtime = null) {
99
+ const hasChat = typeof runtime?.chat?.complete === 'function';
100
+ const hasImages = typeof runtime?.images?.generate === 'function';
101
+ if (runtime !== null && !hasChat && !hasImages) {
102
+ throw new TypeError('MCP inference runtime requires chat.complete() or images.generate()');
103
+ }
104
+ configuredInferenceRuntime = runtime;
105
+ }
106
+
107
+ export function configureMcpX402Client(client = null) {
108
+ if (client !== null && typeof client !== 'function') throw new TypeError('MCP x402 client must be a function');
109
+ configuredX402Client = client;
110
+ }
111
+
112
+ function credentialCacheTag(credentials) {
113
+ return createHmac('sha256', cacheAuthenticationKey)
114
+ .update(credentials.passphrase ?? (credentials.insecureDevMode ? 'insecure-dev' : 'missing'))
115
+ .digest('hex');
116
+ }
117
+
118
+ function zeroizeCachedVault(entry) {
119
+ const vault = entry?.vault;
120
+ if (!vault) return;
121
+ for (const epoch of vault.keyEpochs?.values?.() ?? []) epoch.key?.fill?.(0);
122
+ vault.addressKey?.fill?.(0);
123
+ for (const record of vault.memories?.values?.() ?? []) {
124
+ if (Object.prototype.hasOwnProperty.call(record, 'plaintext')) delete record.plaintext;
125
+ }
126
+ }
127
+
128
+ function invalidateCachedVault(path) {
129
+ const key = resolve(String(path));
130
+ const prior = unlockedVaultCache.get(key);
131
+ unlockedVaultCache.delete(key);
132
+ zeroizeCachedVault(prior);
133
+ }
134
+
135
+ function cacheUnlockedVault(path, entry) {
136
+ const key = resolve(String(path));
137
+ const prior = unlockedVaultCache.get(key);
138
+ if (prior && prior.vault !== entry.vault) zeroizeCachedVault(prior);
139
+ unlockedVaultCache.delete(key);
140
+ unlockedVaultCache.set(key, entry);
141
+ while (unlockedVaultCache.size > UNLOCKED_VAULT_CACHE_LIMIT) {
142
+ const oldestKey = unlockedVaultCache.keys().next().value;
143
+ const oldest = unlockedVaultCache.get(oldestKey);
144
+ unlockedVaultCache.delete(oldestKey);
145
+ zeroizeCachedVault(oldest);
146
+ }
147
+ }
148
+ export function clearUnlockedVaultCache() {
149
+ for (const entry of unlockedVaultCache.values()) zeroizeCachedVault(entry);
150
+ unlockedVaultCache.clear();
151
+ }
152
+
153
+ process.once('exit', () => {
154
+ clearUnlockedVaultCache();
155
+ cacheAuthenticationKey.fill(0);
156
+ });
21
157
  const JSON_RPC_ID_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
22
158
  const JSON_RPC_ERROR = Object.freeze({
23
159
  INVALID_REQUEST: -32600,
@@ -121,28 +257,131 @@ function optionalObjectArray(object, key, context) {
121
257
  }
122
258
  }
123
259
 
260
+ function optionalIntegerRange(object, key, context, minimum, maximum) {
261
+ if (object[key] === undefined) return;
262
+ if (!Number.isInteger(object[key]) || object[key] < minimum || object[key] > maximum) {
263
+ throw invalidParams(`${context} ${key} must be an integer from ${minimum} through ${maximum}.`);
264
+ }
265
+ }
266
+
267
+ function optionalPermissionMask(object, key, context) {
268
+ const value = object[key];
269
+ if (value === undefined) return;
270
+ const validNumber = Number.isInteger(value) && value >= 0 && value <= 63;
271
+ const validString = typeof value === 'string';
272
+ const validArray = Array.isArray(value) && value.every((item) => typeof item === 'string');
273
+ if (!validNumber && !validString && !validArray) {
274
+ throw invalidParams(`${context} ${key} must be a permission bitmask, string, or array of strings.`);
275
+ }
276
+ }
277
+
278
+ function maximumStringLength(object, key, context, maximum) {
279
+ if (typeof object[key] === 'string' && object[key].length > maximum) {
280
+ throw invalidParams(`${context} ${key} exceeds ${maximum} characters.`);
281
+ }
282
+ }
283
+
284
+ function validateCompletionMessages(messages, context) {
285
+ if (!Array.isArray(messages) || messages.length < 1 || messages.length > 128) {
286
+ throw invalidParams(`${context} messages must contain one through 128 messages.`);
287
+ }
288
+ for (const [index, message] of messages.entries()) {
289
+ if (!isPlainObject(message)) throw invalidParams(`${context} messages[${index}] must be an object.`);
290
+ rejectAdditionalProperties(message, new Set(['role', 'content']), `${context} messages[${index}]`);
291
+ requireString(message, 'role', `${context} messages[${index}]`);
292
+ requireString(message, 'content', `${context} messages[${index}]`);
293
+ if (!['system', 'user', 'assistant', 'tool'].includes(message.role)) {
294
+ throw invalidParams(`${context} messages[${index}].role is invalid.`);
295
+ }
296
+ if (message.content.length > 100_000) throw invalidParams(`${context} messages[${index}].content exceeds 100000 characters.`);
297
+ }
298
+ }
299
+
124
300
  function validateToolArguments(name, args) {
125
301
  if (!isPlainObject(args)) throw invalidParams('tools/call arguments must be an object.');
126
302
  switch (name) {
303
+ case 'enigma_private_handoff_health':
304
+ case 'enigma_routing_contract':
305
+ rejectAdditionalProperties(args, new Set(), name);
306
+ return args;
307
+ case 'enigma_complete':
308
+ rejectAdditionalProperties(args, new Set(['messages', 'model', 'privacy_mode', 'temperature', 'max_tokens', 'reasoning_effort', 'web_search', 'require_private_route']), name);
309
+ validateCompletionMessages(args.messages, name);
310
+ optionalString(args, 'model', name);
311
+ maximumStringLength(args, 'model', name, 160);
312
+ optionalString(args, 'privacy_mode', name);
313
+ if (args.privacy_mode !== undefined && !['off', 'smart', 'full'].includes(args.privacy_mode)) throw invalidParams(`${name} privacy_mode must be off, smart, or full.`);
314
+ optionalNonNegativeNumber(args, 'temperature', name);
315
+ if (args.temperature !== undefined && args.temperature > 2) throw invalidParams(`${name} temperature must not exceed 2.`);
316
+ optionalIntegerRange(args, 'max_tokens', name, 1, 131072);
317
+ optionalString(args, 'reasoning_effort', name);
318
+ if (args.reasoning_effort !== undefined && !['none', 'low', 'medium', 'high'].includes(args.reasoning_effort)) throw invalidParams(`${name} reasoning_effort is invalid.`);
319
+ optionalBoolean(args, 'web_search', name);
320
+ optionalBoolean(args, 'require_private_route', name);
321
+ return args;
322
+ case 'enigma_generate_image':
323
+ rejectAdditionalProperties(args, new Set(['prompt', 'model', 'negative_prompt', 'width', 'height', 'steps', 'cfg_scale', 'variants', 'format', 'seed']), name);
324
+ requireString(args, 'prompt', name);
325
+ requireString(args, 'model', name);
326
+ optionalString(args, 'negative_prompt', name);
327
+ maximumStringLength(args, 'prompt', name, 16000);
328
+ maximumStringLength(args, 'model', name, 160);
329
+ maximumStringLength(args, 'negative_prompt', name, 8000);
330
+ optionalIntegerRange(args, 'width', name, 256, 2048);
331
+ optionalIntegerRange(args, 'height', name, 256, 2048);
332
+ optionalIntegerRange(args, 'steps', name, 1, 100);
333
+ optionalNonNegativeNumber(args, 'cfg_scale', name);
334
+ if (args.cfg_scale !== undefined && (args.cfg_scale < 1 || args.cfg_scale > 30)) throw invalidParams(`${name} cfg_scale must be from 1 through 30.`);
335
+ optionalIntegerRange(args, 'variants', name, 1, 4);
336
+ optionalString(args, 'format', name);
337
+ if (args.format !== undefined && !['png', 'jpeg', 'webp'].includes(args.format)) throw invalidParams(`${name} format must be png, jpeg, or webp.`);
338
+ optionalIntegerRange(args, 'seed', name, 0, 2147483647);
339
+ return args;
340
+ case 'enigma_x402_execute':
341
+ rejectAdditionalProperties(args, new Set(['url', 'method', 'headers', 'body', 'max_response_bytes']), name);
342
+ requireString(args, 'url', name);
343
+ maximumStringLength(args, 'url', name, 4096);
344
+ optionalString(args, 'method', name);
345
+ if (args.method !== undefined && !['GET', 'POST'].includes(args.method)) throw invalidParams(`${name} method must be GET or POST.`);
346
+ optionalPlainObject(args, 'headers', name);
347
+ for (const [header, value] of Object.entries(args.headers || {})) {
348
+ if (!['accept', 'content-type', 'idempotency-key'].includes(header.toLowerCase()) || typeof value !== 'string' || value.length > 4096) {
349
+ throw invalidParams(`${name} contains a forbidden or invalid request header.`);
350
+ }
351
+ }
352
+ if (args.body !== undefined && typeof args.body !== 'string' && !isPlainObject(args.body)) throw invalidParams(`${name} body must be a string or object.`);
353
+ optionalIntegerRange(args, 'max_response_bytes', name, 1024, 1048576);
354
+ return args;
355
+ case 'enigma_start_private_session':
356
+ rejectAdditionalProperties(args, new Set(['intent', 'model']), name);
357
+ optionalString(args, 'intent', name);
358
+ optionalString(args, 'model', name);
359
+ rejectSensitiveHandoffIntent(args.intent);
360
+ try { validatePrivateHandoffModel(args.model); } catch (error) { throw invalidParams(error.message); }
361
+ return args;
127
362
  case 'enigma_init':
128
- rejectAdditionalProperties(args, new Set(['bundlePath', 'tenant_id', 'subject_id', 'actor_id', 'policy_id']), name);
363
+ rejectAdditionalProperties(args, new Set(['bundlePath', 'tenant_id', 'subject_id', 'actor_id', 'policy_id', 'insecureDevMode']), name);
129
364
  optionalString(args, 'bundlePath', name);
365
+ optionalBoolean(args, 'insecureDevMode', name);
130
366
  optionalString(args, 'tenant_id', name);
131
367
  optionalString(args, 'subject_id', name);
132
368
  optionalString(args, 'actor_id', name);
133
369
  optionalString(args, 'policy_id', name);
134
370
  return args;
135
371
  case 'enigma_remember':
136
- rejectAdditionalProperties(args, new Set(['bundlePath', 'text', 'purpose', 'tags', 'metadata']), name);
372
+ rejectAdditionalProperties(args, new Set(['bundlePath', 'text', 'purpose', 'tags', 'metadata', 'insecureDevMode']), name);
137
373
  optionalString(args, 'bundlePath', name);
374
+ optionalBoolean(args, 'insecureDevMode', name);
138
375
  requireString(args, 'text', name);
139
376
  optionalString(args, 'purpose', name);
140
377
  optionalStringArray(args, 'tags', name);
141
378
  optionalPlainObject(args, 'metadata', name);
142
379
  return args;
143
380
  case 'enigma_search':
144
- rejectAdditionalProperties(args, new Set(['bundlePath', 'query', 'memory_addr', 'purpose', 'limit']), name);
381
+ rejectAdditionalProperties(args, new Set(['bundlePath', 'query', 'memory_addr', 'purpose', 'limit', 'insecureDevMode', 'allowOldEpochReads']), name);
145
382
  optionalString(args, 'bundlePath', name);
383
+ optionalBoolean(args, 'insecureDevMode', name);
384
+ optionalBoolean(args, 'allowOldEpochReads', name);
146
385
  optionalString(args, 'query', name);
147
386
  optionalString(args, 'memory_addr', name);
148
387
  optionalString(args, 'purpose', name);
@@ -159,8 +398,12 @@ function validateToolArguments(name, args) {
159
398
  'max_estimated_tokens',
160
399
  'price_per_million_tokens',
161
400
  'currency',
401
+ 'insecureDevMode',
402
+ 'allowOldEpochReads',
162
403
  ]), name);
163
404
  optionalString(args, 'bundlePath', name);
405
+ optionalBoolean(args, 'insecureDevMode', name);
406
+ optionalBoolean(args, 'allowOldEpochReads', name);
164
407
  optionalString(args, 'query', name);
165
408
  optionalString(args, 'purpose', name);
166
409
  optionalLimit(args, 'limit', name);
@@ -171,16 +414,85 @@ function validateToolArguments(name, args) {
171
414
  optionalString(args, 'currency', name);
172
415
  return args;
173
416
  case 'enigma_delete':
174
- rejectAdditionalProperties(args, new Set(['bundlePath', 'memory_addr', 'reason']), name);
417
+ rejectAdditionalProperties(args, new Set(['bundlePath', 'memory_addr', 'reason', 'insecureDevMode']), name);
175
418
  optionalString(args, 'bundlePath', name);
419
+ optionalBoolean(args, 'insecureDevMode', name);
176
420
  requireString(args, 'memory_addr', name);
177
421
  optionalString(args, 'reason', name);
178
422
  return args;
423
+ case 'enigma_vault_rotate':
424
+ rejectAdditionalProperties(args, new Set(['bundlePath', 'key_id', 'insecureDevMode']), name);
425
+ optionalString(args, 'bundlePath', name);
426
+ optionalString(args, 'key_id', name);
427
+ optionalBoolean(args, 'insecureDevMode', name);
428
+ return args;
429
+ case 'enigma_vault_seal':
430
+ rejectAdditionalProperties(args, new Set(['bundlePath', 'epoch', 'insecureDevMode']), name);
431
+ optionalString(args, 'bundlePath', name);
432
+ optionalIntegerRange(args, 'epoch', name, 1, Number.MAX_SAFE_INTEGER);
433
+ optionalBoolean(args, 'insecureDevMode', name);
434
+ return args;
435
+ case 'enigma_vault_reset':
436
+ rejectAdditionalProperties(args, new Set(['bundlePath', 'key_id', 'insecureDevMode']), name);
437
+ optionalString(args, 'bundlePath', name);
438
+ optionalString(args, 'key_id', name);
439
+ optionalBoolean(args, 'insecureDevMode', name);
440
+ return args;
179
441
  case 'enigma_verify_receipts':
180
442
  rejectAdditionalProperties(args, new Set(['bundlePath', 'bundle']), name);
181
443
  optionalString(args, 'bundlePath', name);
182
444
  optionalPlainObject(args, 'bundle', name);
183
445
  return args;
446
+ case 'enigma_swarm_register_agent':
447
+ rejectAdditionalProperties(args, new Set(['bundlePath', 'agent_id', 'swarm_id', 'namespace', 'permission_mask']), name);
448
+ optionalString(args, 'bundlePath', name);
449
+ requireString(args, 'agent_id', name);
450
+ optionalString(args, 'swarm_id', name);
451
+ optionalString(args, 'namespace', name);
452
+ optionalPermissionMask(args, 'permission_mask', name);
453
+ return args;
454
+ case 'enigma_graph_query':
455
+ rejectAdditionalProperties(args, new Set(['bundlePath', 'agent_id', 'swarm_id', 'namespace', 'start', 'entity_id', 'query', 'max_depth', 'limit', 'direction']), name);
456
+ optionalString(args, 'bundlePath', name);
457
+ requireString(args, 'agent_id', name);
458
+ optionalString(args, 'swarm_id', name);
459
+ optionalString(args, 'namespace', name);
460
+ optionalString(args, 'start', name);
461
+ optionalString(args, 'entity_id', name);
462
+ optionalString(args, 'query', name);
463
+ optionalIntegerRange(args, 'max_depth', name, 0, 12);
464
+ optionalIntegerRange(args, 'limit', name, 1, 500);
465
+ optionalString(args, 'direction', name);
466
+ return args;
467
+ case 'enigma_cluster_memories':
468
+ rejectAdditionalProperties(args, new Set(['bundlePath', 'agent_id', 'swarm_id', 'namespace', 'threshold', 'max_clusters']), name);
469
+ optionalString(args, 'bundlePath', name);
470
+ requireString(args, 'agent_id', name);
471
+ optionalString(args, 'swarm_id', name);
472
+ optionalString(args, 'namespace', name);
473
+ optionalNonNegativeNumber(args, 'threshold', name);
474
+ if (args.threshold !== undefined && args.threshold > 1) throw invalidParams(`${name} threshold must not exceed 1.`);
475
+ optionalIntegerRange(args, 'max_clusters', name, 1, 100);
476
+ return args;
477
+ case 'enigma_swarm_sync':
478
+ rejectAdditionalProperties(args, new Set(['bundlePath', 'agent_id', 'swarm_id', 'namespace', 'snapshot', 'apply', 'include_content', 'allow_content']), name);
479
+ optionalString(args, 'bundlePath', name);
480
+ requireString(args, 'agent_id', name);
481
+ optionalString(args, 'swarm_id', name);
482
+ optionalString(args, 'namespace', name);
483
+ optionalPlainObject(args, 'snapshot', name);
484
+ optionalBoolean(args, 'apply', name);
485
+ optionalBoolean(args, 'include_content', name);
486
+ optionalBoolean(args, 'allow_content', name);
487
+ return args;
488
+ case 'enigma_immune_ingress':
489
+ rejectAdditionalProperties(args, new Set(['candidate', 'candidates', 'generated_at', 'now']), name);
490
+ optionalPlainObject(args, 'candidate', name);
491
+ optionalObjectArray(args, 'candidates', name);
492
+ optionalString(args, 'generated_at', name);
493
+ optionalString(args, 'now', name);
494
+ if (args.candidate === undefined && args.candidates === undefined) throw invalidParams(`${name} requires candidate or candidates.`);
495
+ return args;
184
496
  case 'enigma_meter_usage':
185
497
  rejectAdditionalProperties(args, new Set([
186
498
  'events',
@@ -291,6 +603,28 @@ function validateToolArguments(name, args) {
291
603
  requireString(args, 'batch_ref', name);
292
604
  optionalString(args, 'generated_at', name);
293
605
  return args;
606
+ case 'enigma_vault_create_record':
607
+ rejectAdditionalProperties(args, new Set(['plaintext', 'metadata']), name);
608
+ requireString(args, 'plaintext', name);
609
+ optionalPlainObject(args, 'metadata', name);
610
+ return args;
611
+ case 'enigma_vault_zeroize_record':
612
+ rejectAdditionalProperties(args, new Set(['recordId']), name);
613
+ requireString(args, 'recordId', name);
614
+ return args;
615
+ case 'enigma_verify_erasure_attestation':
616
+ rejectAdditionalProperties(args, new Set(['attestation', 'allowSimulated', 'expectedPcr']), name);
617
+ optionalPlainObject(args, 'attestation', name);
618
+ optionalBoolean(args, 'allowSimulated', name);
619
+ optionalString(args, 'expectedPcr', name);
620
+ if (!args.attestation) throw invalidParams(`${name} requires attestation object.`);
621
+ return args;
622
+ case 'enigma_nullifier_record_tombstone':
623
+ rejectAdditionalProperties(args, new Set(['commitment', 'nullifierHash', 'ownerPubkey']), name);
624
+ requireString(args, 'commitment', name);
625
+ requireString(args, 'nullifierHash', name);
626
+ requireString(args, 'ownerPubkey', name);
627
+ return args;
294
628
  default:
295
629
  throw invalidParams('Unknown tool.');
296
630
  }
@@ -305,6 +639,117 @@ function validatePromptArguments(args) {
305
639
  return args;
306
640
  }
307
641
 
642
+ function remoteToolDescriptors() {
643
+ assertRemoteToolPolicyCoverage(toolDescriptors);
644
+ return toolDescriptors.map((descriptor) => {
645
+ const policy = remoteToolPolicy(descriptor.name);
646
+ const forbidden = new Set(policy.clientForbiddenFields);
647
+ const schema = descriptor.inputSchema;
648
+ if (!schema?.properties || forbidden.size === 0) return descriptor;
649
+ const properties = Object.fromEntries(Object.entries(schema.properties).filter(([name]) => !forbidden.has(name)));
650
+ const required = Array.isArray(schema.required) ? schema.required.filter((name) => !forbidden.has(name)) : undefined;
651
+ return {
652
+ ...descriptor,
653
+ inputSchema: {
654
+ ...schema,
655
+ properties,
656
+ ...(required === undefined ? {} : { required }),
657
+ },
658
+ };
659
+ });
660
+ }
661
+
662
+ function assertNestedTenantBinding(value, tenantId, { required = false } = {}) {
663
+ let found = 0;
664
+ function visit(candidate) {
665
+ if (Array.isArray(candidate)) {
666
+ candidate.forEach(visit);
667
+ return;
668
+ }
669
+ if (!isPlainObject(candidate)) return;
670
+ for (const [key, child] of Object.entries(candidate)) {
671
+ if (key === 'tenant_id' || key === 'tenantId') {
672
+ found += 1;
673
+ if (child !== tenantId) throw invalidParams('Remote MCP settlement or metering input belongs to a different tenant.');
674
+ }
675
+ visit(child);
676
+ }
677
+ }
678
+ visit(value);
679
+ if (required && found === 0) throw invalidParams('Remote MCP input does not contain the server-bound tenant.');
680
+ }
681
+
682
+ async function bindRemoteToolArguments(name, args, context) {
683
+ if (context.transport !== 'streamable-http') return args;
684
+ if (!isPlainObject(args)) throw invalidParams('tools/call arguments must be an object.');
685
+ const policy = remoteToolPolicy(name);
686
+ if (!policy) throw invalidParams('Remote MCP policy is missing for this tool.');
687
+ const forbidden = Object.keys(args).find((key) => policy.clientForbiddenFields.includes(key));
688
+ if (forbidden) throw invalidParams(`Remote MCP does not accept client-controlled ${forbidden}.`);
689
+ const requiresBinding = policy.vaultBinding !== 'none' || policy.tenantBinding !== 'none';
690
+ let binding = null;
691
+ if (requiresBinding) {
692
+ if (typeof context.resolveVaultContext !== 'function') throw new Error('remote tenant and vault resolver is not configured');
693
+ binding = await context.resolveVaultContext(context.principal);
694
+ if (!isPlainObject(binding) || typeof binding.tenant_id !== 'string' || !binding.tenant_id) {
695
+ throw new Error('remote tenant and vault resolver returned an invalid binding');
696
+ }
697
+ }
698
+ let bound = { ...args };
699
+ if (policy.vaultBinding !== 'none') {
700
+ if (typeof binding.bundlePath !== 'string' || !binding.bundlePath) throw new Error('remote vault resolver returned no bundle path');
701
+ bound.bundlePath = binding.bundlePath;
702
+ if (name === 'enigma_init') {
703
+ bound = {
704
+ ...bound,
705
+ tenant_id: binding.tenant_id,
706
+ subject_id: String(binding.subject_id || ''),
707
+ actor_id: String(binding.actor_id || ''),
708
+ policy_id: String(binding.policy_id || ''),
709
+ };
710
+ }
711
+ if (name === 'enigma_search' || name === 'enigma_context_pack') bound.allowOldEpochReads = false;
712
+ Object.defineProperty(bound, REMOTE_SECURE_VAULT, { value: true, enumerable: true });
713
+ }
714
+ if (policy.tenantBinding === 'inject') {
715
+ if (name === 'enigma_meter_usage' && Array.isArray(bound.events)) {
716
+ assertNestedTenantBinding(bound.events, binding.tenant_id, { required: true });
717
+ }
718
+ bound.tenant_id = binding.tenant_id;
719
+ } else if (policy.tenantBinding === 'validate_nested') {
720
+ assertNestedTenantBinding(bound, binding.tenant_id, { required: true });
721
+ }
722
+ return validateToolArguments(name, bound);
723
+ }
724
+
725
+ async function bindRemoteResourceInput(params, normalizedUri, context) {
726
+ if (context.transport !== 'streamable-http' || normalizedUri !== PASSPORT_SUMMARY_RESOURCE_URI) return inputWithBundlePath(params);
727
+ const forbiddenFields = new Set(['bundlePath', 'bundle_path', 'insecureDevMode', 'insecure_dev_mode']);
728
+ const parsed = new URL(String(params.uri));
729
+ const forbiddenQuery = [...parsed.searchParams.keys()].find((key) => forbiddenFields.has(key));
730
+ const forbiddenProperty = Object.keys(params).find((key) => forbiddenFields.has(key));
731
+ if (forbiddenQuery || forbiddenProperty) throw invalidParams(`Remote MCP does not accept client-controlled ${forbiddenQuery || forbiddenProperty}.`);
732
+ if (typeof context.resolveVaultContext !== 'function') throw new Error('remote vault resolver is not configured');
733
+ const binding = await context.resolveVaultContext(context.principal);
734
+ if (!isPlainObject(binding) || typeof binding.bundlePath !== 'string' || !binding.bundlePath) {
735
+ throw new Error('remote vault resolver returned an invalid binding');
736
+ }
737
+ const bound = { uri: params.uri, bundlePath: binding.bundlePath };
738
+ Object.defineProperty(bound, REMOTE_SECURE_VAULT, { value: true, enumerable: true });
739
+ return bound;
740
+ }
741
+
742
+ function sanitizeRemoteCustodyOutput(value, context) {
743
+ if (context.transport !== 'streamable-http' || !value || typeof value !== 'object' || Buffer.isBuffer(value) || value instanceof Uint8Array) return value;
744
+ if (Array.isArray(value)) return value.map((item) => sanitizeRemoteCustodyOutput(item, context));
745
+ const sanitized = {};
746
+ for (const [key, item] of Object.entries(value)) {
747
+ if (key === 'bundlePath' || key === 'bundle_path') continue;
748
+ sanitized[key] = sanitizeRemoteCustodyOutput(item, context);
749
+ }
750
+ return sanitized;
751
+ }
752
+
308
753
  function sanitizeOperationalError(error) {
309
754
  return {
310
755
  ok: false,
@@ -313,8 +758,148 @@ function sanitizeOperationalError(error) {
313
758
  };
314
759
  }
315
760
 
761
+ function mcpImmuneIngressCandidates(input) {
762
+ if (!isPlainObject(input)) return input;
763
+ if (Object.prototype.hasOwnProperty.call(input, 'candidates')) return input.candidates;
764
+ if (Object.prototype.hasOwnProperty.call(input, 'candidate')) return input.candidate;
765
+ if (Object.prototype.hasOwnProperty.call(input, 'memory_candidates')) return input.memory_candidates;
766
+ if (Object.prototype.hasOwnProperty.call(input, 'memoryCandidates')) return input.memoryCandidates;
767
+ return input;
768
+ }
769
+
770
+ function mcpImmuneIngressOptions(input, options = {}) {
771
+ return {
772
+ ...options,
773
+ now: options.now ?? input?.now ?? input?.generated_at ?? input?.generatedAt,
774
+ source_type: options.source_type ?? options.sourceType ?? 'mcp_candidate_ingress',
775
+ };
776
+ }
777
+
778
+ export function createMcpImmuneIngressReport(input = {}, options = {}) {
779
+ return createImmuneIngressReport(mcpImmuneIngressCandidates(input), mcpImmuneIngressOptions(input, options));
780
+ }
781
+
782
+ export function assertMcpImmuneIngressPublicSafe(input = {}, options = {}) {
783
+ return assertImmuneIngressPublicSafe(mcpImmuneIngressCandidates(input), mcpImmuneIngressOptions(input, options));
784
+ }
785
+
316
786
 
317
787
  export const toolDescriptors = [
788
+ {
789
+ name: 'enigma_private_handoff_health',
790
+ description: 'Check whether the local Enigma private workspace is reachable. This diagnostic never accepts or returns a user prompt.',
791
+ inputSchema: {
792
+ type: 'object',
793
+ properties: {},
794
+ additionalProperties: false,
795
+ },
796
+ },
797
+ {
798
+ name: 'enigma_routing_contract',
799
+ description: 'Explain when an MCP host must use an Enigma private-session handoff instead of relaying prompt text through host-visible tool arguments.',
800
+ inputSchema: {
801
+ type: 'object',
802
+ properties: {},
803
+ additionalProperties: false,
804
+ },
805
+ },
806
+ {
807
+ name: 'enigma_start_private_session',
808
+ description: 'Open the Enigma private workspace for sensitive prompts. Pass only a non-sensitive intent label; the actual prompt must be typed inside Enigma and never placed in this tool call.',
809
+ inputSchema: {
810
+ type: 'object',
811
+ properties: {
812
+ intent: {
813
+ type: 'string',
814
+ maxLength: 160,
815
+ description: 'Optional non-sensitive handoff label such as "private legal question". Never include the actual private prompt.',
816
+ },
817
+ model: {
818
+ type: 'string',
819
+ maxLength: 160,
820
+ description: 'Optional configured Enigma model route to preselect.',
821
+ },
822
+ },
823
+ additionalProperties: false,
824
+ },
825
+ _meta: {
826
+ 'openai/outputTemplate': PRIVATE_SESSION_RESOURCE_URI,
827
+ 'ui/resourceUri': PRIVATE_SESSION_RESOURCE_URI,
828
+ },
829
+ },
830
+ {
831
+ name: 'enigma_complete',
832
+ description: 'Run a direct Enigma completion with Smart Privacy and explicit route disclosure. MCP tool arguments are visible to the MCP host; use enigma_start_private_session when the prompt must remain outside host chat.',
833
+ inputSchema: {
834
+ type: 'object',
835
+ properties: {
836
+ messages: {
837
+ type: 'array',
838
+ minItems: 1,
839
+ maxItems: 128,
840
+ items: {
841
+ type: 'object',
842
+ properties: {
843
+ role: { type: 'string', enum: ['system', 'user', 'assistant', 'tool'] },
844
+ content: { type: 'string', maxLength: 100000 },
845
+ },
846
+ required: ['role', 'content'],
847
+ additionalProperties: false,
848
+ },
849
+ },
850
+ model: { type: 'string', default: 'enigma-auto' },
851
+ privacy_mode: { type: 'string', enum: ['off', 'smart', 'full'], default: 'smart' },
852
+ temperature: { type: 'number', minimum: 0, maximum: 2 },
853
+ max_tokens: { type: 'integer', minimum: 1, maximum: 131072 },
854
+ reasoning_effort: { type: 'string', enum: ['none', 'low', 'medium', 'high'] },
855
+ web_search: { type: 'boolean', default: false },
856
+ require_private_route: { type: 'boolean', default: false },
857
+ },
858
+ required: ['messages'],
859
+ additionalProperties: false,
860
+ },
861
+ },
862
+ {
863
+ name: 'enigma_generate_image',
864
+ description: 'Generate image assets through a configured Enigma image route. The prompt is sent only to that configured route and is not retained in the MCP receipt.',
865
+ inputSchema: {
866
+ type: 'object',
867
+ properties: {
868
+ prompt: { type: 'string', maxLength: 16000 },
869
+ model: { type: 'string', maxLength: 160 },
870
+ negative_prompt: { type: 'string', maxLength: 8000 },
871
+ width: { type: 'integer', minimum: 256, maximum: 2048, default: 1024 },
872
+ height: { type: 'integer', minimum: 256, maximum: 2048, default: 1024 },
873
+ steps: { type: 'integer', minimum: 1, maximum: 100, default: 30 },
874
+ cfg_scale: { type: 'number', minimum: 1, maximum: 30, default: 7.5 },
875
+ variants: { type: 'integer', minimum: 1, maximum: 4, default: 1 },
876
+ format: { type: 'string', enum: ['png', 'jpeg', 'webp'], default: 'webp' },
877
+ seed: { type: 'integer', minimum: 0, maximum: 2147483647 },
878
+ },
879
+ required: ['prompt', 'model'],
880
+ additionalProperties: false,
881
+ },
882
+ },
883
+ {
884
+ name: 'enigma_x402_execute',
885
+ description: 'Execute an x402 v2 paid HTTP request using the configured real wallet signer, strict recipient/network/asset allowlists, and an atomic-unit budget. This moves funds; inspect the server offer before calling.',
886
+ inputSchema: {
887
+ type: 'object',
888
+ properties: {
889
+ url: { type: 'string', maxLength: 4096 },
890
+ method: { type: 'string', enum: ['GET', 'POST'], default: 'GET' },
891
+ headers: {
892
+ type: 'object',
893
+ description: 'Only Accept, Content-Type, and Idempotency-Key are allowed.',
894
+ additionalProperties: { type: 'string', maxLength: 4096 },
895
+ },
896
+ body: { anyOf: [{ type: 'string' }, { type: 'object' }] },
897
+ max_response_bytes: { type: 'integer', minimum: 1024, maximum: 1048576, default: 262144 },
898
+ },
899
+ required: ['url'],
900
+ additionalProperties: false,
901
+ },
902
+ },
318
903
  {
319
904
  name: 'enigma_init',
320
905
  description: 'Create a local Enigma vault bundle when one does not already exist.',
@@ -326,6 +911,7 @@ export const toolDescriptors = [
326
911
  subject_id: { type: 'string' },
327
912
  actor_id: { type: 'string' },
328
913
  policy_id: { type: 'string' },
914
+ insecureDevMode: { type: 'boolean', description: 'Explicitly allow a plaintext disposable development bundle when no server passphrase is configured.' },
329
915
  },
330
916
  additionalProperties: false,
331
917
  },
@@ -341,6 +927,7 @@ export const toolDescriptors = [
341
927
  purpose: { type: 'string' },
342
928
  tags: { type: 'array', items: { type: 'string' } },
343
929
  metadata: { type: 'object' },
930
+ insecureDevMode: { type: 'boolean' },
344
931
  },
345
932
  required: ['text'],
346
933
  additionalProperties: false,
@@ -357,6 +944,8 @@ export const toolDescriptors = [
357
944
  memory_addr: { type: 'string' },
358
945
  purpose: { type: 'string' },
359
946
  limit: { type: 'integer', minimum: 1, maximum: 50 },
947
+ insecureDevMode: { type: 'boolean' },
948
+ allowOldEpochReads: { type: 'boolean' },
360
949
  },
361
950
  additionalProperties: false,
362
951
  },
@@ -376,6 +965,8 @@ export const toolDescriptors = [
376
965
  max_estimated_tokens: { type: 'number', minimum: 0 },
377
966
  price_per_million_tokens: { type: 'number', minimum: 0 },
378
967
  currency: { type: 'string' },
968
+ insecureDevMode: { type: 'boolean' },
969
+ allowOldEpochReads: { type: 'boolean' },
379
970
  },
380
971
  additionalProperties: false,
381
972
  },
@@ -389,11 +980,51 @@ export const toolDescriptors = [
389
980
  bundlePath: { type: 'string' },
390
981
  memory_addr: { type: 'string' },
391
982
  reason: { type: 'string' },
983
+ insecureDevMode: { type: 'boolean' },
392
984
  },
393
985
  required: ['memory_addr'],
394
986
  additionalProperties: false,
395
987
  },
396
988
  },
989
+ {
990
+ name: 'enigma_vault_rotate',
991
+ description: 'Rotate to a new content-key epoch while honestly retaining prior keys for authorized historical reads.',
992
+ inputSchema: {
993
+ type: 'object',
994
+ properties: {
995
+ bundlePath: { type: 'string' },
996
+ key_id: { type: 'string' },
997
+ insecureDevMode: { type: 'boolean' },
998
+ },
999
+ additionalProperties: false,
1000
+ },
1001
+ },
1002
+ {
1003
+ name: 'enigma_vault_seal',
1004
+ description: 'Seal a content-key epoch against future writes while retaining its key for policy-authorized reads.',
1005
+ inputSchema: {
1006
+ type: 'object',
1007
+ properties: {
1008
+ bundlePath: { type: 'string' },
1009
+ epoch: { type: 'integer', minimum: 1 },
1010
+ insecureDevMode: { type: 'boolean' },
1011
+ },
1012
+ additionalProperties: false,
1013
+ },
1014
+ },
1015
+ {
1016
+ name: 'enigma_vault_reset',
1017
+ description: 'Perform a software content-key reset, remove local records, and emit signed evidence without claiming physical-media, backup, or external-copy erasure.',
1018
+ inputSchema: {
1019
+ type: 'object',
1020
+ properties: {
1021
+ bundlePath: { type: 'string' },
1022
+ key_id: { type: 'string' },
1023
+ insecureDevMode: { type: 'boolean' },
1024
+ },
1025
+ additionalProperties: false,
1026
+ },
1027
+ },
397
1028
  {
398
1029
  name: 'enigma_verify_receipts',
399
1030
  description: 'Verify receipts/checkpoints in a local or exported Enigma bundle and return a machine-readable verdict.',
@@ -406,6 +1037,99 @@ export const toolDescriptors = [
406
1037
  additionalProperties: false,
407
1038
  },
408
1039
  },
1040
+ {
1041
+ name: 'enigma_swarm_register_agent',
1042
+ description: 'Register an agent in a local swarm domain with a namespace fence and explicit permission mask.',
1043
+ inputSchema: {
1044
+ type: 'object',
1045
+ properties: {
1046
+ bundlePath: { type: 'string' },
1047
+ agent_id: { type: 'string' },
1048
+ swarm_id: { type: 'string' },
1049
+ namespace: { type: 'string' },
1050
+ permission_mask: {
1051
+ oneOf: [
1052
+ { type: 'integer', minimum: 0, maximum: 63 },
1053
+ { type: 'string' },
1054
+ { type: 'array', items: { type: 'string' } },
1055
+ ],
1056
+ },
1057
+ },
1058
+ required: ['agent_id'],
1059
+ additionalProperties: false,
1060
+ },
1061
+ },
1062
+ {
1063
+ name: 'enigma_graph_query',
1064
+ description: 'Traverse the namespace-fenced local memory entity graph, including cross-atom associative links.',
1065
+ inputSchema: {
1066
+ type: 'object',
1067
+ properties: {
1068
+ bundlePath: { type: 'string' },
1069
+ agent_id: { type: 'string' },
1070
+ swarm_id: { type: 'string' },
1071
+ namespace: { type: 'string' },
1072
+ start: { type: 'string' },
1073
+ entity_id: { type: 'string' },
1074
+ query: { type: 'string' },
1075
+ max_depth: { type: 'integer', minimum: 0, maximum: 12 },
1076
+ limit: { type: 'integer', minimum: 1, maximum: 500 },
1077
+ direction: { type: 'string' },
1078
+ },
1079
+ required: ['agent_id'],
1080
+ additionalProperties: false,
1081
+ },
1082
+ },
1083
+ {
1084
+ name: 'enigma_cluster_memories',
1085
+ description: 'Group active namespace-fenced memories by semantic proximity and return committed centroid fingerprints.',
1086
+ inputSchema: {
1087
+ type: 'object',
1088
+ properties: {
1089
+ bundlePath: { type: 'string' },
1090
+ agent_id: { type: 'string' },
1091
+ swarm_id: { type: 'string' },
1092
+ namespace: { type: 'string' },
1093
+ threshold: { type: 'number', minimum: 0, maximum: 1 },
1094
+ max_clusters: { type: 'integer', minimum: 1, maximum: 100 },
1095
+ },
1096
+ required: ['agent_id'],
1097
+ additionalProperties: false,
1098
+ },
1099
+ },
1100
+ {
1101
+ name: 'enigma_swarm_sync',
1102
+ description: 'Create or validate an opt-in local gossip simulation using Merkle diffs; no network transmission occurs and raw content is excluded by default.',
1103
+ inputSchema: {
1104
+ type: 'object',
1105
+ properties: {
1106
+ bundlePath: { type: 'string' },
1107
+ agent_id: { type: 'string' },
1108
+ swarm_id: { type: 'string' },
1109
+ namespace: { type: 'string' },
1110
+ snapshot: { type: 'object' },
1111
+ apply: { type: 'boolean' },
1112
+ include_content: { type: 'boolean' },
1113
+ allow_content: { type: 'boolean' },
1114
+ },
1115
+ required: ['agent_id'],
1116
+ additionalProperties: false,
1117
+ },
1118
+ },
1119
+ {
1120
+ name: 'enigma_immune_ingress',
1121
+ description: 'Scan MCP-supplied memory candidate objects and return a public-safe immune quarantine report with opaque refs only.',
1122
+ inputSchema: {
1123
+ type: 'object',
1124
+ properties: {
1125
+ candidate: { type: 'object' },
1126
+ candidates: { type: 'array', items: { type: 'object' } },
1127
+ generated_at: { type: 'string' },
1128
+ now: { type: 'string' },
1129
+ },
1130
+ additionalProperties: false,
1131
+ },
1132
+ },
409
1133
  {
410
1134
  name: 'enigma_meter_usage',
411
1135
  description: 'Create content-minimized memory usage events or aggregate supplied usage events without raw prompts or provider responses.',
@@ -546,9 +1270,30 @@ export const toolDescriptors = [
546
1270
  additionalProperties: false,
547
1271
  },
548
1272
  },
1273
+ // NOTE: enigma_verify_erasure_attestation is intentionally NOT exposed as an MCP
1274
+ // tool. The hardware path pins only the chain root and trusts the leaf public key
1275
+ // without verifying leaf/intermediate signatures, and the TPM path allowlists a
1276
+ // forgeable issuer string — an attacker could obtain valid:true with their own
1277
+ // key. The exported function remains for internal/direct use until real chain +
1278
+ // quote validation lands.
1279
+ {
1280
+ name: 'enigma_nullifier_record_tombstone',
1281
+ description: 'Build an owner-signed Solana record_revocation instruction for a registered Enigma commitment. Returns complete ordered account metas and instruction data; the owner must sign. Records protocol-level revocation/non-use only — not physical deletion of plaintext, backups, or external copies.',
1282
+ inputSchema: {
1283
+ type: 'object',
1284
+ properties: {
1285
+ commitment: { type: 'string', pattern: '^(0x)?[0-9a-fA-F]{64}$' },
1286
+ nullifierHash: { type: 'string', pattern: '^(0x)?[0-9a-fA-F]{64}$' },
1287
+ ownerPubkey: { type: 'string', description: 'Base58 Solana public key of the commitment owner (required signer)' },
1288
+ },
1289
+ required: ['commitment', 'nullifierHash', 'ownerPubkey'],
1290
+ additionalProperties: false,
1291
+ },
1292
+ },
549
1293
  ];
550
1294
 
551
1295
  const PASSPORT_SUMMARY_RESOURCE_URI = 'enigma://passport/summary';
1296
+ const PRIVATE_SESSION_HANDOFF_RESOURCE_URI = PRIVATE_SESSION_RESOURCE_URI;
552
1297
  const STANDARD_MEMORY_PROMPT_NAME = 'enigma_standard_memory_prompt';
553
1298
 
554
1299
  export const resourceDescriptors = [
@@ -558,6 +1303,12 @@ export const resourceDescriptors = [
558
1303
  description: 'MCP-safe Passport and Vault metadata for the local Enigma bundle. Raw memory plaintext is never exposed.',
559
1304
  mimeType: 'application/json',
560
1305
  },
1306
+ {
1307
+ uri: PRIVATE_SESSION_HANDOFF_RESOURCE_URI,
1308
+ name: 'Enigma Private Session Handoff',
1309
+ description: 'Enigma-branded MCP App handoff. The host sees only non-sensitive handoff metadata; the actual prompt is typed in the local Enigma workspace.',
1310
+ mimeType: 'text/html;profile=mcp-app',
1311
+ },
561
1312
  ];
562
1313
 
563
1314
  export const promptDescriptors = [
@@ -580,16 +1331,41 @@ export const promptDescriptors = [
580
1331
  ];
581
1332
 
582
1333
  function bundlePath(input = {}) {
583
- return resolve(String(input.bundlePath ?? input.bundle_path ?? DEFAULT_BUNDLE));
1334
+ const raw = input.bundlePath ?? input.bundle_path ?? input.bundle ?? process.env.ENIGMA_BUNDLE ?? DEFAULT_BUNDLE;
1335
+ return resolve(String(raw));
584
1336
  }
585
1337
 
586
- async function readJson(path) {
587
- return JSON.parse(await readFile(path, 'utf8'));
1338
+ function bundlePassphrase(input = {}) {
1339
+ const value = input.passphrase ?? input.passPhrase ?? process.env.ENIGMA_PASSPHRASE ?? undefined;
1340
+ if (value !== undefined && String(value).trim().length === 0) throw new Error('ENIGMA_PASSPHRASE is empty');
1341
+ return value === undefined ? undefined : String(value);
588
1342
  }
589
1343
 
590
- async function writeJson(path, value) {
591
- await mkdir(dirname(path), { recursive: true });
592
- await writeFile(path, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
1344
+ function bundleInsecureDevMode(input = {}) {
1345
+ if (input[REMOTE_SECURE_VAULT] === true) return false;
1346
+ return input.insecureDevMode === true
1347
+ || input.insecure_dev_mode === true
1348
+ || ['1', 'true'].includes(String(process.env.ENIGMA_INSECURE_DEV_MODE ?? '').toLowerCase());
1349
+ }
1350
+
1351
+ function bundleCredentials(input = {}) {
1352
+ const passphrase = bundlePassphrase(input);
1353
+ const insecureDevMode = bundleInsecureDevMode(input);
1354
+ if (passphrase === undefined && !insecureDevMode) {
1355
+ throw new Error('Persistent MCP vault access requires ENIGMA_PASSPHRASE unless ENIGMA_INSECURE_DEV_MODE=1 is explicitly enabled.');
1356
+ }
1357
+ return {
1358
+ passphrase,
1359
+ insecureDevMode,
1360
+ allowOldEpochReads: input.allowOldEpochReads ?? input.allow_old_epoch_reads,
1361
+ checkpointProvider: input.checkpointProvider ?? configuredCheckpointProvider,
1362
+ requireExternalCheckpoint: input.requireExternalCheckpoint === true
1363
+ || process.env.ENIGMA_REQUIRE_EXTERNAL_CHECKPOINT === '1',
1364
+ };
1365
+ }
1366
+
1367
+ async function readJson(path) {
1368
+ return JSON.parse(await readFile(path, 'utf8'));
593
1369
  }
594
1370
 
595
1371
  async function fileExists(path) {
@@ -602,50 +1378,89 @@ async function fileExists(path) {
602
1378
  }
603
1379
  }
604
1380
 
605
- function reviveVault(stored) {
606
- if (stored.schema !== 'enigma.vault_bundle.v1') throw new Error(`Unsupported Enigma bundle schema: ${stored.schema ?? '<missing>'}`);
607
- const signingKeyPair = stored.keyring?.privateKey
608
- ? { key_id: stored.keyring.signer?.key_id, publicKey: stored.keyring.publicKey, privateKey: stored.keyring.privateKey }
609
- : undefined;
610
- const vault = createVault({
611
- vault_id: stored.vault?.vault_id,
612
- tenant_id: stored.vault?.tenant_id,
613
- subject_id: stored.vault?.subject_id,
614
- actor_id: stored.vault?.actor_id,
615
- policy_id: stored.vault?.policy_id,
616
- vault_key: stored.keyring?.vault_key_b64,
617
- address_key: stored.keyring?.address_key_b64,
618
- signingKeyPair,
619
- now: stored.vault?.created_at,
1381
+ async function loadState(path, input = {}) {
1382
+ const credentials = bundleCredentials(input);
1383
+ const loaded = await readJsonFileWithRevision(path, credentials);
1384
+ const cacheKey = resolve(String(path));
1385
+ const authTag = credentialCacheTag(credentials);
1386
+ const cached = unlockedVaultCache.get(cacheKey);
1387
+ const policyMatches = credentials.allowOldEpochReads === undefined
1388
+ || cached?.vault?.allowOldEpochReads === credentials.allowOldEpochReads;
1389
+ if (cached?.revision === loaded.revision && cached.authTag === authTag && policyMatches) {
1390
+ unlockedVaultCache.delete(cacheKey);
1391
+ unlockedVaultCache.set(cacheKey, cached);
1392
+ return { ...cached, stored: loaded.value, revision: loaded.revision };
1393
+ }
1394
+ if (cached) invalidateCachedVault(cacheKey);
1395
+ const imported = importBundle({
1396
+ bundle: loaded.value,
1397
+ ...credentials,
1398
+ strict: true,
1399
+ recordImport: false,
620
1400
  });
621
- if (stored.keyring?.signer) vault.signer = stored.keyring.signer;
622
- vault.created_at = stored.vault?.created_at ?? vault.created_at;
623
- vault.updated_at = stored.vault?.updated_at ?? vault.updated_at;
624
- vault.sequence = Number.isInteger(stored.vault?.sequence) ? stored.vault.sequence : 0;
625
- vault.events = Array.isArray(stored.events) ? stored.events : [];
626
- vault.receipts = Array.isArray(stored.receipts) ? stored.receipts : [];
627
- vault.memories = new Map((stored.memory_objects ?? []).map((record) => [record.memory_addr, { ...record }]));
628
- vault.activeAddresses = new Set(stored.active_memory_addresses ?? []);
629
- vault.tombstones = new Map((stored.tombstones ?? []).map((tombstone) => [tombstone.memory_addr, tombstone]));
630
- const roots = vault.__computeRoots();
631
- vault.active_set_root = roots.active_set_root;
632
- vault.receipt_log_root = roots.receipt_log_root;
633
- return vault;
634
- }
635
-
636
- async function loadState(path) {
637
- const stored = await readJson(path);
638
- const vault = reviveVault(stored);
639
- return { stored, vault, passport: createPassport({ vault }) };
1401
+ const vault = imported.vault;
1402
+ const state = {
1403
+ stored: loaded.value,
1404
+ revision: loaded.revision,
1405
+ vault,
1406
+ passport: createPassport({ vault }),
1407
+ verified: imported.verified,
1408
+ authTag,
1409
+ };
1410
+ cacheUnlockedVault(cacheKey, state);
1411
+ return state;
640
1412
  }
641
1413
 
642
- async function persistState(path, vault) {
643
- const exported = exportBundle({ vault, includePlaintext: false });
1414
+ async function persistState(path, vault, input = {}) {
1415
+ if (input.lockHeld !== true) {
1416
+ return withBundleFileLock(path, async () => {
1417
+ const expectedRevision = Object.prototype.hasOwnProperty.call(input, 'expectedRevision')
1418
+ ? input.expectedRevision
1419
+ : (await fileExists(path) ? (await readJsonFileWithRevision(path, bundleCredentials(input))).revision : null);
1420
+ return persistState(path, vault, { ...input, lockHeld: true, expectedRevision });
1421
+ });
1422
+ }
1423
+ const credentials = bundleCredentials(input);
1424
+ const exported = exportBundle({ vault, passphrase: credentials.passphrase, includePlaintext: false });
644
1425
  const bundle = exported.bundle ?? exported;
645
- await writeJson(path, bundle);
1426
+ const persisted = await writeJsonFileAtomic(path, bundle, {
1427
+ ...(Object.prototype.hasOwnProperty.call(input, 'expectedRevision')
1428
+ ? { expectedRevision: input.expectedRevision }
1429
+ : {}),
1430
+ checkpointProvider: credentials.checkpointProvider,
1431
+ requireExternalCheckpoint: credentials.requireExternalCheckpoint,
1432
+ });
1433
+ const state = {
1434
+ stored: bundle,
1435
+ revision: persisted.revision,
1436
+ vault,
1437
+ passport: createPassport({ vault }),
1438
+ verified: true,
1439
+ authTag: credentialCacheTag(credentials),
1440
+ };
1441
+ cacheUnlockedVault(path, state);
1442
+ Object.defineProperty(bundle, '__revision', { value: persisted.revision, enumerable: false });
646
1443
  return bundle;
647
1444
  }
648
1445
 
1446
+ async function withVaultTransaction(path, input, operation) {
1447
+ return withBundleFileLock(path, async () => {
1448
+ const state = await loadState(path, input);
1449
+ try {
1450
+ const result = await operation(state);
1451
+ const bundle = await persistState(path, state.vault, {
1452
+ ...input,
1453
+ lockHeld: true,
1454
+ expectedRevision: state.revision,
1455
+ });
1456
+ return { ...state, revision: bundle.__revision, result, bundle };
1457
+ } catch (error) {
1458
+ invalidateCachedVault(path);
1459
+ throw error;
1460
+ }
1461
+ });
1462
+ }
1463
+
649
1464
  function bundleSummary(path, bundle, created) {
650
1465
  return {
651
1466
  ok: true,
@@ -681,7 +1496,11 @@ function inputWithBundlePath(params = {}) {
681
1496
  } catch {
682
1497
  // Invalid or relative resource identifiers are rejected by the resource handler.
683
1498
  }
684
- return requestedPath === undefined ? { uri } : { uri, bundlePath: requestedPath };
1499
+ return {
1500
+ uri,
1501
+ ...(requestedPath === undefined ? {} : { bundlePath: requestedPath }),
1502
+ ...(params.insecureDevMode === true ? { insecureDevMode: true } : {}),
1503
+ };
685
1504
  }
686
1505
 
687
1506
  function passportFromBundle(bundle) {
@@ -750,7 +1569,7 @@ export async function enigma_passport_summary_resource(input = {}) {
750
1569
  throw new Error(`Unknown Enigma resource: ${uri ?? '<missing>'}`);
751
1570
  }
752
1571
  const path = bundlePath(options);
753
- const bundle = await readJson(path);
1572
+ const { stored: bundle } = await loadState(path, options);
754
1573
  return {
755
1574
  contents: [
756
1575
  {
@@ -762,6 +1581,162 @@ export async function enigma_passport_summary_resource(input = {}) {
762
1581
  };
763
1582
  }
764
1583
 
1584
+
1585
+ function privateSessionWidgetHtml() {
1586
+ const url = privateSessionUrl();
1587
+ return `<!doctype html>
1588
+ <html lang="en">
1589
+ <head>
1590
+ <meta charset="utf-8">
1591
+ <meta name="viewport" content="width=device-width, initial-scale=1">
1592
+ <title>Enigma private session</title>
1593
+ <style>
1594
+ :root{color-scheme:dark;font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;background:#0a0908;color:#f4f2ef}
1595
+ *{box-sizing:border-box}body{margin:0;min-height:100vh;display:grid;place-items:center;padding:24px;background:radial-gradient(circle at 84% 0,rgba(255,122,26,.15),transparent 24rem),#0a0908}
1596
+ main{width:min(620px,100%);padding:30px;border:1px solid rgba(255,255,255,.14);border-radius:18px;background:rgba(20,17,14,.92);box-shadow:inset 0 1px rgba(255,255,255,.07),0 24px 64px rgba(0,0,0,.45)}
1597
+ .eyebrow{font:600 11px ui-monospace,monospace;letter-spacing:.18em;color:#ff8b3d}.mark{display:inline-block;width:8px;height:8px;margin-right:8px;border-radius:50%;background:#ff7a1a}
1598
+ h1{max-width:14ch;margin:14px 0 10px;font-size:clamp(30px,7vw,52px);line-height:.98;letter-spacing:-.05em}p{max-width:58ch;color:#b9b6b1;font-size:14px;line-height:1.6}
1599
+ a{display:inline-flex;margin-top:20px;padding:11px 15px;border-radius:9px;background:#ff7a1a;color:#1a0b02;font:700 11px ui-monospace,monospace;text-decoration:none;text-transform:uppercase}
1600
+ section{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;margin-top:26px;border:1px solid rgba(255,255,255,.12);background:rgba(255,255,255,.12)}
1601
+ section div{padding:14px;background:#100e0c}small{display:block;color:#7d7a76;font:10px/1.45 ui-monospace,monospace}strong{display:block;margin-bottom:4px;font-size:12px}
1602
+ @media(max-width:520px){section{grid-template-columns:1fr}}
1603
+ </style>
1604
+ </head>
1605
+ <body>
1606
+ <main>
1607
+ <span class="eyebrow"><span class="mark"></span>ENIGMA / PRIVATE HANDOFF</span>
1608
+ <h1>Type the private prompt inside Enigma.</h1>
1609
+ <p>The actual prompt stays out of the MCP host chat. When you send it, the selected provider adapter receives the prepared request; Anchor Guard may mask supported patterns but is not semantic secrecy.</p>
1610
+ <a href="${url}" target="_blank" rel="noreferrer">Open private workspace</a>
1611
+ <section>
1612
+ <div><strong>MCP host</strong><small>Handoff fact, route label, non-sensitive intent. No workspace prompt.</small></div>
1613
+ <div><strong>Enigma workspace</strong><small>In-memory transcript and local context orchestration.</small></div>
1614
+ <div><strong>Configured provider</strong><small>Receives adapter input, optionally pattern-redacted by Anchor Guard.</small></div>
1615
+ </section>
1616
+ </main>
1617
+ </body>
1618
+ </html>`;
1619
+ }
1620
+
1621
+ function enigma_private_session_resource(input = {}) {
1622
+ const uri = input.uri ?? PRIVATE_SESSION_HANDOFF_RESOURCE_URI;
1623
+ if (String(uri) !== PRIVATE_SESSION_HANDOFF_RESOURCE_URI) {
1624
+ throw new Error(`Unknown Enigma resource: ${uri ?? '<missing>'}`);
1625
+ }
1626
+ return {
1627
+ contents: [{
1628
+ uri: PRIVATE_SESSION_HANDOFF_RESOURCE_URI,
1629
+ mimeType: 'text/html;profile=mcp-app',
1630
+ text: privateSessionWidgetHtml(),
1631
+ }],
1632
+ };
1633
+ }
1634
+
1635
+ export async function enigma_private_handoff_health(input = {}) {
1636
+ validateToolArguments('enigma_private_handoff_health', input);
1637
+ const origin = enigmaDesktopOrigin();
1638
+ const startedAt = Date.now();
1639
+ const protocolUrl = createPrivateSessionProtocolUrl();
1640
+ try {
1641
+ const response = await fetch(`${origin}/api/health`, {
1642
+ method: 'GET',
1643
+ headers: { Accept: 'application/json' },
1644
+ redirect: 'error',
1645
+ signal: AbortSignal.timeout(1_500),
1646
+ });
1647
+ const body = await response.json();
1648
+ const reachable = response.ok
1649
+ && body?.ok === true
1650
+ && body?.schema === 'enigma.desktop.health.v1'
1651
+ && body?.service === 'enigma-desktop'
1652
+ && typeof body?.instanceId === 'string';
1653
+ return {
1654
+ schema: 'enigma.private_handoff_health.v1',
1655
+ reachable,
1656
+ origin,
1657
+ latencyMs: Date.now() - startedAt,
1658
+ desktop: reachable ? body : null,
1659
+ protocolUrl,
1660
+ directProbeConfigured: Boolean(process.env.ENIGMA_DESKTOP_URL),
1661
+ privateSessionResource: PRIVATE_SESSION_HANDOFF_RESOURCE_URI,
1662
+ ...(reachable ? {} : { reason: 'invalid_enigma_health_schema' }),
1663
+ };
1664
+ } catch {
1665
+ return {
1666
+ schema: 'enigma.private_handoff_health.v1',
1667
+ reachable: false,
1668
+ origin,
1669
+ latencyMs: Date.now() - startedAt,
1670
+ desktop: null,
1671
+ protocolUrl,
1672
+ directProbeConfigured: Boolean(process.env.ENIGMA_DESKTOP_URL),
1673
+ privateSessionResource: PRIVATE_SESSION_HANDOFF_RESOURCE_URI,
1674
+ action: 'Launch the Enigma desktop app through the enigma:// protocol. Set ENIGMA_DESKTOP_URL only for a known CLI host.',
1675
+ };
1676
+ }
1677
+ }
1678
+
1679
+ export function enigma_routing_contract(input = {}) {
1680
+ validateToolArguments('enigma_routing_contract', input);
1681
+ return {
1682
+ schema: 'enigma.private_routing_contract.v1',
1683
+ decision: {
1684
+ sensitiveOrPrivate: 'Call enigma_start_private_session with a non-sensitive intent label only.',
1685
+ alreadyHostVisible: 'Use normal Enigma memory tools only when the user accepts that the host can inspect the supplied text.',
1686
+ actualPrivatePrompt: 'The user types it inside Enigma to keep it out of the MCP host chat; the configured provider still receives prepared adapter input.',
1687
+ },
1688
+ hostVisible: [
1689
+ 'Messages typed into the host chat.',
1690
+ 'The fact that an Enigma private session was requested.',
1691
+ 'Non-sensitive intent and model labels passed to the handoff tool.',
1692
+ 'Any text explicitly passed to ordinary MCP tool arguments.',
1693
+ ],
1694
+ keptOutOfMcpHost: [
1695
+ 'The prompt typed after the Enigma workspace opens.',
1696
+ 'The in-memory workspace transcript until refresh or explicit clearing.',
1697
+ 'The provider response rendered inside the workspace.',
1698
+ ],
1699
+ providerVisible: [
1700
+ 'The prompt and retrieved context prepared for the selected adapter.',
1701
+ 'Anchor Guard can mask documented patterns but does not detect arbitrary sensitive prose.',
1702
+ ],
1703
+ claims: {
1704
+ hostChatInvisible: false,
1705
+ privateHandoffAvailable: true,
1706
+ promptRelayIsPrivateFromHost: false,
1707
+ workspacePromptReturnedToMcpHost: false,
1708
+ configuredProviderReceivesAdapterInput: true,
1709
+ configuredProviderInvisible: false,
1710
+ },
1711
+ };
1712
+ }
1713
+
1714
+ export function enigma_start_private_session(input = {}) {
1715
+ input = validateToolArguments('enigma_start_private_session', input);
1716
+ const intent = input.intent?.trim() || 'private question';
1717
+ const model = input.model?.trim() || null;
1718
+ return {
1719
+ schema: 'enigma.private_session_handoff.v1',
1720
+ url: privateSessionUrl({ intent, ...(model ? { model } : {}) }),
1721
+ intent,
1722
+ model,
1723
+ resourceUri: PRIVATE_SESSION_HANDOFF_RESOURCE_URI,
1724
+ embedEnabled: true,
1725
+ hostBoundary: 'The workspace keeps the prompt out of the MCP host chat. The configured provider receives Enigma-prepared adapter input.',
1726
+ providerBoundary: 'Anchor Guard may mask email, wallet, IPv4, formatted phone, and base58 patterns. It does not make arbitrary sensitive prose invisible to the configured provider.',
1727
+ instructionsForHost: [
1728
+ 'Do not ask the user to paste the sensitive prompt into the host chat.',
1729
+ 'Do not place the sensitive prompt in an MCP tool argument.',
1730
+ 'Open the Enigma private session and let the user type there.',
1731
+ ],
1732
+ instructionsForUser: [
1733
+ 'Open the Enigma private workspace.',
1734
+ 'Choose a configured route if needed.',
1735
+ 'Type the actual private prompt inside Enigma.',
1736
+ 'The configured provider receives the prepared adapter input when you send.',
1737
+ ],
1738
+ };
1739
+ }
765
1740
  export function enigma_standard_memory_prompt(input = {}) {
766
1741
  const args = input?.arguments && typeof input.arguments === 'object' && !Array.isArray(input.arguments)
767
1742
  ? input.arguments
@@ -788,66 +1763,140 @@ export function enigma_standard_memory_prompt(input = {}) {
788
1763
  };
789
1764
  }
790
1765
 
1766
+ function requireInferenceRuntime(capability) {
1767
+ if (!configuredInferenceRuntime || typeof configuredInferenceRuntime[capability] !== 'object') {
1768
+ throw new Error(`Enigma ${capability} inference runtime is not configured`);
1769
+ }
1770
+ return configuredInferenceRuntime;
1771
+ }
1772
+
1773
+ export async function enigma_complete(input = {}) {
1774
+ input = validateToolArguments('enigma_complete', input);
1775
+ const runtime = requireInferenceRuntime('chat');
1776
+ return runtime.chat.complete({
1777
+ messages: input.messages,
1778
+ model: input.model || 'enigma-auto',
1779
+ privacyMode: input.privacy_mode || 'smart',
1780
+ temperature: input.temperature,
1781
+ maxTokens: input.max_tokens,
1782
+ reasoningEffort: input.reasoning_effort,
1783
+ webSearch: input.web_search === true,
1784
+ requirePrivateRoute: input.require_private_route === true,
1785
+ stream: false,
1786
+ });
1787
+ }
1788
+
1789
+ export async function enigma_generate_image(input = {}) {
1790
+ input = validateToolArguments('enigma_generate_image', input);
1791
+ const runtime = requireInferenceRuntime('images');
1792
+ return runtime.images.generate({
1793
+ prompt: input.prompt,
1794
+ model: input.model,
1795
+ negativePrompt: input.negative_prompt,
1796
+ width: input.width,
1797
+ height: input.height,
1798
+ steps: input.steps,
1799
+ cfgScale: input.cfg_scale,
1800
+ variants: input.variants,
1801
+ format: input.format,
1802
+ seed: input.seed,
1803
+ });
1804
+ }
1805
+
1806
+ export async function enigma_x402_execute(input = {}) {
1807
+ input = validateToolArguments('enigma_x402_execute', input);
1808
+ if (!configuredX402Client) throw new Error('Enigma x402 execution client is not configured');
1809
+ const headers = { ...(input.headers || {}) };
1810
+ const body = input.body === undefined
1811
+ ? undefined
1812
+ : typeof input.body === 'string'
1813
+ ? input.body
1814
+ : JSON.stringify(input.body);
1815
+ if (body !== undefined && !Object.keys(headers).some((name) => name.toLowerCase() === 'content-type')) {
1816
+ headers['Content-Type'] = 'application/json';
1817
+ }
1818
+ const execution = await configuredX402Client(input.url, { method: input.method || 'GET', headers, body });
1819
+ const maximum = input.max_response_bytes || 262144;
1820
+ const declared = Number(execution.response.headers.get('content-length'));
1821
+ if (Number.isFinite(declared) && declared > maximum) {
1822
+ await execution.response.body?.cancel?.();
1823
+ throw new Error('x402 resource response exceeds the configured limit');
1824
+ }
1825
+ const bytes = Buffer.from(await execution.response.arrayBuffer());
1826
+ if (bytes.length > maximum) throw new Error('x402 resource response exceeds the configured limit');
1827
+ const contentType = execution.response.headers.get('content-type') || 'application/octet-stream';
1828
+ let responseBody = bytes.toString('utf8');
1829
+ if (contentType.includes('application/json')) {
1830
+ try { responseBody = JSON.parse(responseBody); } catch { throw new Error('x402 resource returned invalid JSON'); }
1831
+ }
1832
+ return {
1833
+ schema: 'enigma.x402_execution.v1',
1834
+ status: execution.response.status,
1835
+ contentType,
1836
+ body: responseBody,
1837
+ settlement: execution.settlement,
1838
+ paymentRequired: execution.paymentRequired,
1839
+ };
1840
+ }
1841
+
791
1842
  export async function enigma_init(input = {}) {
792
1843
  input = validateToolArguments('enigma_init', input);
793
1844
  const path = bundlePath(input);
794
- if (await fileExists(path)) {
795
- const bundle = await readJson(path);
796
- if (bundle.schema !== 'enigma.vault_bundle.v1') throw new Error(`Unsupported Enigma bundle schema: ${bundle.schema ?? '<missing>'}`);
797
- return bundleSummary(path, bundle, false);
798
- }
799
-
800
- const vault = createVault({
801
- tenant_id: input.tenant_id,
802
- subject_id: input.subject_id,
803
- actor_id: input.actor_id,
804
- policy_id: input.policy_id,
1845
+ const credentials = bundleCredentials(input);
1846
+ return withBundleFileLock(path, async () => {
1847
+ if (await fileExists(path)) {
1848
+ const state = await loadState(path, input);
1849
+ return bundleSummary(path, state.stored, false);
1850
+ }
1851
+ const vault = createVault({
1852
+ tenant_id: input.tenant_id,
1853
+ subject_id: input.subject_id,
1854
+ actor_id: input.actor_id,
1855
+ policy_id: input.policy_id,
1856
+ passphrase: credentials.passphrase,
1857
+ });
1858
+ const bundle = await persistState(path, vault, { ...input, lockHeld: true, expectedRevision: null });
1859
+ return bundleSummary(path, bundle, true);
805
1860
  });
806
- const bundle = await persistState(path, vault);
807
- return bundleSummary(path, bundle, true);
808
1861
  }
809
1862
 
810
1863
  export async function enigma_remember(input = {}) {
811
1864
  input = validateToolArguments('enigma_remember', input);
812
1865
  const path = bundlePath(input);
813
- const { vault, passport } = await loadState(path);
814
- const result = remember({
1866
+ const { result } = await withVaultTransaction(path, input, ({ vault, passport }) => remember({
815
1867
  vault,
816
1868
  passport,
817
1869
  text: input.text,
818
1870
  purpose: input.purpose ?? 'mcp_memory',
819
1871
  purpose_tags: Array.isArray(input.tags) ? input.tags : [],
820
1872
  metadata: input.metadata ?? {},
821
- });
822
- await persistState(path, vault);
1873
+ }));
823
1874
  return { ok: true, memory_addr: result.memory_addr, receipt_id: result.receipt?.receipt_id };
824
1875
  }
825
1876
 
826
1877
  export async function enigma_search(input = {}) {
827
1878
  input = validateToolArguments('enigma_search', input);
828
1879
  const path = bundlePath(input);
829
- const { vault, passport } = await loadState(path);
830
- if (input.memory_addr) {
831
- const result = recall({ vault, passport, memory_addr: input.memory_addr, purpose: input.purpose ?? 'mcp_search' });
832
- await persistState(path, vault);
833
- return result;
834
- }
835
- const pack = compileContextPack({
836
- vault,
837
- passport,
838
- query: input.query ?? '',
839
- purpose: input.purpose ?? 'mcp_search',
840
- limit: Number(input.limit ?? 8),
1880
+ const { result } = await withVaultTransaction(path, input, ({ vault, passport }) => {
1881
+ if (input.memory_addr) {
1882
+ return recall({ vault, passport, memory_addr: input.memory_addr, purpose: input.purpose ?? 'mcp_search' });
1883
+ }
1884
+ const pack = compileContextPack({
1885
+ vault,
1886
+ passport,
1887
+ query: input.query ?? '',
1888
+ purpose: input.purpose ?? 'mcp_search',
1889
+ limit: Number(input.limit ?? 8),
1890
+ });
1891
+ return { memories: pack.memories ?? [], receipts: pack.retrieval_receipts ?? pack.receipts ?? [] };
841
1892
  });
842
- await persistState(path, vault);
843
- return { memories: pack.memories ?? [], receipts: pack.retrieval_receipts ?? pack.receipts ?? [] };
1893
+ return result;
844
1894
  }
845
1895
 
846
1896
  export async function enigma_context_pack(input = {}) {
847
1897
  input = validateToolArguments('enigma_context_pack', input);
848
1898
  const path = bundlePath(input);
849
- const { vault, passport } = await loadState(path);
850
- const pack = compileContextPack({
1899
+ const { result } = await withVaultTransaction(path, input, ({ vault, passport }) => compileContextPack({
851
1900
  vault,
852
1901
  passport,
853
1902
  query: input.query ?? '',
@@ -858,29 +1907,107 @@ export async function enigma_context_pack(input = {}) {
858
1907
  currency: input.currency,
859
1908
  limit: Number(input.limit ?? 8),
860
1909
  memory_addresses: input.memory_addresses,
861
- });
862
- await persistState(path, vault);
863
- return pack;
1910
+ }));
1911
+ return result;
864
1912
  }
865
1913
 
866
1914
  export async function enigma_delete(input = {}) {
867
1915
  input = validateToolArguments('enigma_delete', input);
868
1916
  const path = bundlePath(input);
869
- const { vault, passport } = await loadState(path);
870
- const result = deleteMemory({
1917
+ const { result } = await withVaultTransaction(path, input, ({ vault, passport }) => deleteMemory({
871
1918
  vault,
872
1919
  passport,
873
1920
  memory_addr: input.memory_addr,
874
1921
  reason: input.reason ?? 'mcp_delete',
875
- });
876
- await persistState(path, vault);
1922
+ }));
877
1923
  return { ok: true, memory_addr: result.memory_addr, receipt_id: result.receipt?.receipt_id };
878
1924
  }
879
1925
 
1926
+ export async function enigma_vault_rotate(input = {}) {
1927
+ input = validateToolArguments('enigma_vault_rotate', input);
1928
+ const path = bundlePath(input);
1929
+ const { result } = await withVaultTransaction(path, input, ({ vault }) => rotateContentKey({
1930
+ vault,
1931
+ key_id: input.key_id,
1932
+ }));
1933
+ return { ok: true, operation: 'key_rotate', ...result };
1934
+ }
1935
+
1936
+ export async function enigma_vault_seal(input = {}) {
1937
+ input = validateToolArguments('enigma_vault_seal', input);
1938
+ const path = bundlePath(input);
1939
+ const { result } = await withVaultTransaction(path, input, ({ vault }) => sealContentKeyEpoch({
1940
+ vault,
1941
+ epoch: input.epoch,
1942
+ }));
1943
+ return { ok: true, operation: 'key_seal', ...result };
1944
+ }
1945
+
1946
+ export async function enigma_vault_reset(input = {}) {
1947
+ input = validateToolArguments('enigma_vault_reset', input);
1948
+ const path = bundlePath(input);
1949
+ const { result } = await withVaultTransaction(path, input, ({ vault }) => resetVaultContentKeys({
1950
+ vault,
1951
+ key_id: input.key_id,
1952
+ }));
1953
+ return { ok: true, operation: 'key_reset', ...result };
1954
+ }
1955
+
880
1956
  export async function enigma_verify_receipts(input = {}) {
881
1957
  input = validateToolArguments('enigma_verify_receipts', input);
882
- const bundle = input.bundle ?? await readJson(bundlePath(input));
883
- return verifyBundle(bundle);
1958
+ if (input.bundle) return verifyBundle(input.bundle);
1959
+ const path = bundlePath(input);
1960
+ bundleCredentials(input);
1961
+ const stored = await readJson(path);
1962
+ const report = verifyBundle(stored);
1963
+ if (!report.ok) return report;
1964
+ await loadState(path, input);
1965
+ return report;
1966
+ }
1967
+
1968
+ async function activeSwarmAtoms(input) {
1969
+ const path = bundlePath(input);
1970
+ if (!(await fileExists(path))) return [];
1971
+ const { result } = await withVaultTransaction(path, input, ({ vault, passport }) => {
1972
+ const atoms = [];
1973
+ for (const memoryAddr of [...vault.activeAddresses].sort()) {
1974
+ const recalled = recall({
1975
+ vault,
1976
+ passport,
1977
+ memory_addr: memoryAddr,
1978
+ purpose: 'mcp_swarm_context',
1979
+ });
1980
+ const { content_ciphertext: ignoredCiphertext, ...memory } = recalled.memory;
1981
+ atoms.push({ ...memory, content: recalled.content });
1982
+ }
1983
+ return atoms;
1984
+ });
1985
+ return result;
1986
+ }
1987
+
1988
+ export async function enigma_swarm_register_agent(input = {}) {
1989
+ input = validateToolArguments('enigma_swarm_register_agent', input);
1990
+ return routeSwarmRegisterAgent(input);
1991
+ }
1992
+
1993
+ export async function enigma_graph_query(input = {}) {
1994
+ input = validateToolArguments('enigma_graph_query', input);
1995
+ return routeGraphQuery({ ...input, atoms: await activeSwarmAtoms(input) });
1996
+ }
1997
+
1998
+ export async function enigma_cluster_memories(input = {}) {
1999
+ input = validateToolArguments('enigma_cluster_memories', input);
2000
+ return routeClusterMemories({ ...input, atoms: await activeSwarmAtoms(input) });
2001
+ }
2002
+
2003
+ export async function enigma_swarm_sync(input = {}) {
2004
+ input = validateToolArguments('enigma_swarm_sync', input);
2005
+ return routeSwarmSync({ ...input, atoms: await activeSwarmAtoms(input) });
2006
+ }
2007
+
2008
+ export async function enigma_immune_ingress(input = {}) {
2009
+ input = validateToolArguments('enigma_immune_ingress', input);
2010
+ return createMcpImmuneIngressReport(input);
884
2011
  }
885
2012
 
886
2013
  export async function enigma_meter_usage(input = {}) {
@@ -943,13 +2070,130 @@ export async function enigma_settlement_batch(input = {}) {
943
2070
  }
944
2071
 
945
2072
 
2073
+ export async function enigma_vault_create_record(input = {}) {
2074
+ input = validateToolArguments('enigma_vault_create_record', input);
2075
+ return defaultEnclave.createRecord(input.plaintext, input.metadata || {});
2076
+ }
2077
+
2078
+ export async function enigma_vault_zeroize_record(input = {}) {
2079
+ input = validateToolArguments('enigma_vault_zeroize_record', input);
2080
+ return defaultEnclave.zeroizeRecord(input.recordId);
2081
+ }
2082
+
2083
+ export async function enigma_verify_erasure_attestation(input = {}) {
2084
+ input = validateToolArguments('enigma_verify_erasure_attestation', input);
2085
+ return AttestationVerifier.verify(input.attestation, {
2086
+ // Simulated attestations are developer-mode only: must be explicitly enabled.
2087
+ allowSimulated: input.allowSimulated ?? false,
2088
+ allowlistedSimulatedKeyPem: defaultEnclave.publicKeyPem,
2089
+ expectedPcr: input.expectedPcr,
2090
+ });
2091
+ }
2092
+
2093
+ export async function enigma_nullifier_record_tombstone(input = {}) {
2094
+ input = validateToolArguments('enigma_nullifier_record_tombstone', input);
2095
+ const { PublicKey } = await import('@solana/web3.js');
2096
+
2097
+ const programId = 'E18KZR1Jq8fG3FFACpoHsE21ME3t1ftKPEwXL7cFWkoZ';
2098
+ const programKey = new PublicKey(programId);
2099
+ // Strict 32-byte hex validation — no padding, no truncation, no silent mutation.
2100
+ const HEX32_RE = /^(0x)?[0-9a-fA-F]{64}$/;
2101
+ if (!HEX32_RE.test(input.commitment)) {
2102
+ throw new Error('enigma_nullifier_record_tombstone: commitment must be exactly 32 bytes of hex (64 hex chars, optional 0x prefix)');
2103
+ }
2104
+ if (!HEX32_RE.test(input.nullifierHash)) {
2105
+ throw new Error('enigma_nullifier_record_tombstone: nullifierHash must be exactly 32 bytes of hex (64 hex chars, optional 0x prefix)');
2106
+ }
2107
+ const commitmentClean = input.commitment.replace(/^0x/, '');
2108
+ const nullifierClean = input.nullifierHash.replace(/^0x/, '');
2109
+
2110
+ const commitmentBytes = Buffer.from(commitmentClean, 'hex');
2111
+ const nullifierBytes = Buffer.from(nullifierClean, 'hex');
2112
+ if (commitmentBytes.length !== 32 || nullifierBytes.length !== 32) {
2113
+ throw new Error('enigma_nullifier_record_tombstone: decoded values must be exactly 32 bytes');
2114
+ }
2115
+
2116
+ // Owner is the required Signer; the tombstone PDA is derived from owner + commitment.
2117
+ let ownerKey;
2118
+ try {
2119
+ ownerKey = new PublicKey(input.ownerPubkey);
2120
+ } catch {
2121
+ throw new Error('enigma_nullifier_record_tombstone: ownerPubkey must be a valid base58 public key');
2122
+ }
2123
+
2124
+ const [commitmentPda, commitmentBump] = PublicKey.findProgramAddressSync(
2125
+ [Buffer.from('commitment'), ownerKey.toBuffer(), commitmentBytes],
2126
+ programKey,
2127
+ );
2128
+ const [tombstonePda, tombstoneBump] = PublicKey.findProgramAddressSync(
2129
+ [Buffer.from('tombstone'), ownerKey.toBuffer(), commitmentBytes],
2130
+ programKey,
2131
+ );
2132
+ const [registryPda, registryBump] = PublicKey.findProgramAddressSync(
2133
+ [Buffer.from('nullifier_registry')],
2134
+ programKey,
2135
+ );
2136
+
2137
+ // Anchor discriminator for global:record_revocation (owner-signed revocation record;
2138
+ // no PCR/attestation framing — physical deletion is out of scope).
2139
+ const discriminator = Buffer.from([108, 45, 14, 21, 217, 230, 163, 121]);
2140
+ const instructionData = Buffer.concat([
2141
+ discriminator,
2142
+ commitmentBytes,
2143
+ nullifierBytes,
2144
+ ]);
2145
+
2146
+ // Ordered account metas matching RecordRevocation (commitment_record, tombstone,
2147
+ // registry, owner, system). The commitment must be registered first — revocation
2148
+ // flips its is_nullified flag on-chain.
2149
+ const accounts = [
2150
+ { pubkey: commitmentPda.toBase58(), isSigner: false, isWritable: true, role: 'commitment_record', bump: commitmentBump },
2151
+ { pubkey: tombstonePda.toBase58(), isSigner: false, isWritable: true, role: 'tombstone', bump: tombstoneBump },
2152
+ { pubkey: registryPda.toBase58(), isSigner: false, isWritable: true, role: 'registry', bump: registryBump },
2153
+ { pubkey: ownerKey.toBase58(), isSigner: true, isWritable: true, role: 'owner' },
2154
+ { pubkey: '11111111111111111111111111111111', isSigner: false, isWritable: false, role: 'system_program' },
2155
+ ];
2156
+
2157
+ return {
2158
+ programId,
2159
+ instruction: 'record_revocation',
2160
+ discriminator: Array.from(discriminator),
2161
+ accounts,
2162
+ instructionDataHex: instructionData.toString('hex'),
2163
+ commitment: input.commitment,
2164
+ nullifierHash: input.nullifierHash,
2165
+ ownerPubkey: ownerKey.toBase58(),
2166
+ requiresOwnerSignature: true,
2167
+ requiresRegisteredCommitment: true,
2168
+ timestamp: new Date().toISOString(),
2169
+ broadcastReady: true,
2170
+ };
2171
+ }
2172
+
2173
+
2174
+ // enigma_verify_erasure_attestation deliberately excluded from RPC handlers (see
2175
+ // toolDescriptors note above): hardware attestation verification is not yet safe.
946
2176
  export const handlers = Object.freeze({
2177
+ enigma_private_handoff_health,
2178
+ enigma_routing_contract,
2179
+ enigma_start_private_session,
2180
+ enigma_complete,
2181
+ enigma_generate_image,
2182
+ enigma_x402_execute,
947
2183
  enigma_init,
948
2184
  enigma_remember,
949
2185
  enigma_search,
950
2186
  enigma_context_pack,
951
2187
  enigma_delete,
2188
+ enigma_vault_rotate,
2189
+ enigma_vault_seal,
2190
+ enigma_vault_reset,
952
2191
  enigma_verify_receipts,
2192
+ enigma_swarm_register_agent,
2193
+ enigma_graph_query,
2194
+ enigma_cluster_memories,
2195
+ enigma_swarm_sync,
2196
+ enigma_immune_ingress,
953
2197
  enigma_meter_usage,
954
2198
  enigma_settlement_job,
955
2199
  enigma_settlement_capacity,
@@ -957,6 +2201,9 @@ export const handlers = Object.freeze({
957
2201
  enigma_settlement_receipt,
958
2202
  enigma_settlement_verify,
959
2203
  enigma_settlement_batch,
2204
+ enigma_vault_create_record,
2205
+ enigma_vault_zeroize_record,
2206
+ enigma_nullifier_record_tombstone,
960
2207
  });
961
2208
 
962
2209
  function jsonRpcResult(id, result) {
@@ -987,6 +2234,28 @@ function toolHandler(name) {
987
2234
 
988
2235
  function toolCallContent(result, toolName) {
989
2236
  const verificationFailed = (toolName === 'enigma_verify_receipts' || toolName === 'enigma_settlement_verify') && result && typeof result === 'object' && result.ok === false;
2237
+ if (toolName === 'enigma_generate_image' && result && typeof result === 'object') {
2238
+ const content = [];
2239
+ const assets = (result.assets || []).map((asset, index) => {
2240
+ const mimeType = `image/${asset.format === 'jpg' ? 'jpeg' : asset.format || 'webp'}`;
2241
+ if (typeof asset.base64 === 'string') {
2242
+ content.push({ type: 'image', data: asset.base64, mimeType });
2243
+ return { index, format: asset.format, inline: true };
2244
+ }
2245
+ if (typeof asset.url === 'string' && asset.url.startsWith('data:')) {
2246
+ const match = /^data:([^;,]+);base64,(.+)$/s.exec(asset.url);
2247
+ if (match) {
2248
+ content.push({ type: 'image', data: match[2], mimeType: match[1] });
2249
+ return { index, format: asset.format, inline: true };
2250
+ }
2251
+ }
2252
+ content.push({ type: 'text', text: `Generated image ${index + 1}: ${asset.url}` });
2253
+ return { index, format: asset.format, url: asset.url };
2254
+ });
2255
+ const structuredContent = { ...result, assets };
2256
+ content.unshift({ type: 'text', text: JSON.stringify(structuredContent) });
2257
+ return { content, structuredContent, isError: false };
2258
+ }
990
2259
  return {
991
2260
  content: [{ type: 'text', text: JSON.stringify(result ?? null) }],
992
2261
  structuredContent: result ?? null,
@@ -1020,7 +2289,7 @@ function validateNoParams(params, method) {
1020
2289
  rejectAdditionalProperties(input, new Set(), `${method} params`);
1021
2290
  }
1022
2291
 
1023
- async function handleSingleJsonRpcRequest(request) {
2292
+ async function handleSingleJsonRpcRequest(request, context = {}) {
1024
2293
  if (!isRequestObject(request)) return jsonRpcError(null, JSON_RPC_ERROR.INVALID_REQUEST, 'Invalid Request');
1025
2294
 
1026
2295
  const notification = !hasJsonRpcId(request);
@@ -1031,6 +2300,12 @@ async function handleSingleJsonRpcRequest(request) {
1031
2300
  if (request.jsonrpc !== JSONRPC_VERSION || typeof request.method !== 'string') {
1032
2301
  return jsonRpcError(id, JSON_RPC_ERROR.INVALID_REQUEST, 'Invalid Request');
1033
2302
  }
2303
+ if (context.transport === 'streamable-http') {
2304
+ const requiredScopes = requiredScopesForMcpPayload(request);
2305
+ if (!context.principal || !Array.isArray(context.principal.scopes) || requiredScopes.some((scope) => !context.principal.scopes.includes(scope))) {
2306
+ return notification ? undefined : jsonRpcError(id, JSON_RPC_ERROR.INVALID_REQUEST, 'Insufficient OAuth scope.', { requiredScopes });
2307
+ }
2308
+ }
1034
2309
 
1035
2310
  try {
1036
2311
  switch (request.method) {
@@ -1054,7 +2329,7 @@ async function handleSingleJsonRpcRequest(request) {
1054
2329
  return notification ? undefined : jsonRpcResult(id, {});
1055
2330
  case 'tools/list':
1056
2331
  validateNoParams(request.params, 'tools/list');
1057
- return notification ? undefined : jsonRpcResult(id, { tools: toolDescriptors });
2332
+ return notification ? undefined : jsonRpcResult(id, { tools: context.transport === 'streamable-http' ? remoteToolDescriptors() : toolDescriptors });
1058
2333
  case 'resources/list':
1059
2334
  validateNoParams(request.params, 'resources/list');
1060
2335
  return notification ? undefined : jsonRpcResult(id, { resources: resourceDescriptors });
@@ -1063,11 +2338,32 @@ async function handleSingleJsonRpcRequest(request) {
1063
2338
  return notification ? undefined : jsonRpcResult(id, { resourceTemplates: [] });
1064
2339
  case 'resources/read': {
1065
2340
  const params = ensureParamsObject(request.params, 'resources/read');
1066
- rejectAdditionalProperties(params, new Set(['uri', 'bundlePath']), 'resources/read params');
2341
+ rejectAdditionalProperties(params, new Set(['uri', 'bundlePath', 'insecureDevMode']), 'resources/read params');
1067
2342
  requireString(params, 'uri', 'resources/read params');
1068
2343
  optionalString(params, 'bundlePath', 'resources/read params');
1069
- if (normalizeResourceUri(params.uri) !== PASSPORT_SUMMARY_RESOURCE_URI) throw invalidParams('Unknown resource.');
1070
- const result = await enigma_passport_summary_resource(inputWithBundlePath(params));
2344
+ optionalBoolean(params, 'insecureDevMode', 'resources/read params');
2345
+ const normalizedUri = normalizeResourceUri(params.uri);
2346
+ let result;
2347
+ if (normalizedUri === PASSPORT_SUMMARY_RESOURCE_URI) {
2348
+ result = await enigma_passport_summary_resource(await bindRemoteResourceInput(params, normalizedUri, context));
2349
+ } else if (normalizedUri === PRIVATE_SESSION_HANDOFF_RESOURCE_URI) {
2350
+ result = enigma_private_session_resource({ uri: normalizedUri });
2351
+ } else {
2352
+ throw invalidParams('Unknown resource.');
2353
+ }
2354
+ if (context.transport === 'streamable-http' && Array.isArray(result?.contents)) {
2355
+ result = {
2356
+ ...result,
2357
+ contents: result.contents.map((content) => {
2358
+ if (content?.mimeType !== 'application/json' || typeof content.text !== 'string') return content;
2359
+ try {
2360
+ return { ...content, text: JSON.stringify(sanitizeRemoteCustodyOutput(JSON.parse(content.text), context)) };
2361
+ } catch {
2362
+ return content;
2363
+ }
2364
+ }),
2365
+ };
2366
+ }
1071
2367
  return notification ? undefined : jsonRpcResult(id, result);
1072
2368
  }
1073
2369
  case 'prompts/list':
@@ -1087,9 +2383,11 @@ async function handleSingleJsonRpcRequest(request) {
1087
2383
  requireString(params, 'name', 'tools/call params');
1088
2384
  const handler = toolHandler(params.name);
1089
2385
  if (!handler) throw invalidParams('Unknown tool.');
1090
- const args = toolArguments(params.name, params);
2386
+ const args = context.transport === 'streamable-http'
2387
+ ? await bindRemoteToolArguments(params.name, params.arguments ?? {}, context)
2388
+ : toolArguments(params.name, params);
1091
2389
  try {
1092
- const result = await handler(args);
2390
+ const result = sanitizeRemoteCustodyOutput(await handler(args), context);
1093
2391
  return notification ? undefined : jsonRpcResult(id, toolCallContent(result, params.name));
1094
2392
  } catch (error) {
1095
2393
  return notification ? undefined : jsonRpcResult(id, toolCallError(error));
@@ -1105,13 +2403,13 @@ async function handleSingleJsonRpcRequest(request) {
1105
2403
  }
1106
2404
  }
1107
2405
 
1108
- export async function handleJsonRpcRequest(request) {
1109
- if (!Array.isArray(request)) return handleSingleJsonRpcRequest(request);
2406
+ export async function handleJsonRpcRequest(request, context = {}) {
2407
+ if (!Array.isArray(request)) return handleSingleJsonRpcRequest(request, context);
1110
2408
  if (request.length === 0) return jsonRpcError(null, JSON_RPC_ERROR.INVALID_REQUEST, 'Invalid Request');
1111
2409
 
1112
2410
  const responses = [];
1113
2411
  for (const item of request) {
1114
- const response = await handleSingleJsonRpcRequest(item);
2412
+ const response = await handleSingleJsonRpcRequest(item, context);
1115
2413
  if (response !== undefined) responses.push(response);
1116
2414
  }
1117
2415
  return responses.length === 0 ? undefined : responses;
@@ -1165,12 +2463,25 @@ export default {
1165
2463
  resourceDescriptors,
1166
2464
  promptDescriptors,
1167
2465
  handlers,
2466
+ enigma_private_handoff_health,
2467
+ enigma_routing_contract,
2468
+ enigma_start_private_session,
2469
+ configureMcpInferenceRuntime,
2470
+ configureMcpX402Client,
2471
+ enigma_complete,
2472
+ enigma_generate_image,
2473
+ enigma_x402_execute,
1168
2474
  enigma_init,
1169
2475
  enigma_remember,
1170
2476
  enigma_search,
1171
2477
  enigma_context_pack,
1172
2478
  enigma_delete,
1173
2479
  enigma_verify_receipts,
2480
+ enigma_swarm_register_agent,
2481
+ enigma_graph_query,
2482
+ enigma_cluster_memories,
2483
+ enigma_swarm_sync,
2484
+ enigma_immune_ingress,
1174
2485
  enigma_meter_usage,
1175
2486
  enigma_settlement_job,
1176
2487
  enigma_settlement_capacity,
@@ -1180,6 +2491,8 @@ export default {
1180
2491
  enigma_settlement_batch,
1181
2492
  enigma_passport_summary_resource,
1182
2493
  enigma_standard_memory_prompt,
2494
+ createMcpImmuneIngressReport,
2495
+ assertMcpImmuneIngressPublicSafe,
1183
2496
  handleJsonRpcRequest,
1184
2497
  startStdioServer,
1185
2498
  };