@emilia-protocol/sdk 0.1.0 → 0.9.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 +810 -0
- package/dist/client.d.ts +540 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +740 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +512 -189
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +317 -139
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +508 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +35 -0
- package/dist/types.js.map +1 -0
- package/package.json +28 -11
- package/src/client.ts +920 -0
- package/src/index.ts +932 -0
- package/src/types.ts +640 -0
package/src/client.ts
ADDED
|
@@ -0,0 +1,920 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// EMILIA Protocol — TypeScript Client
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
EntityType,
|
|
7
|
+
TrustPolicy,
|
|
8
|
+
TrustContext,
|
|
9
|
+
AgentBehavior,
|
|
10
|
+
TransactionType,
|
|
11
|
+
DisputeReason,
|
|
12
|
+
ReportType,
|
|
13
|
+
TrustDomain,
|
|
14
|
+
EntityTrustProfile,
|
|
15
|
+
TrustEvaluation,
|
|
16
|
+
SubmitReceiptInput,
|
|
17
|
+
SubmitReceiptResult,
|
|
18
|
+
EntitySearchResult,
|
|
19
|
+
Dispute,
|
|
20
|
+
LeaderboardEntry,
|
|
21
|
+
TrustGateResult,
|
|
22
|
+
DelegationRecord,
|
|
23
|
+
DomainScoreResult,
|
|
24
|
+
InstallPreflightResult,
|
|
25
|
+
PrincipalLookupResult,
|
|
26
|
+
LineageResult,
|
|
27
|
+
BatchReceiptResult,
|
|
28
|
+
ConfirmReceiptResult,
|
|
29
|
+
TrustPolicyDefinition,
|
|
30
|
+
EPStats,
|
|
31
|
+
EPClientOptions,
|
|
32
|
+
EPCommit,
|
|
33
|
+
EPCommitRequest,
|
|
34
|
+
EPCommitVerification,
|
|
35
|
+
EPCommitIssueResult,
|
|
36
|
+
EPCommitStatusResult,
|
|
37
|
+
EPCommitRevokeResult,
|
|
38
|
+
EPCommitReceiptResult,
|
|
39
|
+
} from './types.js';
|
|
40
|
+
|
|
41
|
+
import { EPError } from './types.js';
|
|
42
|
+
|
|
43
|
+
const SDK_VERSION = '1.0.0';
|
|
44
|
+
const DEFAULT_BASE_URL = 'https://emiliaprotocol.ai';
|
|
45
|
+
const DEFAULT_TIMEOUT = 30_000;
|
|
46
|
+
|
|
47
|
+
// ----------------------------------------------------------------------------
|
|
48
|
+
// Internal fetch helper types
|
|
49
|
+
// ----------------------------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
interface FetchOptions {
|
|
52
|
+
method?: string;
|
|
53
|
+
body?: unknown;
|
|
54
|
+
/** If true, include the Bearer token from this.apiKey */
|
|
55
|
+
auth?: boolean;
|
|
56
|
+
/** Query parameters appended to the URL */
|
|
57
|
+
params?: Record<string, string | number | boolean | undefined | null>;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ----------------------------------------------------------------------------
|
|
61
|
+
// EPClient
|
|
62
|
+
// ----------------------------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Client for the EMILIA Protocol API.
|
|
66
|
+
*
|
|
67
|
+
* All public methods return typed promises and throw `EPError` on failure.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
71
|
+
* import { EPClient } from '@emilia-protocol/sdk';
|
|
72
|
+
*
|
|
73
|
+
* const ep = new EPClient({ apiKey: process.env.EP_API_KEY });
|
|
74
|
+
*
|
|
75
|
+
* const profile = await ep.trustProfile('merchant-xyz');
|
|
76
|
+
* console.log(profile.current_confidence); // "confident"
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export class EPClient {
|
|
80
|
+
private readonly baseUrl: string;
|
|
81
|
+
private readonly apiKey: string;
|
|
82
|
+
private readonly timeout: number;
|
|
83
|
+
private readonly fetchImpl: typeof fetch;
|
|
84
|
+
|
|
85
|
+
constructor(options: EPClientOptions = {}) {
|
|
86
|
+
this.baseUrl = (
|
|
87
|
+
options.baseUrl ??
|
|
88
|
+
(typeof process !== 'undefined' ? process.env['EP_BASE_URL'] : undefined) ??
|
|
89
|
+
DEFAULT_BASE_URL
|
|
90
|
+
).replace(/\/+$/, '');
|
|
91
|
+
|
|
92
|
+
this.apiKey =
|
|
93
|
+
options.apiKey ??
|
|
94
|
+
(typeof process !== 'undefined' ? process.env['EP_API_KEY'] : undefined) ??
|
|
95
|
+
'';
|
|
96
|
+
|
|
97
|
+
this.timeout = options.timeout ?? DEFAULT_TIMEOUT;
|
|
98
|
+
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// --------------------------------------------------------------------------
|
|
102
|
+
// Core fetch implementation
|
|
103
|
+
// --------------------------------------------------------------------------
|
|
104
|
+
|
|
105
|
+
private async request<T>(path: string, options: FetchOptions = {}): Promise<T> {
|
|
106
|
+
// Build URL with query params
|
|
107
|
+
let url = `${this.baseUrl}${path}`;
|
|
108
|
+
if (options.params) {
|
|
109
|
+
const entries = Object.entries(options.params).filter(
|
|
110
|
+
([, v]) => v !== undefined && v !== null,
|
|
111
|
+
) as [string, string | number | boolean][];
|
|
112
|
+
if (entries.length > 0) {
|
|
113
|
+
url += `?${new URLSearchParams(entries.map(([k, v]) => [k, String(v)]))}`;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const headers: Record<string, string> = {
|
|
118
|
+
'Content-Type': 'application/json',
|
|
119
|
+
'User-Agent': `@emilia-protocol/sdk/${SDK_VERSION}`,
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
if (options.auth && this.apiKey) {
|
|
123
|
+
headers['Authorization'] = `Bearer ${this.apiKey}`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const controller = new AbortController();
|
|
127
|
+
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
const res = await this.fetchImpl(url, {
|
|
131
|
+
method: options.method ?? 'GET',
|
|
132
|
+
headers,
|
|
133
|
+
body: options.body !== undefined ? JSON.stringify(options.body) : undefined,
|
|
134
|
+
signal: controller.signal,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Parse JSON regardless of status so we can surface API error messages
|
|
138
|
+
const data: unknown = await res.json().catch(() => undefined);
|
|
139
|
+
|
|
140
|
+
if (!res.ok) {
|
|
141
|
+
const payload = data as Record<string, unknown> | undefined;
|
|
142
|
+
const message =
|
|
143
|
+
typeof payload?.['error'] === 'string'
|
|
144
|
+
? payload['error']
|
|
145
|
+
: `EP API error: ${res.status}`;
|
|
146
|
+
const code =
|
|
147
|
+
typeof payload?.['code'] === 'string' ? payload['code'] : undefined;
|
|
148
|
+
throw new EPError(message, res.status, code);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return data as T;
|
|
152
|
+
} catch (err) {
|
|
153
|
+
if (err instanceof EPError) throw err;
|
|
154
|
+
// AbortError → timeout
|
|
155
|
+
if (err instanceof Error && err.name === 'AbortError') {
|
|
156
|
+
throw new EPError(`Request timed out after ${this.timeout}ms`, undefined, 'timeout');
|
|
157
|
+
}
|
|
158
|
+
throw new EPError(
|
|
159
|
+
err instanceof Error ? err.message : 'Unknown network error',
|
|
160
|
+
undefined,
|
|
161
|
+
'network_error',
|
|
162
|
+
);
|
|
163
|
+
} finally {
|
|
164
|
+
clearTimeout(timer);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// --------------------------------------------------------------------------
|
|
169
|
+
// Trust Profile & Evaluation
|
|
170
|
+
// --------------------------------------------------------------------------
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Get an entity's full trust profile.
|
|
174
|
+
*
|
|
175
|
+
* This is the CANONICAL read surface for EP trust data. Call this before
|
|
176
|
+
* transacting with any counterparty or installing any software.
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* ```typescript
|
|
180
|
+
* const profile = await ep.trustProfile('merchant-xyz');
|
|
181
|
+
* console.log(profile.current_confidence); // "confident"
|
|
182
|
+
* console.log(profile.trust_profile?.behavioral?.completion_rate); // 97.2
|
|
183
|
+
* ```
|
|
184
|
+
*/
|
|
185
|
+
async trustProfile(entityId: string): Promise<EntityTrustProfile> {
|
|
186
|
+
return this.request<EntityTrustProfile>(
|
|
187
|
+
`/api/trust/profile/${encodeURIComponent(entityId)}`,
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Evaluate an entity against a named trust policy.
|
|
193
|
+
*
|
|
194
|
+
* Returns a canonical TrustDecision with detailed reasoning.
|
|
195
|
+
* Supply `context` for context-aware evaluation (geo, category, value_band, etc.).
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```typescript
|
|
199
|
+
* const result = await ep.trustEvaluate('merchant-xyz', 'strict', {
|
|
200
|
+
* category: 'furniture',
|
|
201
|
+
* geo: 'US-CA',
|
|
202
|
+
* value_band: 'high',
|
|
203
|
+
* });
|
|
204
|
+
* if (result.decision !== 'allow') console.warn('Reasons:', result.reasons);
|
|
205
|
+
* ```
|
|
206
|
+
*/
|
|
207
|
+
async trustEvaluate(
|
|
208
|
+
entityId: string,
|
|
209
|
+
policy: TrustPolicy | string = 'standard',
|
|
210
|
+
context?: TrustContext,
|
|
211
|
+
): Promise<TrustEvaluation> {
|
|
212
|
+
return this.request<TrustEvaluation>('/api/trust/evaluate', {
|
|
213
|
+
method: 'POST',
|
|
214
|
+
body: {
|
|
215
|
+
entity_id: entityId,
|
|
216
|
+
policy,
|
|
217
|
+
...(context ? { context } : {}),
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Pre-action trust gate — call before any high-stakes autonomous action.
|
|
224
|
+
*
|
|
225
|
+
* Combines trust evaluation with delegation verification in a single call.
|
|
226
|
+
* The gate returns allow/review/deny with appeal paths for non-allow decisions.
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* ```typescript
|
|
230
|
+
* const gate = await ep.trustGate({
|
|
231
|
+
* entityId: 'payment-agent-v2',
|
|
232
|
+
* action: 'execute_payment',
|
|
233
|
+
* policy: 'strict',
|
|
234
|
+
* valueUsd: 500,
|
|
235
|
+
* });
|
|
236
|
+
* if (gate.decision !== 'allow') throw new Error(`Blocked: ${gate.reasons?.join(', ')}`);
|
|
237
|
+
* ```
|
|
238
|
+
*/
|
|
239
|
+
async trustGate(options: {
|
|
240
|
+
entityId: string;
|
|
241
|
+
action: string;
|
|
242
|
+
policy?: TrustPolicy | string;
|
|
243
|
+
valueUsd?: number;
|
|
244
|
+
delegationId?: string;
|
|
245
|
+
}): Promise<TrustGateResult> {
|
|
246
|
+
return this.request<TrustGateResult>('/api/trust/gate', {
|
|
247
|
+
method: 'POST',
|
|
248
|
+
body: {
|
|
249
|
+
entity_id: options.entityId,
|
|
250
|
+
action: options.action,
|
|
251
|
+
policy: options.policy ?? 'standard',
|
|
252
|
+
value_usd: options.valueUsd ?? null,
|
|
253
|
+
delegation_id: options.delegationId ?? null,
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Get domain-specific trust scores for an entity.
|
|
260
|
+
*
|
|
261
|
+
* Optionally filter to a subset of domains. Useful when you need trust
|
|
262
|
+
* context scoped to a specific action category (e.g. "financial" before
|
|
263
|
+
* authorizing a payment).
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* ```typescript
|
|
267
|
+
* const scores = await ep.domainScore('agent-v2', ['financial', 'delegation']);
|
|
268
|
+
* console.log(scores.domains.financial?.confidence); // "confident"
|
|
269
|
+
* ```
|
|
270
|
+
*/
|
|
271
|
+
async domainScore(entityId: string, domains?: TrustDomain[]): Promise<DomainScoreResult> {
|
|
272
|
+
return this.request<DomainScoreResult>(
|
|
273
|
+
`/api/trust/domain-score/${encodeURIComponent(entityId)}`,
|
|
274
|
+
{ params: domains?.length ? { domains: domains.join(',') } : undefined },
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* EP-SX: Software pre-action enforcement check (experimental).
|
|
280
|
+
*
|
|
281
|
+
* Evaluates a software entity (MCP server, npm package, browser extension,
|
|
282
|
+
* GitHub App, Shopify App, etc.) for installation safety. Returns allow/
|
|
283
|
+
* review/deny with publisher verification, permission class, and provenance.
|
|
284
|
+
*
|
|
285
|
+
* @example
|
|
286
|
+
* ```typescript
|
|
287
|
+
* const preflight = await ep.installPreflight(
|
|
288
|
+
* 'mcp-server-acme-v1',
|
|
289
|
+
* 'mcp_server_safe_v1',
|
|
290
|
+
* { host: 'claude-desktop', permission_class: 'bounded_external_access' },
|
|
291
|
+
* );
|
|
292
|
+
* if (preflight.decision === 'deny') throw new Error('Installation blocked by EP');
|
|
293
|
+
* ```
|
|
294
|
+
*/
|
|
295
|
+
async installPreflight(
|
|
296
|
+
entityId: string,
|
|
297
|
+
policy?: TrustPolicy | string,
|
|
298
|
+
context?: Record<string, string>,
|
|
299
|
+
): Promise<InstallPreflightResult> {
|
|
300
|
+
return this.request<InstallPreflightResult>('/api/trust/install-preflight', {
|
|
301
|
+
method: 'POST',
|
|
302
|
+
body: {
|
|
303
|
+
entity_id: entityId,
|
|
304
|
+
policy: policy ?? 'standard',
|
|
305
|
+
...(context ? { context } : {}),
|
|
306
|
+
},
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// --------------------------------------------------------------------------
|
|
311
|
+
// Entities
|
|
312
|
+
// --------------------------------------------------------------------------
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Register a new entity.
|
|
316
|
+
*
|
|
317
|
+
* Public endpoint — no API key required. Returns the entity record and the
|
|
318
|
+
* first API key. Store the API key securely; it will not be shown again.
|
|
319
|
+
*
|
|
320
|
+
* @example
|
|
321
|
+
* ```typescript
|
|
322
|
+
* const { entity, api_key } = await ep.registerEntity({
|
|
323
|
+
* entityId: 'acme-payment-agent',
|
|
324
|
+
* displayName: 'Acme Payment Agent',
|
|
325
|
+
* entityType: 'agent',
|
|
326
|
+
* description: 'Handles autonomous payment flows for Acme Corp.',
|
|
327
|
+
* capabilities: ['payment', 'refund'],
|
|
328
|
+
* });
|
|
329
|
+
* console.log('Save this key:', api_key); // ep_live_...
|
|
330
|
+
* ```
|
|
331
|
+
*/
|
|
332
|
+
async registerEntity(options: {
|
|
333
|
+
entityId: string;
|
|
334
|
+
displayName: string;
|
|
335
|
+
entityType: EntityType;
|
|
336
|
+
description: string;
|
|
337
|
+
capabilities?: string[];
|
|
338
|
+
}): Promise<{ entity: { entity_id: string; display_name: string }; api_key: string }> {
|
|
339
|
+
return this.request('/api/entities/register', {
|
|
340
|
+
method: 'POST',
|
|
341
|
+
body: {
|
|
342
|
+
entity_id: options.entityId,
|
|
343
|
+
display_name: options.displayName,
|
|
344
|
+
entity_type: options.entityType,
|
|
345
|
+
description: options.description,
|
|
346
|
+
capabilities: options.capabilities,
|
|
347
|
+
},
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Search for entities by name, capability, or category.
|
|
353
|
+
*
|
|
354
|
+
* @example
|
|
355
|
+
* ```typescript
|
|
356
|
+
* const { entities } = await ep.searchEntities('payment', 'agent');
|
|
357
|
+
* for (const e of entities) {
|
|
358
|
+
* console.log(e.display_name, e.confidence);
|
|
359
|
+
* }
|
|
360
|
+
* ```
|
|
361
|
+
*/
|
|
362
|
+
async searchEntities(
|
|
363
|
+
query: string,
|
|
364
|
+
entityType?: EntityType,
|
|
365
|
+
minConfidence?: string,
|
|
366
|
+
): Promise<{ entities: EntitySearchResult[] }> {
|
|
367
|
+
return this.request('/api/entities/search', {
|
|
368
|
+
params: {
|
|
369
|
+
q: query,
|
|
370
|
+
type: entityType,
|
|
371
|
+
min_confidence: minConfidence,
|
|
372
|
+
},
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Get the entity leaderboard ranked by trust confidence.
|
|
378
|
+
*
|
|
379
|
+
* @example
|
|
380
|
+
* ```typescript
|
|
381
|
+
* const { leaderboard } = await ep.leaderboard(5, 'merchant');
|
|
382
|
+
* leaderboard.forEach(e => console.log(`#${e.rank} ${e.display_name}`));
|
|
383
|
+
* ```
|
|
384
|
+
*/
|
|
385
|
+
async leaderboard(
|
|
386
|
+
limit = 10,
|
|
387
|
+
entityType?: EntityType,
|
|
388
|
+
): Promise<{ leaderboard: LeaderboardEntry[] }> {
|
|
389
|
+
return this.request('/api/leaderboard', {
|
|
390
|
+
params: {
|
|
391
|
+
limit: Math.min(limit, 50),
|
|
392
|
+
type: entityType,
|
|
393
|
+
},
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// --------------------------------------------------------------------------
|
|
398
|
+
// Receipts
|
|
399
|
+
// --------------------------------------------------------------------------
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Submit a transaction receipt to the EP ledger.
|
|
403
|
+
*
|
|
404
|
+
* Requires an API key. Receipts are append-only, cryptographically hashed,
|
|
405
|
+
* and chain-linked. `transaction_ref` must be unique per entity.
|
|
406
|
+
*
|
|
407
|
+
* The `agent_behavior` field is the strongest Phase 1 signal — always set it.
|
|
408
|
+
*
|
|
409
|
+
* @example
|
|
410
|
+
* ```typescript
|
|
411
|
+
* const { receipt } = await ep.submitReceipt({
|
|
412
|
+
* entity_id: 'merchant-xyz',
|
|
413
|
+
* transaction_ref: 'order-8821',
|
|
414
|
+
* transaction_type: 'purchase',
|
|
415
|
+
* agent_behavior: 'completed',
|
|
416
|
+
* delivery_accuracy: 98,
|
|
417
|
+
* product_accuracy: 95,
|
|
418
|
+
* price_integrity: 100,
|
|
419
|
+
* });
|
|
420
|
+
* console.log('Receipt ID:', receipt.receipt_id);
|
|
421
|
+
* ```
|
|
422
|
+
*/
|
|
423
|
+
async submitReceipt(input: SubmitReceiptInput): Promise<SubmitReceiptResult> {
|
|
424
|
+
return this.request<SubmitReceiptResult>('/api/receipts/submit', {
|
|
425
|
+
method: 'POST',
|
|
426
|
+
auth: true,
|
|
427
|
+
body: input,
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Submit multiple receipts atomically. Maximum 50 per call.
|
|
433
|
+
*
|
|
434
|
+
* Each result in the response array indicates success or failure for that
|
|
435
|
+
* receipt independently — partial success is possible.
|
|
436
|
+
*
|
|
437
|
+
* @example
|
|
438
|
+
* ```typescript
|
|
439
|
+
* const result = await ep.batchSubmit([
|
|
440
|
+
* { entity_id: 'merchant-a', transaction_ref: 'tx-1', transaction_type: 'purchase', agent_behavior: 'completed' },
|
|
441
|
+
* { entity_id: 'merchant-b', transaction_ref: 'tx-2', transaction_type: 'service', agent_behavior: 'completed' },
|
|
442
|
+
* ]);
|
|
443
|
+
* result.results.forEach(r => console.log(r.entity_id, r.success ? 'ok' : r.error));
|
|
444
|
+
* ```
|
|
445
|
+
*/
|
|
446
|
+
async batchSubmit(receipts: SubmitReceiptInput[]): Promise<BatchReceiptResult> {
|
|
447
|
+
return this.request<BatchReceiptResult>('/api/receipts/batch', {
|
|
448
|
+
method: 'POST',
|
|
449
|
+
auth: true,
|
|
450
|
+
body: { receipts: receipts.slice(0, 50) },
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Confirm or reject a receipt as the counterparty (bilateral confirmation).
|
|
456
|
+
*
|
|
457
|
+
* The confirmation window is 48 hours from receipt creation. Confirmed
|
|
458
|
+
* receipts receive a higher provenance tier, improving their evidential weight.
|
|
459
|
+
*
|
|
460
|
+
* @example
|
|
461
|
+
* ```typescript
|
|
462
|
+
* await ep.confirmReceipt('ep_rcpt_abc123', true);
|
|
463
|
+
* ```
|
|
464
|
+
*/
|
|
465
|
+
async confirmReceipt(receiptId: string, confirm: boolean): Promise<ConfirmReceiptResult> {
|
|
466
|
+
return this.request<ConfirmReceiptResult>('/api/receipts/confirm', {
|
|
467
|
+
method: 'POST',
|
|
468
|
+
auth: true,
|
|
469
|
+
body: { receipt_id: receiptId, confirm },
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Verify a receipt against the on-chain Merkle root.
|
|
475
|
+
*
|
|
476
|
+
* @example
|
|
477
|
+
* ```typescript
|
|
478
|
+
* const { verified, anchored } = await ep.verifyReceipt('ep_rcpt_abc123');
|
|
479
|
+
* if (!verified) console.error('Receipt integrity check failed');
|
|
480
|
+
* ```
|
|
481
|
+
*/
|
|
482
|
+
async verifyReceipt(receiptId: string): Promise<{
|
|
483
|
+
receipt_id: string;
|
|
484
|
+
receipt_hash: string;
|
|
485
|
+
anchored: boolean;
|
|
486
|
+
verified: boolean;
|
|
487
|
+
}> {
|
|
488
|
+
return this.request(`/api/verify/${encodeURIComponent(receiptId)}`);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// --------------------------------------------------------------------------
|
|
492
|
+
// Disputes & Due Process
|
|
493
|
+
// --------------------------------------------------------------------------
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* File a dispute against a receipt.
|
|
497
|
+
*
|
|
498
|
+
* Requires an API key. Any affected party can challenge. The receipt
|
|
499
|
+
* submitter has 7 days to respond before EP escalates.
|
|
500
|
+
*
|
|
501
|
+
* @example
|
|
502
|
+
* ```typescript
|
|
503
|
+
* const dispute = await ep.fileDispute({
|
|
504
|
+
* receiptId: 'ep_rcpt_abc123',
|
|
505
|
+
* reason: 'inaccurate_signals',
|
|
506
|
+
* description: 'Delivery accuracy was reported as 98 but the item arrived damaged.',
|
|
507
|
+
* evidence: { photo_url: 'https://...' },
|
|
508
|
+
* });
|
|
509
|
+
* console.log('Dispute ID:', dispute.dispute_id);
|
|
510
|
+
* console.log('Respond by:', dispute.response_deadline);
|
|
511
|
+
* ```
|
|
512
|
+
*/
|
|
513
|
+
async fileDispute(options: {
|
|
514
|
+
receiptId: string;
|
|
515
|
+
reason: DisputeReason;
|
|
516
|
+
description?: string;
|
|
517
|
+
evidence?: Record<string, unknown>;
|
|
518
|
+
}): Promise<Dispute & { response_deadline: string; _message: string }> {
|
|
519
|
+
return this.request('/api/disputes/file', {
|
|
520
|
+
method: 'POST',
|
|
521
|
+
auth: true,
|
|
522
|
+
body: {
|
|
523
|
+
receipt_id: options.receiptId,
|
|
524
|
+
reason: options.reason,
|
|
525
|
+
description: options.description ?? null,
|
|
526
|
+
evidence: options.evidence ?? null,
|
|
527
|
+
},
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Get the current status of a dispute.
|
|
533
|
+
*
|
|
534
|
+
* Dispute status is public — transparency is a protocol value.
|
|
535
|
+
*
|
|
536
|
+
* @example
|
|
537
|
+
* ```typescript
|
|
538
|
+
* const dispute = await ep.disputeStatus('ep_disp_xyz789');
|
|
539
|
+
* console.log(dispute.status, dispute.resolution);
|
|
540
|
+
* ```
|
|
541
|
+
*/
|
|
542
|
+
async disputeStatus(disputeId: string): Promise<Dispute> {
|
|
543
|
+
return this.request<Dispute>(`/api/disputes/${encodeURIComponent(disputeId)}`);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Respond to a dispute filed against one of your receipts.
|
|
548
|
+
*
|
|
549
|
+
* Requires an API key. Must be called within the response_deadline window.
|
|
550
|
+
*
|
|
551
|
+
* @example
|
|
552
|
+
* ```typescript
|
|
553
|
+
* await ep.respondToDispute({
|
|
554
|
+
* disputeId: 'ep_disp_xyz789',
|
|
555
|
+
* response: 'The delivery accuracy score reflects the state at handoff, confirmed by carrier log.',
|
|
556
|
+
* evidence: { carrier_log_url: 'https://...' },
|
|
557
|
+
* });
|
|
558
|
+
* ```
|
|
559
|
+
*/
|
|
560
|
+
async respondToDispute(options: {
|
|
561
|
+
disputeId: string;
|
|
562
|
+
response: string;
|
|
563
|
+
evidence?: Record<string, unknown>;
|
|
564
|
+
}): Promise<{ dispute_id: string; status: string }> {
|
|
565
|
+
return this.request('/api/disputes/respond', {
|
|
566
|
+
method: 'POST',
|
|
567
|
+
auth: true,
|
|
568
|
+
body: {
|
|
569
|
+
dispute_id: options.disputeId,
|
|
570
|
+
response: options.response,
|
|
571
|
+
evidence: options.evidence ?? null,
|
|
572
|
+
},
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Withdraw an open dispute before it reaches resolution.
|
|
578
|
+
*
|
|
579
|
+
* Requires an API key. Only the filer can withdraw.
|
|
580
|
+
*
|
|
581
|
+
* @example
|
|
582
|
+
* ```typescript
|
|
583
|
+
* await ep.withdrawDispute('ep_disp_xyz789');
|
|
584
|
+
* ```
|
|
585
|
+
*/
|
|
586
|
+
async withdrawDispute(disputeId: string): Promise<{ dispute_id: string; status: string }> {
|
|
587
|
+
return this.request('/api/disputes/withdraw', {
|
|
588
|
+
method: 'POST',
|
|
589
|
+
auth: true,
|
|
590
|
+
body: { dispute_id: disputeId },
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* Appeal a dispute resolution.
|
|
596
|
+
*
|
|
597
|
+
* Requires an API key. Only dispute participants may appeal. The dispute must
|
|
598
|
+
* be in upheld, reversed, or dismissed state. The appeal decision is final.
|
|
599
|
+
*
|
|
600
|
+
* "Trust must never be more powerful than appeal." — EP Constitutional Principle
|
|
601
|
+
*
|
|
602
|
+
* @example
|
|
603
|
+
* ```typescript
|
|
604
|
+
* await ep.appealDispute({
|
|
605
|
+
* disputeId: 'ep_disp_xyz789',
|
|
606
|
+
* reason: 'New evidence shows the carrier log was misread. Attaching corrected scan.',
|
|
607
|
+
* evidence: { corrected_scan: 'https://...' },
|
|
608
|
+
* });
|
|
609
|
+
* ```
|
|
610
|
+
*/
|
|
611
|
+
async appealDispute(options: {
|
|
612
|
+
disputeId: string;
|
|
613
|
+
reason: string;
|
|
614
|
+
evidence?: Record<string, unknown>;
|
|
615
|
+
}): Promise<{ appeal_id?: string; dispute_id: string; status: string; _message?: string }> {
|
|
616
|
+
return this.request('/api/disputes/appeal', {
|
|
617
|
+
method: 'POST',
|
|
618
|
+
auth: true,
|
|
619
|
+
body: {
|
|
620
|
+
dispute_id: options.disputeId,
|
|
621
|
+
reason: options.reason,
|
|
622
|
+
evidence: options.evidence ?? null,
|
|
623
|
+
},
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Report a trust issue as a human.
|
|
629
|
+
*
|
|
630
|
+
* No authentication required. The human appeal channel — use when someone
|
|
631
|
+
* is wrongly downgraded, harmed by a trusted entity, or has observed fraud.
|
|
632
|
+
*
|
|
633
|
+
* @example
|
|
634
|
+
* ```typescript
|
|
635
|
+
* await ep.reportTrustIssue({
|
|
636
|
+
* entityId: 'merchant-xyz',
|
|
637
|
+
* reportType: 'harmed_by_trusted_entity',
|
|
638
|
+
* description: 'I paid for an item marked as delivered but never received it.',
|
|
639
|
+
* contactEmail: 'jane@example.com',
|
|
640
|
+
* });
|
|
641
|
+
* ```
|
|
642
|
+
*/
|
|
643
|
+
async reportTrustIssue(options: {
|
|
644
|
+
entityId: string;
|
|
645
|
+
reportType: ReportType;
|
|
646
|
+
description: string;
|
|
647
|
+
contactEmail?: string;
|
|
648
|
+
}): Promise<{ report_id: string; _message: string; _principle: string }> {
|
|
649
|
+
return this.request('/api/disputes/report', {
|
|
650
|
+
method: 'POST',
|
|
651
|
+
body: {
|
|
652
|
+
entity_id: options.entityId,
|
|
653
|
+
report_type: options.reportType,
|
|
654
|
+
description: options.description,
|
|
655
|
+
contact_email: options.contactEmail ?? null,
|
|
656
|
+
},
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// --------------------------------------------------------------------------
|
|
661
|
+
// Delegation (EP-DX)
|
|
662
|
+
// --------------------------------------------------------------------------
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Create a delegation: authorize an agent to act on behalf of a principal.
|
|
666
|
+
*
|
|
667
|
+
* Requires an API key. The delegation record can be verified by any party
|
|
668
|
+
* using `verifyDelegation`.
|
|
669
|
+
*
|
|
670
|
+
* @example
|
|
671
|
+
* ```typescript
|
|
672
|
+
* const delegation = await ep.createDelegation({
|
|
673
|
+
* principalId: 'ep_principal_acme',
|
|
674
|
+
* agentEntityId: 'acme-payment-agent',
|
|
675
|
+
* scope: ['purchase', 'refund'],
|
|
676
|
+
* maxValueUsd: 1000,
|
|
677
|
+
* expiresAt: '2026-12-31T23:59:59Z',
|
|
678
|
+
* });
|
|
679
|
+
* console.log('Delegation ID:', delegation.delegation_id);
|
|
680
|
+
* ```
|
|
681
|
+
*/
|
|
682
|
+
async createDelegation(options: {
|
|
683
|
+
principalId: string;
|
|
684
|
+
agentEntityId: string;
|
|
685
|
+
scope: string[];
|
|
686
|
+
maxValueUsd?: number;
|
|
687
|
+
expiresAt?: string;
|
|
688
|
+
constraints?: Record<string, unknown>;
|
|
689
|
+
}): Promise<DelegationRecord> {
|
|
690
|
+
return this.request<DelegationRecord>('/api/delegations/create', {
|
|
691
|
+
method: 'POST',
|
|
692
|
+
auth: true,
|
|
693
|
+
body: {
|
|
694
|
+
principal_id: options.principalId,
|
|
695
|
+
agent_entity_id: options.agentEntityId,
|
|
696
|
+
scope: options.scope,
|
|
697
|
+
max_value_usd: options.maxValueUsd ?? null,
|
|
698
|
+
expires_at: options.expiresAt ?? null,
|
|
699
|
+
constraints: options.constraints ?? null,
|
|
700
|
+
},
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/**
|
|
705
|
+
* Verify that a delegation is valid and covers a given action type.
|
|
706
|
+
*
|
|
707
|
+
* @example
|
|
708
|
+
* ```typescript
|
|
709
|
+
* const result = await ep.verifyDelegation('ep_del_abc123', 'purchase');
|
|
710
|
+
* if (!result.valid) throw new Error('Delegation invalid or expired');
|
|
711
|
+
* ```
|
|
712
|
+
*/
|
|
713
|
+
async verifyDelegation(
|
|
714
|
+
delegationId: string,
|
|
715
|
+
actionType?: string,
|
|
716
|
+
): Promise<DelegationRecord & { valid: boolean; action_permitted?: boolean; reason?: string }> {
|
|
717
|
+
return this.request(`/api/delegations/${encodeURIComponent(delegationId)}/verify`, {
|
|
718
|
+
params: { action_type: actionType },
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// --------------------------------------------------------------------------
|
|
723
|
+
// Identity Continuity (EP-IX)
|
|
724
|
+
// --------------------------------------------------------------------------
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* Look up a principal — the enduring actor behind one or more entities.
|
|
728
|
+
*
|
|
729
|
+
* Returns the principal record, its controlled entities, identity bindings,
|
|
730
|
+
* and continuity claim history.
|
|
731
|
+
*
|
|
732
|
+
* @example
|
|
733
|
+
* ```typescript
|
|
734
|
+
* const result = await ep.principalLookup('ep_principal_acme');
|
|
735
|
+
* console.log('Entities:', result.entities?.map(e => e.entity_id));
|
|
736
|
+
* ```
|
|
737
|
+
*/
|
|
738
|
+
async principalLookup(principalId: string): Promise<PrincipalLookupResult> {
|
|
739
|
+
return this.request<PrincipalLookupResult>(
|
|
740
|
+
`/api/identity/principal/${encodeURIComponent(principalId)}`,
|
|
741
|
+
);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* View entity lineage — predecessors, successors, and continuity decisions.
|
|
746
|
+
*
|
|
747
|
+
* Use to check whether an entity has suspicious continuity gaps that might
|
|
748
|
+
* indicate reputation laundering (whitewashing).
|
|
749
|
+
*
|
|
750
|
+
* @example
|
|
751
|
+
* ```typescript
|
|
752
|
+
* const lineage = await ep.lineage('merchant-xyz');
|
|
753
|
+
* if (lineage.predecessors?.some(p => p.status === 'disputed')) {
|
|
754
|
+
* console.warn('Entity has disputed predecessor — review before transacting');
|
|
755
|
+
* }
|
|
756
|
+
* ```
|
|
757
|
+
*/
|
|
758
|
+
async lineage(entityId: string): Promise<LineageResult> {
|
|
759
|
+
return this.request<LineageResult>(
|
|
760
|
+
`/api/identity/lineage/${encodeURIComponent(entityId)}`,
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
// --------------------------------------------------------------------------
|
|
765
|
+
// Policies
|
|
766
|
+
// --------------------------------------------------------------------------
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* List all available trust policies with their requirements and families.
|
|
770
|
+
*
|
|
771
|
+
* Returns 8 policies: 4 core (strict, standard, permissive, discovery) and
|
|
772
|
+
* 4 software-specific (github_private_repo_safe_v1, npm_buildtime_safe_v1,
|
|
773
|
+
* browser_extension_safe_v1, mcp_server_safe_v1).
|
|
774
|
+
*
|
|
775
|
+
* @example
|
|
776
|
+
* ```typescript
|
|
777
|
+
* const { policies } = await ep.listPolicies();
|
|
778
|
+
* policies.forEach(p => console.log(p.name, '-', p.description));
|
|
779
|
+
* ```
|
|
780
|
+
*/
|
|
781
|
+
async listPolicies(): Promise<{ policies: TrustPolicyDefinition[] }> {
|
|
782
|
+
return this.request('/api/policies');
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
// --------------------------------------------------------------------------
|
|
786
|
+
// System
|
|
787
|
+
// --------------------------------------------------------------------------
|
|
788
|
+
|
|
789
|
+
/**
|
|
790
|
+
* Public proof metrics — entity count, test count, tool count, policy count.
|
|
791
|
+
*
|
|
792
|
+
* @example
|
|
793
|
+
* ```typescript
|
|
794
|
+
* const stats = await ep.stats();
|
|
795
|
+
* console.log(`${stats.total_entities} entities across ${stats.trust_policies} policies`);
|
|
796
|
+
* ```
|
|
797
|
+
*/
|
|
798
|
+
async stats(): Promise<EPStats> {
|
|
799
|
+
return this.request<EPStats>('/api/stats');
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/**
|
|
803
|
+
* Health check. Returns subsystem status.
|
|
804
|
+
*
|
|
805
|
+
* @example
|
|
806
|
+
* ```typescript
|
|
807
|
+
* const health = await ep.health();
|
|
808
|
+
* console.log(health.status); // "ok"
|
|
809
|
+
* ```
|
|
810
|
+
*/
|
|
811
|
+
async health(): Promise<{ status: string; [key: string]: unknown }> {
|
|
812
|
+
return this.request('/api/health');
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// --------------------------------------------------------------------------
|
|
816
|
+
// EP Commit
|
|
817
|
+
// --------------------------------------------------------------------------
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
* Issue a signed EP Commit before a high-stakes action.
|
|
821
|
+
*
|
|
822
|
+
* The commit binds the agent to a specific action type, entity, and policy
|
|
823
|
+
* before execution. Returns decision (allow/deny/review), commit_id, expiry,
|
|
824
|
+
* scope, and appeal path.
|
|
825
|
+
*
|
|
826
|
+
* @example
|
|
827
|
+
* ```typescript
|
|
828
|
+
* const { decision, commit } = await ep.issueCommit({
|
|
829
|
+
* action_type: 'transact',
|
|
830
|
+
* entity_id: 'payment-agent-v2',
|
|
831
|
+
* max_value_usd: 500,
|
|
832
|
+
* policy: 'strict',
|
|
833
|
+
* });
|
|
834
|
+
* if (decision !== 'allow') throw new Error('Commit denied');
|
|
835
|
+
* console.log(commit.commit_id);
|
|
836
|
+
* ```
|
|
837
|
+
*/
|
|
838
|
+
async issueCommit(params: EPCommitRequest): Promise<EPCommitIssueResult> {
|
|
839
|
+
return this.request<EPCommitIssueResult>('/api/commit/issue', {
|
|
840
|
+
method: 'POST',
|
|
841
|
+
auth: true,
|
|
842
|
+
body: params,
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
/**
|
|
847
|
+
* Verify a commit's signature, status, and validity.
|
|
848
|
+
*
|
|
849
|
+
* @example
|
|
850
|
+
* ```typescript
|
|
851
|
+
* const result = await ep.verifyCommit('epc_abc123');
|
|
852
|
+
* if (!result.valid) console.error('Commit invalid');
|
|
853
|
+
* ```
|
|
854
|
+
*/
|
|
855
|
+
async verifyCommit(commitId: string): Promise<EPCommitVerification> {
|
|
856
|
+
return this.request<EPCommitVerification>('/api/commit/verify', {
|
|
857
|
+
method: 'POST',
|
|
858
|
+
body: { commit_id: commitId },
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
/**
|
|
863
|
+
* Get the current state of a commit.
|
|
864
|
+
*
|
|
865
|
+
* @example
|
|
866
|
+
* ```typescript
|
|
867
|
+
* const { commit } = await ep.getCommitStatus('epc_abc123');
|
|
868
|
+
* console.log(commit.status); // "active" | "revoked" | "expired" | "fulfilled"
|
|
869
|
+
* ```
|
|
870
|
+
*/
|
|
871
|
+
async getCommitStatus(commitId: string): Promise<EPCommitStatusResult> {
|
|
872
|
+
return this.request<EPCommitStatusResult>(`/api/commit/${encodeURIComponent(commitId)}`, {
|
|
873
|
+
auth: true,
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
/**
|
|
878
|
+
* Revoke an active commit before it is fulfilled or expires.
|
|
879
|
+
*
|
|
880
|
+
* @example
|
|
881
|
+
* ```typescript
|
|
882
|
+
* await ep.revokeCommit('epc_abc123', 'Action no longer needed');
|
|
883
|
+
* ```
|
|
884
|
+
*/
|
|
885
|
+
async revokeCommit(commitId: string, reason: string): Promise<EPCommitRevokeResult> {
|
|
886
|
+
return this.request<EPCommitRevokeResult>(`/api/commit/${encodeURIComponent(commitId)}/revoke`, {
|
|
887
|
+
method: 'POST',
|
|
888
|
+
auth: true,
|
|
889
|
+
body: { reason },
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
/**
|
|
894
|
+
* Bind a post-action receipt to a commit, completing the commit-execute-receipt cycle.
|
|
895
|
+
*
|
|
896
|
+
* @example
|
|
897
|
+
* ```typescript
|
|
898
|
+
* await ep.bindReceiptToCommit('epc_abc123', 'ep_rcpt_xyz789');
|
|
899
|
+
* ```
|
|
900
|
+
*/
|
|
901
|
+
async bindReceiptToCommit(commitId: string, receiptId: string): Promise<EPCommitReceiptResult> {
|
|
902
|
+
return this.request<EPCommitReceiptResult>(`/api/commit/${encodeURIComponent(commitId)}/receipt`, {
|
|
903
|
+
method: 'POST',
|
|
904
|
+
auth: true,
|
|
905
|
+
body: { receipt_id: receiptId },
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
/**
|
|
910
|
+
* Legacy: get the 0-100 compatibility score for an entity.
|
|
911
|
+
*
|
|
912
|
+
* Prefer `trustProfile()` for all new integrations. This endpoint exists
|
|
913
|
+
* for backward compatibility only.
|
|
914
|
+
*
|
|
915
|
+
* @deprecated Use trustProfile() instead.
|
|
916
|
+
*/
|
|
917
|
+
async legacyScore(entityId: string): Promise<{ entity_id: string; score: number }> {
|
|
918
|
+
return this.request(`/api/score/${encodeURIComponent(entityId)}`);
|
|
919
|
+
}
|
|
920
|
+
}
|