@intentrax/sdk 1.0.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/LICENSE +185 -0
- package/README.md +148 -0
- package/cli/intentrax.mjs +2412 -0
- package/examples/adapters/claude-code.md +27 -0
- package/examples/adapters/cursor.md +27 -0
- package/examples/adapters/langchain.md +27 -0
- package/examples/adapters/langgraph.md +28 -0
- package/examples/adapters/mcp.md +27 -0
- package/examples/adapters/vercel-ai-sdk.md +28 -0
- package/examples/agent-gateway-admission.md +72 -0
- package/examples/one-command-init.md +94 -0
- package/http.d.ts +392 -0
- package/http.js +667 -0
- package/http.ts +667 -0
- package/index.d.ts +235 -0
- package/index.js +626 -0
- package/index.ts +626 -0
- package/package.json +63 -0
package/index.ts
ADDED
|
@@ -0,0 +1,626 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
export const SDK_VERSION = "1.0.0";
|
|
4
|
+
export const API_VERSION = "v1";
|
|
5
|
+
export const VERIFY_REQUEST_HASH_DOMAIN = "INTENTRAX_PUBLIC_SDK_VERIFY_REQUEST_V1";
|
|
6
|
+
export const IDEMPOTENCY_KEY_DOMAIN = "INTENTRAX_PUBLIC_SDK_IDEMPOTENCY_V1";
|
|
7
|
+
export const AGENT_GATEWAY_ADMISSION_REQUEST_HASH_DOMAIN = "INTENTRAX_AGENT_GATEWAY_ADMISSION_REQUEST_V1";
|
|
8
|
+
export const AGENT_ADMISSION_SCHEMA_VERSION = "intentrax.runtime.agent_admission_request/1.0";
|
|
9
|
+
export const AGENT_ADMISSION_ENDPOINT = "/v1/agent/admissions";
|
|
10
|
+
// Live intent execution lane. The schema_version below is the exact string the
|
|
11
|
+
// engine's intentexecute.DecodeRequest requires; the endpoint is fail-closed
|
|
12
|
+
// (HTTP 423 until LiveExecutionEnabled, HTTP 403 until the tenant is
|
|
13
|
+
// allowlisted). See engine/internal/runtime/intentexecute/intentexecute.go.
|
|
14
|
+
export const INTENT_EXECUTE_REQUEST_SCHEMA_VERSION = "intentrax.runtime.intent_execute.request/1.0";
|
|
15
|
+
export const INTENT_EXECUTE_ENDPOINT = "/v1/intents/execute";
|
|
16
|
+
// Mirrors intentexecute.MaxRequestBytes: the engine rejects a canonical body
|
|
17
|
+
// larger than this with HTTP 400.
|
|
18
|
+
export const INTENT_EXECUTE_MAX_REQUEST_BYTES = 4096;
|
|
19
|
+
export const RECEIPT_VERIFICATION_REQUEST_SCHEMA_VERSION = "intentrax.runtime.proof_verification.request/1.0";
|
|
20
|
+
export const RECEIPT_VERIFICATION_ENDPOINT = "/v1/proofs/verify";
|
|
21
|
+
export const RECEIPT_VERIFICATION_REQUEST_HASH_DOMAIN = "INTENTRAX_RECEIPT_VERIFICATION_REQUEST_V1";
|
|
22
|
+
export const RECEIPT_VERIFICATION_IDEMPOTENCY_DOMAIN = "INTENTRAX_RECEIPT_VERIFICATION_IDEMPOTENCY_V1";
|
|
23
|
+
export const PROOF_EXPORT_ENDPOINT_PREFIX = "/v1/proofs";
|
|
24
|
+
export const PROOF_REPLAY_REQUEST_SCHEMA_VERSION = "intentrax.runtime.proof_replay.request/1.0";
|
|
25
|
+
export const PROOF_REPLAY_ENDPOINT = "/v1/proofs/replay";
|
|
26
|
+
export const VERIFIER_HANDOFF_ENDPOINT_PREFIX = "/v1/evidence/dry-run";
|
|
27
|
+
export const SDK_INPUT_ERROR_CODE = "INTX-SDK-INPUT-001";
|
|
28
|
+
|
|
29
|
+
const REQUIRED_FIELDS = [
|
|
30
|
+
"action",
|
|
31
|
+
"execution_id",
|
|
32
|
+
"intent_id",
|
|
33
|
+
"payload_hash",
|
|
34
|
+
"principal_id",
|
|
35
|
+
"proof_id",
|
|
36
|
+
"request_body_hash",
|
|
37
|
+
"request_method",
|
|
38
|
+
"request_path",
|
|
39
|
+
"required_tier",
|
|
40
|
+
"timestamp",
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
const AGENT_ADMISSION_REQUIRED_FIELDS = [
|
|
44
|
+
"schema_version",
|
|
45
|
+
"tenant_id",
|
|
46
|
+
"surface_id",
|
|
47
|
+
"action",
|
|
48
|
+
"adapter_id",
|
|
49
|
+
"evidence_refs",
|
|
50
|
+
"proof_refs",
|
|
51
|
+
"request_payload_hash",
|
|
52
|
+
"protocol_payload_ref",
|
|
53
|
+
"idempotency_key_hash",
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
// The engine decodes exactly these six fields with DisallowUnknownFields, so
|
|
57
|
+
// any extra field is a 400. The order here is the sorted invalid-field order
|
|
58
|
+
// the engine's validateRequest emits.
|
|
59
|
+
const INTENT_EXECUTE_REQUIRED_FIELDS = [
|
|
60
|
+
"schema_version",
|
|
61
|
+
"intent_id",
|
|
62
|
+
"tenant_id",
|
|
63
|
+
"action",
|
|
64
|
+
"idempotency_key",
|
|
65
|
+
"proof_chain_fixture_ref",
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
const RECEIPT_VERIFICATION_REQUIRED_FIELDS = [
|
|
69
|
+
"schema_version",
|
|
70
|
+
"receipt_id",
|
|
71
|
+
"receipt_hash",
|
|
72
|
+
"evidence_record_hash",
|
|
73
|
+
"handoff_hash",
|
|
74
|
+
"proof_chain_fixture_ref",
|
|
75
|
+
"proof_chain_fixture_hash",
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
const VALID_TIERS = new Set(["TIER_1", "TIER_2", "TIER_3", "TIER_4"]);
|
|
79
|
+
const PROOF_EXPORT_FORMATS = new Set(["json", "aep-json", "pdf"]);
|
|
80
|
+
const SHA256_RE = /^[a-f0-9]{64}$/;
|
|
81
|
+
const SHA256_REF_RE = /^sha256:[a-f0-9]{64}$/;
|
|
82
|
+
|
|
83
|
+
export function sha256Hex(value) {
|
|
84
|
+
return createHash("sha256").update(value, "utf8").digest("hex");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function isSha256Hex(value) {
|
|
88
|
+
return typeof value === "string" && SHA256_RE.test(value);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function isSha256Ref(value) {
|
|
92
|
+
return typeof value === "string" && SHA256_REF_RE.test(value);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function canonicalJson(value) {
|
|
96
|
+
if (value === null) {
|
|
97
|
+
return "null";
|
|
98
|
+
}
|
|
99
|
+
if (Array.isArray(value)) {
|
|
100
|
+
return "[" + value.map((item) => canonicalJson(item)).join(",") + "]";
|
|
101
|
+
}
|
|
102
|
+
if (typeof value === "string") {
|
|
103
|
+
return JSON.stringify(value);
|
|
104
|
+
}
|
|
105
|
+
if (typeof value === "boolean") {
|
|
106
|
+
return value ? "true" : "false";
|
|
107
|
+
}
|
|
108
|
+
if (typeof value === "number" && Number.isInteger(value) && Number.isFinite(value)) {
|
|
109
|
+
return String(value);
|
|
110
|
+
}
|
|
111
|
+
if (typeof value === "object") {
|
|
112
|
+
return "{" + Object.keys(value).sort().map((key) => {
|
|
113
|
+
const member = value[key];
|
|
114
|
+
if (member === undefined) {
|
|
115
|
+
throw new Error("canonical_json: undefined member " + key);
|
|
116
|
+
}
|
|
117
|
+
return JSON.stringify(key) + ":" + canonicalJson(member);
|
|
118
|
+
}).join(",") + "}";
|
|
119
|
+
}
|
|
120
|
+
throw new Error("canonical_json: unsupported value");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function validateVerificationRequest(request) {
|
|
124
|
+
const invalid = [];
|
|
125
|
+
if (request === null || typeof request !== "object" || Array.isArray(request)) {
|
|
126
|
+
return ["verification_request"];
|
|
127
|
+
}
|
|
128
|
+
for (const field of REQUIRED_FIELDS) {
|
|
129
|
+
if (typeof request[field] !== "string" || request[field].length === 0) {
|
|
130
|
+
invalid.push(field);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (!isSha256Hex(request.payload_hash)) {
|
|
134
|
+
invalid.push("payload_hash");
|
|
135
|
+
}
|
|
136
|
+
if (!isSha256Hex(request.request_body_hash)) {
|
|
137
|
+
invalid.push("request_body_hash");
|
|
138
|
+
}
|
|
139
|
+
if (!VALID_TIERS.has(request.required_tier)) {
|
|
140
|
+
invalid.push("required_tier");
|
|
141
|
+
}
|
|
142
|
+
return [...new Set(invalid)].sort();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function verificationRequestHash(request) {
|
|
146
|
+
return sha256Hex(VERIFY_REQUEST_HASH_DOMAIN + canonicalJson(request));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function idempotencyInput(request) {
|
|
150
|
+
return {
|
|
151
|
+
payload_hash: request.payload_hash,
|
|
152
|
+
proof_id: request.proof_id,
|
|
153
|
+
request_body_hash: request.request_body_hash,
|
|
154
|
+
request_method: request.request_method,
|
|
155
|
+
request_path: request.request_path,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function createIdempotencyKey(request) {
|
|
160
|
+
return sha256Hex(IDEMPOTENCY_KEY_DOMAIN + canonicalJson(idempotencyInput(request)));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function buildVerificationEnvelope(request) {
|
|
164
|
+
const invalidFields = validateVerificationRequest(request);
|
|
165
|
+
if (invalidFields.length > 0) {
|
|
166
|
+
return {
|
|
167
|
+
status: "FAIL",
|
|
168
|
+
error: {
|
|
169
|
+
error_code: SDK_INPUT_ERROR_CODE,
|
|
170
|
+
error_type: "public_sdk_input_error",
|
|
171
|
+
message: "Verification request is missing or malformed.",
|
|
172
|
+
invalid_fields: invalidFields,
|
|
173
|
+
retriable: false,
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
status: "PASS",
|
|
179
|
+
output: {
|
|
180
|
+
schema_version: "intentrax.public_sdk.verify/1.0",
|
|
181
|
+
request_hash: verificationRequestHash(request),
|
|
182
|
+
idempotency_key: createIdempotencyKey(request),
|
|
183
|
+
request,
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function validateReceiptVerificationRequest(request) {
|
|
189
|
+
const invalid = [];
|
|
190
|
+
if (request === null || typeof request !== "object" || Array.isArray(request)) {
|
|
191
|
+
return ["receipt_verification_request"];
|
|
192
|
+
}
|
|
193
|
+
for (const field of RECEIPT_VERIFICATION_REQUIRED_FIELDS) {
|
|
194
|
+
if (!(field in request)) {
|
|
195
|
+
invalid.push(field);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (request.schema_version !== RECEIPT_VERIFICATION_REQUEST_SCHEMA_VERSION) {
|
|
199
|
+
invalid.push("schema_version");
|
|
200
|
+
}
|
|
201
|
+
if (!isSafeReceiptID(request.receipt_id)) {
|
|
202
|
+
invalid.push("receipt_id");
|
|
203
|
+
}
|
|
204
|
+
for (const field of ["receipt_hash", "evidence_record_hash", "handoff_hash", "proof_chain_fixture_hash"]) {
|
|
205
|
+
if (!isSha256Hex(request[field])) {
|
|
206
|
+
invalid.push(field);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (typeof request.proof_chain_fixture_ref !== "string" ||
|
|
210
|
+
request.proof_chain_fixture_ref.length === 0 ||
|
|
211
|
+
request.proof_chain_fixture_ref.trim() !== request.proof_chain_fixture_ref ||
|
|
212
|
+
request.proof_chain_fixture_ref.includes("..")) {
|
|
213
|
+
invalid.push("proof_chain_fixture_ref");
|
|
214
|
+
}
|
|
215
|
+
return [...new Set(invalid)].sort();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function receiptVerificationRequestHash(request) {
|
|
219
|
+
return sha256Hex(RECEIPT_VERIFICATION_REQUEST_HASH_DOMAIN + canonicalJson(request));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function receiptVerificationIdempotencyInput(request) {
|
|
223
|
+
return {
|
|
224
|
+
evidence_record_hash: request.evidence_record_hash,
|
|
225
|
+
handoff_hash: request.handoff_hash,
|
|
226
|
+
proof_chain_fixture_hash: request.proof_chain_fixture_hash,
|
|
227
|
+
proof_chain_fixture_ref: request.proof_chain_fixture_ref,
|
|
228
|
+
receipt_hash: request.receipt_hash,
|
|
229
|
+
receipt_id: request.receipt_id,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function createReceiptVerificationIdempotencyKey(request) {
|
|
234
|
+
return sha256Hex(RECEIPT_VERIFICATION_IDEMPOTENCY_DOMAIN + canonicalJson(receiptVerificationIdempotencyInput(request)));
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export function buildReceiptVerificationEnvelope(request) {
|
|
238
|
+
const invalidFields = validateReceiptVerificationRequest(request);
|
|
239
|
+
if (invalidFields.length > 0) {
|
|
240
|
+
return {
|
|
241
|
+
status: "FAIL",
|
|
242
|
+
error: {
|
|
243
|
+
error_code: SDK_INPUT_ERROR_CODE,
|
|
244
|
+
error_type: "public_sdk_input_error",
|
|
245
|
+
message: "Receipt verification request is missing or malformed.",
|
|
246
|
+
invalid_fields: invalidFields,
|
|
247
|
+
retriable: false,
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
status: "PASS",
|
|
253
|
+
output: {
|
|
254
|
+
schema_version: "intentrax.public_sdk.receipt_verification/1.0",
|
|
255
|
+
endpoint: RECEIPT_VERIFICATION_ENDPOINT,
|
|
256
|
+
request_hash: receiptVerificationRequestHash(request),
|
|
257
|
+
idempotency_key: createReceiptVerificationIdempotencyKey(request),
|
|
258
|
+
request,
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export function normalizeProofExportFormat(format) {
|
|
264
|
+
if (format === undefined || format === null || format === "") {
|
|
265
|
+
return "json";
|
|
266
|
+
}
|
|
267
|
+
return String(format).toLowerCase();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export function validateProofExportRequest(request) {
|
|
271
|
+
const invalid = [];
|
|
272
|
+
if (request === null || typeof request !== "object" || Array.isArray(request)) {
|
|
273
|
+
return ["proof_export_request"];
|
|
274
|
+
}
|
|
275
|
+
if (!isSafeProofID(request.proof_id)) {
|
|
276
|
+
invalid.push("proof_id");
|
|
277
|
+
}
|
|
278
|
+
if (!PROOF_EXPORT_FORMATS.has(normalizeProofExportFormat(request.format))) {
|
|
279
|
+
invalid.push("format");
|
|
280
|
+
}
|
|
281
|
+
return [...new Set(invalid)].sort();
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export function buildProofExportEnvelope(request) {
|
|
285
|
+
const invalidFields = validateProofExportRequest(request);
|
|
286
|
+
if (invalidFields.length > 0) {
|
|
287
|
+
return {
|
|
288
|
+
status: "FAIL",
|
|
289
|
+
error: {
|
|
290
|
+
error_code: SDK_INPUT_ERROR_CODE,
|
|
291
|
+
error_type: "public_sdk_input_error",
|
|
292
|
+
message: "Proof export request is missing or malformed.",
|
|
293
|
+
invalid_fields: invalidFields,
|
|
294
|
+
retriable: false,
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
const format = normalizeProofExportFormat(request.format);
|
|
299
|
+
return {
|
|
300
|
+
status: "PASS",
|
|
301
|
+
output: {
|
|
302
|
+
schema_version: "intentrax.public_sdk.proof_export/1.0",
|
|
303
|
+
endpoint: `${PROOF_EXPORT_ENDPOINT_PREFIX}/${request.proof_id}/export`,
|
|
304
|
+
format,
|
|
305
|
+
authoritative_json: format !== "pdf",
|
|
306
|
+
pdf_receipt_non_authoritative: format === "pdf",
|
|
307
|
+
request: {
|
|
308
|
+
proof_id: request.proof_id,
|
|
309
|
+
format,
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export function validateProofReadRequest(request) {
|
|
316
|
+
const invalid = [];
|
|
317
|
+
if (request === null || typeof request !== "object" || Array.isArray(request)) {
|
|
318
|
+
return ["proof_read_request"];
|
|
319
|
+
}
|
|
320
|
+
if (!isSafeProofID(request.proof_id)) {
|
|
321
|
+
invalid.push("proof_id");
|
|
322
|
+
}
|
|
323
|
+
return [...new Set(invalid)].sort();
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export function buildProofReadEnvelope(request) {
|
|
327
|
+
const invalidFields = validateProofReadRequest(request);
|
|
328
|
+
if (invalidFields.length > 0) {
|
|
329
|
+
return {
|
|
330
|
+
status: "FAIL",
|
|
331
|
+
error: {
|
|
332
|
+
error_code: SDK_INPUT_ERROR_CODE,
|
|
333
|
+
error_type: "public_sdk_input_error",
|
|
334
|
+
message: "Proof read request is missing or malformed.",
|
|
335
|
+
invalid_fields: invalidFields,
|
|
336
|
+
retriable: false,
|
|
337
|
+
},
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
return {
|
|
341
|
+
status: "PASS",
|
|
342
|
+
output: {
|
|
343
|
+
schema_version: "intentrax.public_sdk.proof_read/1.0",
|
|
344
|
+
endpoint: `${PROOF_EXPORT_ENDPOINT_PREFIX}/${request.proof_id}`,
|
|
345
|
+
request: {
|
|
346
|
+
proof_id: request.proof_id,
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export function validateProofReplayRequest(request) {
|
|
353
|
+
const invalid = [];
|
|
354
|
+
if (request === null || typeof request !== "object" || Array.isArray(request)) {
|
|
355
|
+
return ["proof_replay_request"];
|
|
356
|
+
}
|
|
357
|
+
if (request.schema_version !== PROOF_REPLAY_REQUEST_SCHEMA_VERSION) {
|
|
358
|
+
invalid.push("schema_version");
|
|
359
|
+
}
|
|
360
|
+
if (!isSafeProofID(request.proof_id)) {
|
|
361
|
+
invalid.push("proof_id");
|
|
362
|
+
}
|
|
363
|
+
return [...new Set(invalid)].sort();
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export function buildProofReplayEnvelope(request) {
|
|
367
|
+
const invalidFields = validateProofReplayRequest(request);
|
|
368
|
+
if (invalidFields.length > 0) {
|
|
369
|
+
return {
|
|
370
|
+
status: "FAIL",
|
|
371
|
+
error: {
|
|
372
|
+
error_code: SDK_INPUT_ERROR_CODE,
|
|
373
|
+
error_type: "public_sdk_input_error",
|
|
374
|
+
message: "Proof replay request is missing or malformed.",
|
|
375
|
+
invalid_fields: invalidFields,
|
|
376
|
+
retriable: false,
|
|
377
|
+
},
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
return {
|
|
381
|
+
status: "PASS",
|
|
382
|
+
output: {
|
|
383
|
+
schema_version: "intentrax.public_sdk.proof_replay/1.0",
|
|
384
|
+
endpoint: PROOF_REPLAY_ENDPOINT,
|
|
385
|
+
request,
|
|
386
|
+
},
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export function validateProofInclusionRequest(request) {
|
|
391
|
+
const invalid = [];
|
|
392
|
+
if (request === null || typeof request !== "object" || Array.isArray(request)) {
|
|
393
|
+
return ["proof_inclusion_request"];
|
|
394
|
+
}
|
|
395
|
+
if (!isSafeProofID(request.proof_id)) {
|
|
396
|
+
invalid.push("proof_id");
|
|
397
|
+
}
|
|
398
|
+
return [...new Set(invalid)].sort();
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
export function buildProofInclusionEnvelope(request) {
|
|
402
|
+
const invalidFields = validateProofInclusionRequest(request);
|
|
403
|
+
if (invalidFields.length > 0) {
|
|
404
|
+
return {
|
|
405
|
+
status: "FAIL",
|
|
406
|
+
error: {
|
|
407
|
+
error_code: SDK_INPUT_ERROR_CODE,
|
|
408
|
+
error_type: "public_sdk_input_error",
|
|
409
|
+
message: "Proof inclusion request is missing or malformed.",
|
|
410
|
+
invalid_fields: invalidFields,
|
|
411
|
+
retriable: false,
|
|
412
|
+
},
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
return {
|
|
416
|
+
status: "PASS",
|
|
417
|
+
output: {
|
|
418
|
+
schema_version: "intentrax.public_sdk.proof_inclusion/1.0",
|
|
419
|
+
endpoint: `${PROOF_EXPORT_ENDPOINT_PREFIX}/${request.proof_id}/inclusion`,
|
|
420
|
+
request: {
|
|
421
|
+
proof_id: request.proof_id,
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
export function validateVerifierHandoffRequest(request) {
|
|
428
|
+
const invalid = [];
|
|
429
|
+
if (request === null || typeof request !== "object" || Array.isArray(request)) {
|
|
430
|
+
return ["verifier_handoff_request"];
|
|
431
|
+
}
|
|
432
|
+
if (!isSafeReceiptID(request.receipt_id)) {
|
|
433
|
+
invalid.push("receipt_id");
|
|
434
|
+
}
|
|
435
|
+
return [...new Set(invalid)].sort();
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
export function buildVerifierHandoffEnvelope(request) {
|
|
439
|
+
const invalidFields = validateVerifierHandoffRequest(request);
|
|
440
|
+
if (invalidFields.length > 0) {
|
|
441
|
+
return {
|
|
442
|
+
status: "FAIL",
|
|
443
|
+
error: {
|
|
444
|
+
error_code: SDK_INPUT_ERROR_CODE,
|
|
445
|
+
error_type: "public_sdk_input_error",
|
|
446
|
+
message: "Verifier handoff request is missing or malformed.",
|
|
447
|
+
invalid_fields: invalidFields,
|
|
448
|
+
retriable: false,
|
|
449
|
+
},
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
return {
|
|
453
|
+
status: "PASS",
|
|
454
|
+
output: {
|
|
455
|
+
schema_version: "intentrax.public_sdk.verifier_handoff/1.0",
|
|
456
|
+
endpoint: `${VERIFIER_HANDOFF_ENDPOINT_PREFIX}/${request.receipt_id}/verifier-handoff`,
|
|
457
|
+
request: {
|
|
458
|
+
receipt_id: request.receipt_id,
|
|
459
|
+
},
|
|
460
|
+
},
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
export function validateAgentGatewayAdmissionRequest(request) {
|
|
465
|
+
const invalid = [];
|
|
466
|
+
if (request === null || typeof request !== "object" || Array.isArray(request)) {
|
|
467
|
+
return ["agent_admission_request"];
|
|
468
|
+
}
|
|
469
|
+
for (const field of AGENT_ADMISSION_REQUIRED_FIELDS) {
|
|
470
|
+
if (!(field in request)) {
|
|
471
|
+
invalid.push(field);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
for (const field of ["tenant_id", "surface_id", "action", "adapter_id"]) {
|
|
475
|
+
if (typeof request[field] !== "string" || request[field].length === 0) {
|
|
476
|
+
invalid.push(field);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
if (request.schema_version !== AGENT_ADMISSION_SCHEMA_VERSION) {
|
|
480
|
+
invalid.push("schema_version");
|
|
481
|
+
}
|
|
482
|
+
for (const field of ["evidence_refs", "proof_refs"]) {
|
|
483
|
+
if (!Array.isArray(request[field]) || request[field].length === 0 || request[field].some((ref) => !isSha256Ref(ref))) {
|
|
484
|
+
invalid.push(field);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
if (!isSha256Hex(request.request_payload_hash)) {
|
|
488
|
+
invalid.push("request_payload_hash");
|
|
489
|
+
}
|
|
490
|
+
if (!isSha256Ref(request.protocol_payload_ref)) {
|
|
491
|
+
invalid.push("protocol_payload_ref");
|
|
492
|
+
}
|
|
493
|
+
if (!isSha256Hex(request.idempotency_key_hash)) {
|
|
494
|
+
invalid.push("idempotency_key_hash");
|
|
495
|
+
}
|
|
496
|
+
return [...new Set(invalid)].sort();
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
function isNonEmptyStable(value) {
|
|
500
|
+
return typeof value === "string" && value.length > 0 && value.trim() === value;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// Mirrors engine intentexecute.isUpperSnake: UPPER_SNAKE over [A-Z0-9_] with no
|
|
504
|
+
// leading/trailing whitespace and no "__" run.
|
|
505
|
+
function isUpperSnakeAction(value) {
|
|
506
|
+
return isNonEmptyStable(value) && /^[A-Z0-9_]+$/.test(value) && !value.includes("__");
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
function isSafeReceiptID(value) {
|
|
510
|
+
return typeof value === "string" && /^[A-Za-z0-9_-]+$/.test(value);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function isSafeProofID(value) {
|
|
514
|
+
return typeof value === "string" &&
|
|
515
|
+
value.length > 0 &&
|
|
516
|
+
value.length <= 128 &&
|
|
517
|
+
value.trim() === value &&
|
|
518
|
+
!value.includes("..") &&
|
|
519
|
+
!value.includes("\u0000") &&
|
|
520
|
+
/^[A-Za-z0-9_.-]+$/.test(value);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
export function agentGatewayAdmissionRequestHash(request) {
|
|
524
|
+
return sha256Hex(AGENT_GATEWAY_ADMISSION_REQUEST_HASH_DOMAIN + canonicalJson(request));
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
export function buildAgentGatewayAdmissionEnvelope(request) {
|
|
528
|
+
const invalidFields = validateAgentGatewayAdmissionRequest(request);
|
|
529
|
+
if (invalidFields.length > 0) {
|
|
530
|
+
return {
|
|
531
|
+
status: "FAIL",
|
|
532
|
+
error: {
|
|
533
|
+
error_code: SDK_INPUT_ERROR_CODE,
|
|
534
|
+
error_type: "public_sdk_input_error",
|
|
535
|
+
message: "Agent Gateway admission request is missing or malformed.",
|
|
536
|
+
invalid_fields: invalidFields,
|
|
537
|
+
retriable: false,
|
|
538
|
+
},
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
return {
|
|
542
|
+
status: "PASS",
|
|
543
|
+
output: {
|
|
544
|
+
schema_version: "intentrax.public_sdk.agent_gateway_admission/1.0",
|
|
545
|
+
request_hash: agentGatewayAdmissionRequestHash(request),
|
|
546
|
+
idempotency_key_hash: request.idempotency_key_hash,
|
|
547
|
+
endpoint: AGENT_ADMISSION_ENDPOINT,
|
|
548
|
+
request,
|
|
549
|
+
},
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// validateIntentExecuteRequest mirrors the engine's intentexecute.validateRequest
|
|
554
|
+
// (and its DecodeRequest strictness): exactly the six fields, an UPPER_SNAKE
|
|
555
|
+
// action, and a canonical body no larger than the engine's limit. It computes
|
|
556
|
+
// NO proof/hash truth — receipt_id, request_hash, registry_proof_id, and
|
|
557
|
+
// response_hash are the engine's to produce.
|
|
558
|
+
export function validateIntentExecuteRequest(request) {
|
|
559
|
+
if (request === null || typeof request !== "object" || Array.isArray(request)) {
|
|
560
|
+
return ["intent_execute_request"];
|
|
561
|
+
}
|
|
562
|
+
const invalid = [];
|
|
563
|
+
// Unknown fields are a 400 at the engine (DisallowUnknownFields); reject them
|
|
564
|
+
// here too so the SDK never emits a body the engine would refuse.
|
|
565
|
+
for (const key of Object.keys(request)) {
|
|
566
|
+
if (!INTENT_EXECUTE_REQUIRED_FIELDS.includes(key)) {
|
|
567
|
+
invalid.push("body");
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
if (request.schema_version !== INTENT_EXECUTE_REQUEST_SCHEMA_VERSION) {
|
|
571
|
+
invalid.push("schema_version");
|
|
572
|
+
}
|
|
573
|
+
if (!isNonEmptyStable(request.intent_id)) {
|
|
574
|
+
invalid.push("intent_id");
|
|
575
|
+
}
|
|
576
|
+
if (!isNonEmptyStable(request.tenant_id)) {
|
|
577
|
+
invalid.push("tenant_id");
|
|
578
|
+
}
|
|
579
|
+
if (!isUpperSnakeAction(request.action)) {
|
|
580
|
+
invalid.push("action");
|
|
581
|
+
}
|
|
582
|
+
// idempotency_key is a caller-supplied OPAQUE body field. It is NOT hashed by
|
|
583
|
+
// the SDK; the engine derives RequestHash over the whole canonical body.
|
|
584
|
+
if (!isNonEmptyStable(request.idempotency_key)) {
|
|
585
|
+
invalid.push("idempotency_key");
|
|
586
|
+
}
|
|
587
|
+
if (!isNonEmptyStable(request.proof_chain_fixture_ref)) {
|
|
588
|
+
invalid.push("proof_chain_fixture_ref");
|
|
589
|
+
}
|
|
590
|
+
if (invalid.every((field) => field !== "body")) {
|
|
591
|
+
const canonicalBytes = Buffer.byteLength(canonicalJson(request), "utf8");
|
|
592
|
+
if (canonicalBytes > INTENT_EXECUTE_MAX_REQUEST_BYTES) {
|
|
593
|
+
invalid.push("body");
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
return [...new Set(invalid)].sort();
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// buildIntentExecuteEnvelope is a THIN envelope builder: it validates and
|
|
600
|
+
// passes the caller-supplied body through verbatim. It performs no hashing and
|
|
601
|
+
// asserts no proof truth.
|
|
602
|
+
export function buildIntentExecuteEnvelope(request) {
|
|
603
|
+
const invalidFields = validateIntentExecuteRequest(request);
|
|
604
|
+
if (invalidFields.length > 0) {
|
|
605
|
+
return {
|
|
606
|
+
status: "FAIL",
|
|
607
|
+
error: {
|
|
608
|
+
error_code: SDK_INPUT_ERROR_CODE,
|
|
609
|
+
error_type: "public_sdk_input_error",
|
|
610
|
+
message: "Intent execute request is missing or malformed.",
|
|
611
|
+
invalid_fields: invalidFields,
|
|
612
|
+
retriable: false,
|
|
613
|
+
},
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
return {
|
|
617
|
+
status: "PASS",
|
|
618
|
+
output: {
|
|
619
|
+
schema_version: "intentrax.public_sdk.intent_execute/1.0",
|
|
620
|
+
endpoint: INTENT_EXECUTE_ENDPOINT,
|
|
621
|
+
// Passthrough of the caller-supplied body. No client-computed
|
|
622
|
+
// request_hash/receipt_id: those are returned verbatim from the engine.
|
|
623
|
+
request,
|
|
624
|
+
},
|
|
625
|
+
};
|
|
626
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@intentrax/sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Intentrax deterministic public SDK for proof verification and API-wrapper execution.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "node conformance.test.mjs"
|
|
9
|
+
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"intentrax": "./cli/intentrax.mjs"
|
|
12
|
+
},
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./index.d.ts",
|
|
16
|
+
"default": "./index.js"
|
|
17
|
+
},
|
|
18
|
+
"./http": {
|
|
19
|
+
"types": "./http.d.ts",
|
|
20
|
+
"default": "./http.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"types": "./index.d.ts",
|
|
24
|
+
"files": [
|
|
25
|
+
"LICENSE",
|
|
26
|
+
"cli/intentrax.mjs",
|
|
27
|
+
"examples",
|
|
28
|
+
"index.js",
|
|
29
|
+
"index.ts",
|
|
30
|
+
"index.d.ts",
|
|
31
|
+
"http.js",
|
|
32
|
+
"http.ts",
|
|
33
|
+
"http.d.ts",
|
|
34
|
+
"README.md"
|
|
35
|
+
],
|
|
36
|
+
"keywords": [
|
|
37
|
+
"intentrax",
|
|
38
|
+
"aep",
|
|
39
|
+
"eeo",
|
|
40
|
+
"proof-verification",
|
|
41
|
+
"deterministic-ai",
|
|
42
|
+
"mcp"
|
|
43
|
+
],
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=20"
|
|
46
|
+
},
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public",
|
|
49
|
+
"provenance": true
|
|
50
|
+
},
|
|
51
|
+
"repository": {
|
|
52
|
+
"type": "git",
|
|
53
|
+
"url": "https://github.com/Adelodun-Adejare/Intentrax.git",
|
|
54
|
+
"directory": "sdk/typescript"
|
|
55
|
+
},
|
|
56
|
+
"homepage": "https://intentrax.dev",
|
|
57
|
+
"bugs": {
|
|
58
|
+
"url": "https://github.com/Adelodun-Adejare/Intentrax/issues"
|
|
59
|
+
},
|
|
60
|
+
"intentrax": {
|
|
61
|
+
"sharedSpec": "../shared-specs/public-sdk-conformance.v1.json"
|
|
62
|
+
}
|
|
63
|
+
}
|