@provnai/vex-sdk 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # VEX SDK for TypeScript (v1.5.0) 🛡js
2
+ ## Cognitive Routing & Silicon-Rooted Evidence
3
+
4
+ Official TypeScript implementation of the VEX Protocol. Designed to wrap agent tool-calls in a cryptographically verifiable envelope.
5
+
6
+ ### Installation
7
+ ```bash
8
+ npm install @provnai/vex-sdk
9
+ ```
10
+
11
+ ### 🚀 Usage
12
+
13
+ ```typescript
14
+ import { VexAgent, vexMiddleware } from '@provnai/vex-sdk';
15
+
16
+ const agent = new VexAgent({
17
+ identityKey: process.env.VEX_IDENTITY_KEY!,
18
+ vanguardUrl: 'https://vanguard.provn.ai'
19
+ });
20
+
21
+ // Use with Vercel AI SDK or custom tool loops
22
+ const securedResult = await agent.execute(
23
+ 'transfer_funds',
24
+ { amount: 1000, currency: 'USD' },
25
+ 'Authorize emergency liquidation'
26
+ );
27
+
28
+ console.log(`VEX Outcome: ${securedResult.outcome}`); // ALLOWED
29
+ ```
30
+
31
+ ---
32
+ **Note:** This SDK natively integrates with `@provncloud/sdk` for hardware-rooted trust.
33
+ 🛡⚓🚀
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Copyright 2026 ProvnAI
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ export interface VexConfig {
17
+ identityKey: string;
18
+ vanguardUrl: string;
19
+ }
20
+ export declare class VexAgent {
21
+ private config;
22
+ private sdk;
23
+ constructor(config: VexConfig);
24
+ private ensureSDK;
25
+ /**
26
+ * Executes a tool via the VEX verifiable execution loop.
27
+ */
28
+ execute(toolName: string, params: Record<string, any>, intentContext?: string): Promise<any>;
29
+ /**
30
+ * Manually construct a signed Evidence Capsule without dispatching it.
31
+ */
32
+ buildCapsule(toolName: string, params: Record<string, any>, intentContext?: string): Promise<any>;
33
+ /**
34
+ * Serializes the Evidence Capsule into the v0x03 Binary Wire format.
35
+ */
36
+ toBinary(capsule: any): Buffer;
37
+ private hashObject;
38
+ }
package/dist/agent.js ADDED
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright 2026 ProvnAI
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ var __importDefault = (this && this.__importDefault) || function (mod) {
18
+ return (mod && mod.__esModule) ? mod : { "default": mod };
19
+ };
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.VexAgent = void 0;
22
+ const axios_1 = __importDefault(require("axios"));
23
+ const builder_1 = require("./builder");
24
+ const sdk_1 = require("@provncloud/sdk");
25
+ class VexAgent {
26
+ constructor(config) {
27
+ this.sdk = null;
28
+ this.config = config;
29
+ }
30
+ async ensureSDK() {
31
+ if (!this.sdk) {
32
+ await (0, sdk_1.init)();
33
+ this.sdk = new sdk_1.ProvnSDK();
34
+ }
35
+ }
36
+ /**
37
+ * Executes a tool via the VEX verifiable execution loop.
38
+ */
39
+ async execute(toolName, params, intentContext) {
40
+ await this.ensureSDK();
41
+ // 1. Build Capsule
42
+ const capsuleData = await this.buildCapsule(toolName, params, intentContext);
43
+ const capsuleId = capsuleData.authority.capsule_id;
44
+ // 2. Dispatch to Vanguard
45
+ try {
46
+ const response = await axios_1.default.post(`${this.config.vanguardUrl}/dispatch`, capsuleData, {
47
+ headers: { 'Content-Type': 'application/json' }
48
+ });
49
+ let result = response.data;
50
+ // 3. Handle AEM (ESCALATE Loop)
51
+ let attempts = 0;
52
+ const maxAttempts = 15; // ~30 seconds
53
+ while (result.outcome === 'ESCALATE' && attempts < maxAttempts) {
54
+ await new Promise(resolve => setTimeout(resolve, 2000));
55
+ attempts++;
56
+ const pollResp = await axios_1.default.get(`${this.config.vanguardUrl}/capsule/${capsuleId}/status`, {
57
+ headers: { 'Accept': 'application/json' }
58
+ });
59
+ result = pollResp.data;
60
+ }
61
+ if (result.outcome === 'HALT') {
62
+ throw new Error(`VEX Execution HALTED: ${result.reason_code}`);
63
+ }
64
+ if (result.outcome === 'ESCALATE') {
65
+ throw new Error('VEX Escalated resolution timed out.');
66
+ }
67
+ // 4. Store Capability Token
68
+ if (result.capability_token) {
69
+ this.currentToken = result.capability_token;
70
+ }
71
+ return result;
72
+ }
73
+ catch (error) {
74
+ console.error('VEX Execution Failed:', error.response?.data || error.message);
75
+ throw error;
76
+ }
77
+ }
78
+ /**
79
+ * Manually construct a signed Evidence Capsule without dispatching it.
80
+ */
81
+ async buildCapsule(toolName, params, intentContext) {
82
+ await this.ensureSDK();
83
+ // This is a simplified implementation for the SDK.
84
+ // It demonstrates how the builder is leveraged.
85
+ const intent = {
86
+ request_sha256: this.hashObject(params),
87
+ confidence: 1.0,
88
+ capabilities: ['sdk_execution']
89
+ };
90
+ if (intentContext) {
91
+ intent.intent_context = intentContext;
92
+ }
93
+ const authority = {
94
+ capsule_id: require('crypto').randomUUID(),
95
+ outcome: 'ALLOW',
96
+ reason_code: 'SDK_GENERATED',
97
+ trace_root: '00'.repeat(32),
98
+ nonce: Date.now(),
99
+ prev_hash: '00'.repeat(32), // Start of chain
100
+ supervision: {
101
+ branch_completeness: 1.0,
102
+ contradictions: 0,
103
+ confidence: 1.0
104
+ }
105
+ };
106
+ const identity = {
107
+ aid: '00'.repeat(32), // Placeholder for hardware AID
108
+ identity_type: 'software_sim',
109
+ pcrs: { '0': '00'.repeat(32) }
110
+ };
111
+ const witness = {
112
+ chora_node_id: 'local_witness',
113
+ receipt_hash: '00'.repeat(32),
114
+ timestamp: Math.floor(Date.now() / 1000)
115
+ };
116
+ const intent_hash = builder_1.VEPBuilder.hashSegment(intent);
117
+ const authority_hash = builder_1.VEPBuilder.hashSegment(authority);
118
+ const identity_hash = builder_1.VEPBuilder.hashSegment(identity);
119
+ const witness_hash = builder_1.VEPBuilder.hashSegment(witness, false); // Minimal scope
120
+ const capsule_root = builder_1.VEPBuilder.calculateCapsuleRoot({
121
+ intent_hash,
122
+ authority_hash,
123
+ identity_hash,
124
+ witness_hash
125
+ });
126
+ // Sign the capsule_root
127
+ const claim = this.sdk.createClaimNow(capsule_root);
128
+ const keyPair = {
129
+ private_key: this.config.identityKey,
130
+ public_key: '00'.repeat(32) // In real use, this comes from the config/key derivation
131
+ };
132
+ this.sdk.setKeypair(keyPair);
133
+ const signedClaim = this.sdk.signClaim(claim);
134
+ const signature = Buffer.from(signedClaim.signature, 'hex');
135
+ return {
136
+ intent,
137
+ authority,
138
+ identity,
139
+ witness,
140
+ intent_hash,
141
+ authority_hash,
142
+ identity_hash,
143
+ witness_hash,
144
+ capsule_root,
145
+ crypto: {
146
+ algo: 'ed25519',
147
+ signature_scope: 'capsule_root',
148
+ signature_b64: signature.toString('base64'),
149
+ signature_raw: signature // Keep raw for binary spec
150
+ }
151
+ };
152
+ }
153
+ /**
154
+ * Serializes the Evidence Capsule into the v0x03 Binary Wire format.
155
+ */
156
+ toBinary(capsule) {
157
+ const { canonicalize } = require('json-canonicalize');
158
+ // --- Header (76 Bytes) ---
159
+ const header = Buffer.alloc(76);
160
+ header.write('VEP', 0);
161
+ header.writeUInt8(0x03, 3);
162
+ header.write(capsule.identity.aid.replace(/-/g, ''), 4, 'hex'); // Ensure hex
163
+ header.write(capsule.capsule_root, 36, 'hex');
164
+ header.writeBigUInt64BE(BigInt(capsule.authority.nonce), 68);
165
+ // --- TLV Body ---
166
+ const packTLV = (tag, data) => {
167
+ const tlv = Buffer.alloc(5 + data.length);
168
+ tlv.writeUInt8(tag, 0);
169
+ tlv.writeUInt32BE(data.length, 1);
170
+ data.copy(tlv, 5);
171
+ return tlv;
172
+ };
173
+ const segments = [
174
+ packTLV(0x01, Buffer.from(canonicalize(capsule.intent))),
175
+ packTLV(0x02, Buffer.from(canonicalize(capsule.authority))),
176
+ packTLV(0x03, Buffer.from(canonicalize(capsule.identity))),
177
+ packTLV(0x05, Buffer.from(canonicalize(capsule.witness))),
178
+ packTLV(0x06, capsule.crypto.signature_raw)
179
+ ];
180
+ return Buffer.concat([header, ...segments, header]);
181
+ }
182
+ hashObject(obj) {
183
+ const { canonicalize } = require('json-canonicalize');
184
+ const crypto = require('crypto');
185
+ const canonicalJSON = canonicalize(obj);
186
+ return crypto.createHash('sha256').update(canonicalJSON).digest('hex');
187
+ }
188
+ }
189
+ exports.VexAgent = VexAgent;
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Copyright 2026 ProvnAI
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ export interface IntentSegment {
17
+ request_sha256: string;
18
+ confidence: number;
19
+ capabilities: string[];
20
+ magpie_source?: string;
21
+ }
22
+ export interface AuthoritySegment {
23
+ capsule_id: string;
24
+ outcome: 'ALLOW' | 'HALT' | 'ESCALATE';
25
+ reason_code: string;
26
+ trace_root: string;
27
+ nonce: number;
28
+ prev_hash?: string;
29
+ supervision?: {
30
+ branch_completeness?: number;
31
+ contradictions?: number;
32
+ confidence?: number;
33
+ };
34
+ gate_sensors?: Record<string, any>;
35
+ }
36
+ export interface IdentitySegment {
37
+ aid: string;
38
+ identity_type: string;
39
+ pcrs: Record<string, string>;
40
+ }
41
+ export interface WitnessSegment {
42
+ chora_node_id: string;
43
+ receipt_hash: string;
44
+ timestamp: number;
45
+ }
46
+ export interface VexPillars {
47
+ intent: IntentSegment;
48
+ authority: AuthoritySegment;
49
+ identity: IdentitySegment;
50
+ witness: WitnessSegment;
51
+ }
52
+ export declare class VEPBuilder {
53
+ /**
54
+ * Computes the SHA-256 hash of a JCS canonicalized object.
55
+ */
56
+ static hashSegment(segment: any, inclusive?: boolean): string;
57
+ /**
58
+ * Calculates the capsule_root commitment using a 4-leaf binary Merkle tree
59
+ * with domain separation (0x00 for leaves, 0x01 for internal nodes).
60
+ * Pillar Order: Intent, Authority, Identity, Witness.
61
+ */
62
+ static calculateCapsuleRoot(hashes: {
63
+ intent_hash: string;
64
+ authority_hash: string;
65
+ identity_hash: string;
66
+ witness_hash: string;
67
+ }): string;
68
+ }
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright 2026 ProvnAI
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ var desc = Object.getOwnPropertyDescriptor(m, k);
20
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
+ desc = { enumerable: true, get: function() { return m[k]; } };
22
+ }
23
+ Object.defineProperty(o, k2, desc);
24
+ }) : (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ o[k2] = m[k];
27
+ }));
28
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
29
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
30
+ }) : function(o, v) {
31
+ o["default"] = v;
32
+ });
33
+ var __importStar = (this && this.__importStar) || (function () {
34
+ var ownKeys = function(o) {
35
+ ownKeys = Object.getOwnPropertyNames || function (o) {
36
+ var ar = [];
37
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
38
+ return ar;
39
+ };
40
+ return ownKeys(o);
41
+ };
42
+ return function (mod) {
43
+ if (mod && mod.__esModule) return mod;
44
+ var result = {};
45
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
46
+ __setModuleDefault(result, mod);
47
+ return result;
48
+ };
49
+ })();
50
+ Object.defineProperty(exports, "__esModule", { value: true });
51
+ exports.VEPBuilder = void 0;
52
+ const crypto = __importStar(require("crypto"));
53
+ const { canonicalize } = require('json-canonicalize');
54
+ class VEPBuilder {
55
+ /**
56
+ * Computes the SHA-256 hash of a JCS canonicalized object.
57
+ */
58
+ static hashSegment(segment, inclusive = true) {
59
+ let dataToHash = segment;
60
+ if (!inclusive) {
61
+ // Witness pillar uses Minimal scope (Explicit fields only, EXCLUDING receipt_hash)
62
+ const minimal = {
63
+ chora_node_id: segment.chora_node_id,
64
+ timestamp: segment.timestamp
65
+ };
66
+ dataToHash = minimal;
67
+ }
68
+ const canonicalJSON = canonicalize(dataToHash);
69
+ return crypto.createHash('sha256').update(canonicalJSON).digest('hex');
70
+ }
71
+ /**
72
+ * Calculates the capsule_root commitment using a 4-leaf binary Merkle tree
73
+ * with domain separation (0x00 for leaves, 0x01 for internal nodes).
74
+ * Pillar Order: Intent, Authority, Identity, Witness.
75
+ */
76
+ static calculateCapsuleRoot(hashes) {
77
+ const hashLeaf = (data_hex) => {
78
+ const leaf = Buffer.concat([Buffer.from([0x00]), Buffer.from(data_hex, 'hex')]);
79
+ return crypto.createHash('sha256').update(leaf).digest();
80
+ };
81
+ const hashInternal = (left, right) => {
82
+ const internal = Buffer.concat([Buffer.from([0x01]), left, right]);
83
+ return crypto.createHash('sha256').update(internal).digest();
84
+ };
85
+ // 1. Leaf Hashes
86
+ const hi = hashLeaf(hashes.intent_hash);
87
+ const ha = hashLeaf(hashes.authority_hash);
88
+ const hid = hashLeaf(hashes.identity_hash);
89
+ const hw = hashLeaf(hashes.witness_hash);
90
+ // 2. Layer 1 (Internal Nodes)
91
+ const h12 = hashInternal(hi, ha);
92
+ const h34 = hashInternal(hid, hw);
93
+ // 3. Root
94
+ const rootDigest = hashInternal(h12, h34);
95
+ return rootDigest.toString('hex');
96
+ }
97
+ }
98
+ exports.VEPBuilder = VEPBuilder;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Copyright 2026 ProvnAI
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ export * from './agent';
17
+ export * from './builder';
18
+ export * from './middleware';
19
+ export type { VexConfig } from './agent';
20
+ export type { IntentSegment, AuthoritySegment, IdentitySegment, WitnessSegment, VexPillars } from './builder';
package/dist/index.js ADDED
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright 2026 ProvnAI
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ var desc = Object.getOwnPropertyDescriptor(m, k);
20
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
+ desc = { enumerable: true, get: function() { return m[k]; } };
22
+ }
23
+ Object.defineProperty(o, k2, desc);
24
+ }) : (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ o[k2] = m[k];
27
+ }));
28
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
29
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
30
+ };
31
+ Object.defineProperty(exports, "__esModule", { value: true });
32
+ __exportStar(require("./agent"), exports);
33
+ __exportStar(require("./builder"), exports);
34
+ __exportStar(require("./middleware"), exports);
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Copyright 2026 ProvnAI
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ /**
17
+ * Vercel AI SDK compatible middleware for VEX-secured tool execution.
18
+ */
19
+ export declare const vexMiddleware: (config: {
20
+ identityKey: string;
21
+ vanguardUrl: string;
22
+ }) => {
23
+ onToolCall({ toolName, args }: {
24
+ toolName: string;
25
+ args: any;
26
+ }): Promise<{
27
+ status: string;
28
+ vex_root: any;
29
+ }>;
30
+ };
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright 2026 ProvnAI
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.vexMiddleware = void 0;
19
+ const agent_1 = require("./agent");
20
+ /**
21
+ * Vercel AI SDK compatible middleware for VEX-secured tool execution.
22
+ */
23
+ const vexMiddleware = (config) => {
24
+ const agent = new agent_1.VexAgent(config);
25
+ return {
26
+ // This is a pattern used by Vercel AI SDK to intercept tool calls
27
+ async onToolCall({ toolName, args }) {
28
+ console.log(`[VEX] Securing tool call: ${toolName}`);
29
+ try {
30
+ const result = await agent.execute(toolName, args, `Vercel AI SDK forced verification for ${toolName}`);
31
+ // If execute doesn't throw, it means Vanguard/VEX sidecar allowed it
32
+ return {
33
+ status: 'verified',
34
+ vex_root: result.capsule_root
35
+ };
36
+ }
37
+ catch (error) {
38
+ console.error(`[VEX] Blocked tool execution: ${toolName}`);
39
+ throw new Error(`VEX Verification Failed: ${toolName} execution not authorized. Reason: ${error instanceof Error ? error.message : String(error)}`);
40
+ }
41
+ }
42
+ };
43
+ };
44
+ exports.vexMiddleware = vexMiddleware;
@@ -0,0 +1,36 @@
1
+ const tsParser = require('@typescript-eslint/parser');
2
+ const tsPlugin = require('@typescript-eslint/eslint-plugin');
3
+ const js = require('@eslint/js');
4
+
5
+ module.exports = [
6
+ js.configs.recommended,
7
+ {
8
+ files: ['src/**/*.ts'],
9
+ languageOptions: {
10
+ parser: tsParser,
11
+ globals: {
12
+ node: true,
13
+ jest: true,
14
+ process: true,
15
+ console: true,
16
+ Buffer: true,
17
+ setTimeout: true,
18
+ Math: true,
19
+ require: true,
20
+ module: true
21
+ }
22
+ },
23
+ plugins: {
24
+ '@typescript-eslint': tsPlugin
25
+ },
26
+ rules: {
27
+ ...tsPlugin.configs.recommended.rules,
28
+ '@typescript-eslint/no-explicit-any': 'warn',
29
+ '@typescript-eslint/no-unused-vars': 'warn',
30
+ '@typescript-eslint/no-require-imports': 'off',
31
+ 'no-console': 'off',
32
+ 'semi': ['error', 'always'],
33
+ 'quotes': ['error', 'single']
34
+ }
35
+ }
36
+ ];
package/jest.config.js ADDED
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ preset: 'ts-jest',
3
+ testEnvironment: 'node',
4
+ testMatch: ['**/tests/**/*.test.ts'],
5
+ };
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@provnai/vex-sdk",
3
+ "version": "1.5.0",
4
+ "description": "High-level routing and cognitive abstraction SDK for the VEX Protocol",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "test": "jest",
10
+ "lint": "eslint src/**/*.ts"
11
+ },
12
+ "dependencies": {
13
+ "@provncloud/sdk": "^0.3.0",
14
+ "axios": "^1.6.0",
15
+ "json-canonicalize": "^1.0.4"
16
+ },
17
+ "devDependencies": {
18
+ "@types/jest": "^29.5.0",
19
+ "@types/node": "^20.0.0",
20
+ "@typescript-eslint/eslint-plugin": "^8.57.1",
21
+ "@typescript-eslint/parser": "^8.57.1",
22
+ "@eslint/js": "^9.39.4",
23
+ "eslint": "^9.11.0",
24
+ "jest": "^29.5.0",
25
+ "ts-jest": "^29.1.0",
26
+ "typescript": "^5.0.0"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public"
30
+ }
31
+ }
package/src/agent.ts ADDED
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Copyright 2026 ProvnAI
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import axios from 'axios';
18
+ import { VEPBuilder, VexPillars } from './builder';
19
+ import { ProvnSDK, init } from '@provncloud/sdk';
20
+
21
+ export interface VexConfig {
22
+ identityKey: string; // Hex or Base64 Ed25519 private key
23
+ vanguardUrl: string; // Target proxy endpoint (McpVanguard/VEX sidecar)
24
+ }
25
+
26
+ export class VexAgent {
27
+ private config: VexConfig;
28
+ private sdk: ProvnSDK | null = null;
29
+
30
+ constructor(config: VexConfig) {
31
+ this.config = config;
32
+ }
33
+
34
+ private async ensureSDK() {
35
+ if (!this.sdk) {
36
+ await init();
37
+ this.sdk = new ProvnSDK();
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Executes a tool via the VEX verifiable execution loop.
43
+ */
44
+ async execute(toolName: string, params: Record<string, any>, intentContext?: string): Promise<any> {
45
+ await this.ensureSDK();
46
+
47
+ // 1. Build Capsule
48
+ const capsuleData = await this.buildCapsule(toolName, params, intentContext);
49
+ const capsuleId = capsuleData.authority.capsule_id;
50
+
51
+ // 2. Dispatch to Vanguard
52
+ try {
53
+ const response = await axios.post(`${this.config.vanguardUrl}/dispatch`, capsuleData, {
54
+ headers: { 'Content-Type': 'application/json' }
55
+ });
56
+ let result = response.data;
57
+
58
+ // 3. Handle AEM (ESCALATE Loop)
59
+ let attempts = 0;
60
+ const maxAttempts = 15; // ~30 seconds
61
+
62
+ while (result.outcome === 'ESCALATE' && attempts < maxAttempts) {
63
+ await new Promise(resolve => setTimeout(resolve, 2000));
64
+ attempts++;
65
+
66
+ const pollResp = await axios.get(`${this.config.vanguardUrl}/capsule/${capsuleId}/status`, {
67
+ headers: { 'Accept': 'application/json' }
68
+ });
69
+ result = pollResp.data;
70
+ }
71
+
72
+ if (result.outcome === 'HALT') {
73
+ throw new Error(`VEX Execution HALTED: ${result.reason_code}`);
74
+ }
75
+
76
+ if (result.outcome === 'ESCALATE') {
77
+ throw new Error('VEX Escalated resolution timed out.');
78
+ }
79
+
80
+ // 4. Store Capability Token
81
+ if (result.capability_token) {
82
+ (this as any).currentToken = result.capability_token;
83
+ }
84
+
85
+ return result;
86
+ } catch (error: any) {
87
+ console.error('VEX Execution Failed:', error.response?.data || error.message);
88
+ throw error;
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Manually construct a signed Evidence Capsule without dispatching it.
94
+ */
95
+ async buildCapsule(toolName: string, params: Record<string, any>, intentContext?: string): Promise<any> {
96
+ await this.ensureSDK();
97
+ // This is a simplified implementation for the SDK.
98
+ // It demonstrates how the builder is leveraged.
99
+
100
+ const intent: any = {
101
+ request_sha256: this.hashObject(params),
102
+ confidence: 1.0,
103
+ capabilities: ['sdk_execution']
104
+ };
105
+ if (intentContext) {
106
+ intent.intent_context = intentContext;
107
+ }
108
+
109
+ const authority = {
110
+ capsule_id: require('crypto').randomUUID(),
111
+ outcome: 'ALLOW',
112
+ reason_code: 'SDK_GENERATED',
113
+ trace_root: '00'.repeat(32),
114
+ nonce: Date.now(),
115
+ prev_hash: '00'.repeat(32), // Start of chain
116
+ supervision: {
117
+ branch_completeness: 1.0,
118
+ contradictions: 0,
119
+ confidence: 1.0
120
+ }
121
+ };
122
+
123
+ const identity = {
124
+ aid: '00'.repeat(32), // Placeholder for hardware AID
125
+ identity_type: 'software_sim',
126
+ pcrs: { '0': '00'.repeat(32) }
127
+ };
128
+
129
+ const witness = {
130
+ chora_node_id: 'local_witness',
131
+ receipt_hash: '00'.repeat(32),
132
+ timestamp: Math.floor(Date.now() / 1000)
133
+ };
134
+
135
+ const intent_hash = VEPBuilder.hashSegment(intent);
136
+ const authority_hash = VEPBuilder.hashSegment(authority);
137
+ const identity_hash = VEPBuilder.hashSegment(identity);
138
+ const witness_hash = VEPBuilder.hashSegment(witness, false); // Minimal scope
139
+
140
+ const capsule_root = VEPBuilder.calculateCapsuleRoot({
141
+ intent_hash,
142
+ authority_hash,
143
+ identity_hash,
144
+ witness_hash
145
+ });
146
+
147
+ // Sign the capsule_root
148
+ const claim = this.sdk!.createClaimNow(capsule_root);
149
+ const keyPair = {
150
+ private_key: this.config.identityKey,
151
+ public_key: '00'.repeat(32) // In real use, this comes from the config/key derivation
152
+ };
153
+ this.sdk!.setKeypair(keyPair);
154
+ const signedClaim = this.sdk!.signClaim(claim);
155
+ const signature = Buffer.from(signedClaim.signature, 'hex');
156
+
157
+ return {
158
+ intent,
159
+ authority,
160
+ identity,
161
+ witness,
162
+ intent_hash,
163
+ authority_hash,
164
+ identity_hash,
165
+ witness_hash,
166
+ capsule_root,
167
+ crypto: {
168
+ algo: 'ed25519',
169
+ signature_scope: 'capsule_root',
170
+ signature_b64: signature.toString('base64'),
171
+ signature_raw: signature // Keep raw for binary spec
172
+ }
173
+ };
174
+ }
175
+
176
+ /**
177
+ * Serializes the Evidence Capsule into the v0x03 Binary Wire format.
178
+ */
179
+ toBinary(capsule: any): Buffer {
180
+ const { canonicalize } = require('json-canonicalize');
181
+
182
+ // --- Header (76 Bytes) ---
183
+ const header = Buffer.alloc(76);
184
+ header.write('VEP', 0);
185
+ header.writeUInt8(0x03, 3);
186
+ header.write(capsule.identity.aid.replace(/-/g, ''), 4, 'hex'); // Ensure hex
187
+ header.write(capsule.capsule_root, 36, 'hex');
188
+ header.writeBigUInt64BE(BigInt(capsule.authority.nonce), 68);
189
+
190
+ // --- TLV Body ---
191
+ const packTLV = (tag: number, data: Buffer) => {
192
+ const tlv = Buffer.alloc(5 + data.length);
193
+ tlv.writeUInt8(tag, 0);
194
+ tlv.writeUInt32BE(data.length, 1);
195
+ data.copy(tlv, 5);
196
+ return tlv;
197
+ };
198
+
199
+ const segments = [
200
+ packTLV(0x01, Buffer.from(canonicalize(capsule.intent))),
201
+ packTLV(0x02, Buffer.from(canonicalize(capsule.authority))),
202
+ packTLV(0x03, Buffer.from(canonicalize(capsule.identity))),
203
+ packTLV(0x05, Buffer.from(canonicalize(capsule.witness))),
204
+ packTLV(0x06, capsule.crypto.signature_raw)
205
+ ];
206
+
207
+ return Buffer.concat([header, ...segments, header]);
208
+ }
209
+
210
+ private hashObject(obj: any): string {
211
+ const { canonicalize } = require('json-canonicalize');
212
+ const crypto = require('crypto');
213
+ const canonicalJSON = canonicalize(obj);
214
+ return crypto.createHash('sha256').update(canonicalJSON).digest('hex');
215
+ }
216
+ }
217
+
package/src/builder.ts ADDED
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Copyright 2026 ProvnAI
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import * as crypto from 'crypto';
18
+ const { canonicalize } = require('json-canonicalize');
19
+
20
+ export interface IntentSegment {
21
+ request_sha256: string;
22
+ confidence: number;
23
+ capabilities: string[];
24
+ magpie_source?: string;
25
+ }
26
+
27
+ export interface AuthoritySegment {
28
+ capsule_id: string;
29
+ outcome: 'ALLOW' | 'HALT' | 'ESCALATE';
30
+ reason_code: string;
31
+ trace_root: string;
32
+ nonce: number;
33
+ prev_hash?: string; // VEX Ledger Link
34
+ supervision?: { // MCS Signals
35
+ branch_completeness?: number;
36
+ contradictions?: number;
37
+ confidence?: number;
38
+ };
39
+ gate_sensors?: Record<string, any>;
40
+ }
41
+
42
+ export interface IdentitySegment {
43
+ aid: string;
44
+ identity_type: string;
45
+ pcrs: Record<string, string>;
46
+ }
47
+
48
+ export interface WitnessSegment {
49
+ chora_node_id: string;
50
+ receipt_hash: string;
51
+ timestamp: number;
52
+ }
53
+
54
+ export interface VexPillars {
55
+ intent: IntentSegment;
56
+ authority: AuthoritySegment;
57
+ identity: IdentitySegment;
58
+ witness: WitnessSegment;
59
+ }
60
+
61
+ export class VEPBuilder {
62
+ /**
63
+ * Computes the SHA-256 hash of a JCS canonicalized object.
64
+ */
65
+ static hashSegment(segment: any, inclusive: boolean = true): string {
66
+ let dataToHash = segment;
67
+
68
+ if (!inclusive) {
69
+ // Witness pillar uses Minimal scope (Explicit fields only, EXCLUDING receipt_hash)
70
+ const minimal = {
71
+ chora_node_id: segment.chora_node_id,
72
+ timestamp: segment.timestamp
73
+ };
74
+ dataToHash = minimal;
75
+ }
76
+
77
+ const canonicalJSON = canonicalize(dataToHash);
78
+ return crypto.createHash('sha256').update(canonicalJSON).digest('hex');
79
+ }
80
+
81
+ /**
82
+ * Calculates the capsule_root commitment using a 4-leaf binary Merkle tree
83
+ * with domain separation (0x00 for leaves, 0x01 for internal nodes).
84
+ * Pillar Order: Intent, Authority, Identity, Witness.
85
+ */
86
+ static calculateCapsuleRoot(hashes: {
87
+ intent_hash: string;
88
+ authority_hash: string;
89
+ identity_hash: string;
90
+ witness_hash: string;
91
+ }): string {
92
+ const hashLeaf = (data_hex: string) => {
93
+ const leaf = Buffer.concat([Buffer.from([0x00]), Buffer.from(data_hex, 'hex')]);
94
+ return crypto.createHash('sha256').update(leaf).digest();
95
+ };
96
+
97
+ const hashInternal = (left: Buffer, right: Buffer) => {
98
+ const internal = Buffer.concat([Buffer.from([0x01]), left, right]);
99
+ return crypto.createHash('sha256').update(internal).digest();
100
+ };
101
+
102
+ // 1. Leaf Hashes
103
+ const hi = hashLeaf(hashes.intent_hash);
104
+ const ha = hashLeaf(hashes.authority_hash);
105
+ const hid = hashLeaf(hashes.identity_hash);
106
+ const hw = hashLeaf(hashes.witness_hash);
107
+
108
+ // 2. Layer 1 (Internal Nodes)
109
+ const h12 = hashInternal(hi, ha);
110
+ const h34 = hashInternal(hid, hw);
111
+
112
+ // 3. Root
113
+ const rootDigest = hashInternal(h12, h34);
114
+ return rootDigest.toString('hex');
115
+ }
116
+ }
117
+
package/src/index.ts ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Copyright 2026 ProvnAI
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { VexAgent } from './agent';
18
+ import { VEPBuilder } from './builder';
19
+
20
+ export * from './agent';
21
+ export * from './builder';
22
+ export * from './middleware';
23
+
24
+ // Explicit type exports for better IDE support
25
+ export type { VexConfig } from './agent';
26
+ export type {
27
+ IntentSegment,
28
+ AuthoritySegment,
29
+ IdentitySegment,
30
+ WitnessSegment,
31
+ VexPillars
32
+ } from './builder';
33
+
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Copyright 2026 ProvnAI
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { VexAgent } from './agent';
18
+
19
+ /**
20
+ * Vercel AI SDK compatible middleware for VEX-secured tool execution.
21
+ */
22
+ export const vexMiddleware = (config: { identityKey: string, vanguardUrl: string }) => {
23
+ const agent = new VexAgent(config);
24
+
25
+ return {
26
+ // This is a pattern used by Vercel AI SDK to intercept tool calls
27
+ async onToolCall({ toolName, args }: { toolName: string, args: any }) {
28
+ console.log(`[VEX] Securing tool call: ${toolName}`);
29
+
30
+ try {
31
+ const result = await agent.execute(toolName, args, `Vercel AI SDK forced verification for ${toolName}`);
32
+
33
+ // If execute doesn't throw, it means Vanguard/VEX sidecar allowed it
34
+ return {
35
+ status: 'verified',
36
+ vex_root: result.capsule_root
37
+ };
38
+ } catch (error) {
39
+ console.error(`[VEX] Blocked tool execution: ${toolName}`);
40
+ throw new Error(
41
+ `VEX Verification Failed: ${toolName} execution not authorized. Reason: ${error instanceof Error ? error.message : String(error)}`
42
+ );
43
+ }
44
+ }
45
+ };
46
+ };
47
+
@@ -0,0 +1,50 @@
1
+ import { VexAgent, VexConfig } from '../src/agent';
2
+ import axios from 'axios';
3
+
4
+ jest.mock('axios');
5
+ jest.mock('@provncloud/sdk', () => ({
6
+ ProvnSDK: jest.fn().mockImplementation(() => ({
7
+ generateKeypair: jest.fn(),
8
+ setKeypair: jest.fn(),
9
+ createClaimNow: jest.fn().mockReturnValue({ data: 'root', timestamp: 123 }),
10
+ signClaim: jest.fn().mockReturnValue({ signature: '66616b655f7369675f686578' })
11
+ })),
12
+ init: jest.fn().mockResolvedValue(undefined)
13
+ }));
14
+ const mockedAxios = axios as jest.Mocked<typeof axios>;
15
+
16
+ describe('VexAgent', () => {
17
+ const config: VexConfig = {
18
+ identityKey: 'fake_key',
19
+ vanguardUrl: 'http://localhost:3000'
20
+ };
21
+
22
+ test('execute() constructs a capsule and sends it via POST', async () => {
23
+ const agent = new VexAgent(config);
24
+
25
+ mockedAxios.post.mockResolvedValue({ data: { status: 'verified' } });
26
+
27
+ const result = await agent.execute('test_tool', { foo: 'bar' });
28
+
29
+ expect(mockedAxios.post).toHaveBeenCalledWith(
30
+ `${config.vanguardUrl}/dispatch`,
31
+ expect.objectContaining({
32
+ intent_hash: expect.any(String),
33
+ capsule_root: expect.any(String)
34
+ }),
35
+ expect.any(Object)
36
+ );
37
+ expect(result.status).toBe('verified');
38
+ });
39
+
40
+ test('buildCapsule() produces expected structure', async () => {
41
+ const agent = new VexAgent(config);
42
+ const capsule = await agent.buildCapsule('test_tool', { foo: 'bar' });
43
+
44
+ expect(capsule).toHaveProperty('intent');
45
+ expect(capsule).toHaveProperty('authority');
46
+ expect(capsule).toHaveProperty('identity');
47
+ expect(capsule).toHaveProperty('witness');
48
+ expect(capsule).toHaveProperty('capsule_root');
49
+ });
50
+ });
@@ -0,0 +1,48 @@
1
+ import { VexAgent } from '../src/agent';
2
+
3
+ jest.mock('@provncloud/sdk', () => ({
4
+ ProvnSDK: jest.fn().mockImplementation(() => ({
5
+ generateKeypair: jest.fn(),
6
+ setKeypair: jest.fn(),
7
+ createClaimNow: jest.fn().mockReturnValue({ data: 'root', timestamp: 123 }),
8
+ signClaim: jest.fn().mockReturnValue({ signature: '00'.repeat(64) })
9
+ })),
10
+ init: jest.fn().mockResolvedValue(undefined)
11
+ }));
12
+
13
+ describe('Binary Parity', () => {
14
+ test('produces deterministic binary hex', async () => {
15
+ const config = {
16
+ identityKey: '0'.repeat(64),
17
+ vanguardUrl: 'http://localhost:3000'
18
+ };
19
+ const agent = new VexAgent(config as any);
20
+
21
+ // Mock Date.now and crypto.randomUUID
22
+ const fixedNow = 1710500000000;
23
+ jest.spyOn(Date, 'now').mockReturnValue(fixedNow);
24
+
25
+ const crypto = require('crypto');
26
+ jest.spyOn(crypto, 'randomUUID').mockReturnValue('0'.repeat(64));
27
+
28
+ // Mock SDK signing
29
+ const mockSDK = {
30
+ createClaimNow: jest.fn().mockReturnValue({}),
31
+ setKeypair: jest.fn(),
32
+ signClaim: jest.fn().mockReturnValue({ signature: '00'.repeat(64) })
33
+ };
34
+ (agent as any).sdk = mockSDK;
35
+ (agent as any).ensureSDK = jest.fn().mockResolvedValue(undefined);
36
+
37
+ const capsule = await agent.buildCapsule('test_tool', { foo: 'bar' });
38
+ const binary = agent.toBinary(capsule);
39
+
40
+ require('fs').writeFileSync('../python/ts_payload.hex', binary.toString('hex'));
41
+ expect(binary.subarray(0, 4).toString()).toBe('VEP\x03');
42
+ // Forensic Footer Check
43
+ const header = binary.subarray(0, 76);
44
+ const footer = binary.subarray(binary.length - 76);
45
+ expect(footer.equals(header)).toBe(true);
46
+ expect(binary.length).toBeGreaterThan(152);
47
+ });
48
+ });
@@ -0,0 +1,48 @@
1
+ import { VEPBuilder } from '../src/builder';
2
+
3
+ describe('VEX SDK Parity Verification', () => {
4
+ test('calculateCapsuleRoot matches Rust test vector (v1.5.0 Merkle Shift)', () => {
5
+ // Test hashes provided in COWORKER_HANDOFF.md.resolved
6
+ const parityHashes = {
7
+ authority_hash: "6fac0de31355fc1dfe36eee1e0c226f7cc36dd58eaad0aca0c2d3873b4784d35",
8
+ identity_hash: "7869bae0249b33e09b881a0b44faba6ee3f4bab7edcc2aa5a5e9290e2563c828",
9
+ intent_hash: "e02504ea88bd9f05a744cd8a462a114dc2045eb7210ea8c6f5ff2679663c92cb",
10
+ witness_hash: "174dfb80917cca8a8d4760b82656e78df0778cb3aadd60b51cd018b3313d5733"
11
+ };
12
+
13
+ // Recalculated for 4-leaf Merkle Tree with 0x00/0x01 domain separation (Definitive v1.5.0)
14
+ const expectedRoot = "8acf6d45aedaf61c61142ea8f9f7a89bc90994532313f20fcc1493a95e36d405";
15
+
16
+ const calculatedRoot = VEPBuilder.calculateCapsuleRoot(parityHashes);
17
+ expect(calculatedRoot).toBe(expectedRoot);
18
+ });
19
+
20
+ test('calculateCapsuleRoot matches Rust test vector (Empty Tree)', () => {
21
+ const hashes = {
22
+ intent_hash: '00'.repeat(32),
23
+ authority_hash: '00'.repeat(32),
24
+ identity_hash: '00'.repeat(32),
25
+ witness_hash: '00'.repeat(32)
26
+ };
27
+ const root = VEPBuilder.calculateCapsuleRoot(hashes);
28
+ expect(root).toBe('b46fd516fa6c7dcddd52ac2be2a014d8a8de4eaa059f79ccfcff4b8afc4e7ddc');
29
+ });
30
+
31
+ test('hashSegment matches expected output (Verification of JCS + SHA256)', () => {
32
+ // Intent Pillar (Inclusive)
33
+ const intent = {
34
+ request_sha256: "e02504ea88bd9f05a744cd8a462a114dc2045eb7210ea8c6f5ff2679663c92cb",
35
+ confidence: 0.95,
36
+ capabilities: ["filesystem", "network"]
37
+ };
38
+ // This is a sanity check that JCS stable-sorting works
39
+ const hash1 = VEPBuilder.hashSegment(intent);
40
+ const hash2 = VEPBuilder.hashSegment({
41
+ capabilities: ["filesystem", "network"],
42
+ confidence: 0.95,
43
+ request_sha256: "e02504ea88bd9f05a744cd8a462a114dc2045eb7210ea8c6f5ff2679663c92cb"
44
+ });
45
+
46
+ expect(hash1).toBe(hash2);
47
+ });
48
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "CommonJS",
5
+ "declaration": true,
6
+ "outDir": "./dist",
7
+ "strict": true,
8
+ "esModuleInterop": true,
9
+ "skipLibCheck": true,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "moduleResolution": "node"
12
+ },
13
+ "include": ["src/**/*"],
14
+ "exclude": ["node_modules", "**/*.test.ts"]
15
+ }