@voidly/agent-sdk 1.3.1 → 1.4.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/dist/index.d.mts +206 -0
- package/dist/index.d.ts +206 -0
- package/dist/index.js +352 -0
- package/dist/index.mjs +352 -0
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -244,6 +244,212 @@ declare class VoidlyAgent {
|
|
|
244
244
|
* This is a soft delete — messages expire per TTL. Re-register for a new identity.
|
|
245
245
|
*/
|
|
246
246
|
deactivate(): Promise<void>;
|
|
247
|
+
/**
|
|
248
|
+
* Register a capability this agent can perform.
|
|
249
|
+
* Other agents can search for your capabilities and send tasks.
|
|
250
|
+
*
|
|
251
|
+
* @example
|
|
252
|
+
* ```ts
|
|
253
|
+
* await agent.registerCapability({
|
|
254
|
+
* name: 'dns-analysis',
|
|
255
|
+
* description: 'Analyze DNS for censorship evidence',
|
|
256
|
+
* });
|
|
257
|
+
* ```
|
|
258
|
+
*/
|
|
259
|
+
registerCapability(options: {
|
|
260
|
+
name: string;
|
|
261
|
+
description?: string;
|
|
262
|
+
inputSchema?: Record<string, unknown>;
|
|
263
|
+
outputSchema?: Record<string, unknown>;
|
|
264
|
+
version?: string;
|
|
265
|
+
}): Promise<{
|
|
266
|
+
id: string;
|
|
267
|
+
name: string;
|
|
268
|
+
}>;
|
|
269
|
+
/**
|
|
270
|
+
* List this agent's registered capabilities.
|
|
271
|
+
*/
|
|
272
|
+
listCapabilities(): Promise<any[]>;
|
|
273
|
+
/**
|
|
274
|
+
* Search all agents' capabilities. Public — no auth required.
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* ```ts
|
|
278
|
+
* // Find agents that can analyze DNS
|
|
279
|
+
* const results = await agent.searchCapabilities({ query: 'dns' });
|
|
280
|
+
* console.log(results[0].agent.did); // did:voidly:...
|
|
281
|
+
* ```
|
|
282
|
+
*/
|
|
283
|
+
searchCapabilities(options?: {
|
|
284
|
+
query?: string;
|
|
285
|
+
name?: string;
|
|
286
|
+
limit?: number;
|
|
287
|
+
}): Promise<any[]>;
|
|
288
|
+
/**
|
|
289
|
+
* Remove a capability.
|
|
290
|
+
*/
|
|
291
|
+
deleteCapability(capabilityId: string): Promise<void>;
|
|
292
|
+
/**
|
|
293
|
+
* Create a task for another agent. The input is E2E encrypted using NaCl box.
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* ```ts
|
|
297
|
+
* // Find an agent with dns-analysis capability, then create a task
|
|
298
|
+
* const agents = await agent.searchCapabilities({ name: 'dns-analysis' });
|
|
299
|
+
* const task = await agent.createTask({
|
|
300
|
+
* to: agents[0].agent.did,
|
|
301
|
+
* capability: 'dns-analysis',
|
|
302
|
+
* input: { domain: 'twitter.com', country: 'IR' },
|
|
303
|
+
* });
|
|
304
|
+
* ```
|
|
305
|
+
*/
|
|
306
|
+
createTask(options: {
|
|
307
|
+
to: string;
|
|
308
|
+
capability?: string;
|
|
309
|
+
input: Record<string, unknown>;
|
|
310
|
+
priority?: 'low' | 'normal' | 'high' | 'urgent';
|
|
311
|
+
expiresIn?: number;
|
|
312
|
+
}): Promise<{
|
|
313
|
+
id: string;
|
|
314
|
+
status: string;
|
|
315
|
+
}>;
|
|
316
|
+
/**
|
|
317
|
+
* List tasks assigned to this agent or created by this agent.
|
|
318
|
+
*/
|
|
319
|
+
listTasks(options?: {
|
|
320
|
+
role?: 'assignee' | 'requester';
|
|
321
|
+
status?: string;
|
|
322
|
+
capability?: string;
|
|
323
|
+
limit?: number;
|
|
324
|
+
}): Promise<any[]>;
|
|
325
|
+
/**
|
|
326
|
+
* Get task detail. Includes encrypted input/output (only visible to participants).
|
|
327
|
+
*/
|
|
328
|
+
getTask(taskId: string): Promise<any>;
|
|
329
|
+
/**
|
|
330
|
+
* Accept, complete, or cancel a task.
|
|
331
|
+
*
|
|
332
|
+
* @example
|
|
333
|
+
* ```ts
|
|
334
|
+
* // Accept a pending task
|
|
335
|
+
* await agent.updateTask(taskId, { status: 'accepted' });
|
|
336
|
+
*
|
|
337
|
+
* // Complete with encrypted output
|
|
338
|
+
* await agent.updateTask(taskId, {
|
|
339
|
+
* status: 'completed',
|
|
340
|
+
* output: { blocked: true, method: 'dns-poisoning' },
|
|
341
|
+
* });
|
|
342
|
+
* ```
|
|
343
|
+
*/
|
|
344
|
+
updateTask(taskId: string, update: {
|
|
345
|
+
status?: 'accepted' | 'in_progress' | 'completed' | 'failed' | 'cancelled';
|
|
346
|
+
output?: Record<string, unknown>;
|
|
347
|
+
rating?: number;
|
|
348
|
+
ratingComment?: string;
|
|
349
|
+
}): Promise<{
|
|
350
|
+
updated: boolean;
|
|
351
|
+
}>;
|
|
352
|
+
/**
|
|
353
|
+
* Decrypt a task's encrypted input (when you're the assignee).
|
|
354
|
+
*/
|
|
355
|
+
decryptTaskInput(task: {
|
|
356
|
+
encrypted_input: string;
|
|
357
|
+
input_nonce: string;
|
|
358
|
+
from: string;
|
|
359
|
+
}, senderPubKey: Uint8Array): Record<string, unknown> | null;
|
|
360
|
+
/**
|
|
361
|
+
* Decrypt a task's encrypted output (when you're the requester).
|
|
362
|
+
*/
|
|
363
|
+
decryptTaskOutput(task: {
|
|
364
|
+
encrypted_output: string;
|
|
365
|
+
output_nonce: string;
|
|
366
|
+
to: string;
|
|
367
|
+
}, assigneePubKey: Uint8Array): Record<string, unknown> | null;
|
|
368
|
+
/**
|
|
369
|
+
* Create a signed attestation — a verifiable claim about the internet.
|
|
370
|
+
* The signature is verified by the relay and can be verified by ANYONE
|
|
371
|
+
* using your public signing key. This builds a decentralized evidence chain.
|
|
372
|
+
*
|
|
373
|
+
* @example
|
|
374
|
+
* ```ts
|
|
375
|
+
* // Attest that twitter.com is DNS-blocked in Iran
|
|
376
|
+
* const att = await agent.attest({
|
|
377
|
+
* claimType: 'domain-blocked',
|
|
378
|
+
* claimData: {
|
|
379
|
+
* domain: 'twitter.com',
|
|
380
|
+
* country: 'IR',
|
|
381
|
+
* method: 'dns-poisoning',
|
|
382
|
+
* isp: 'AS12880',
|
|
383
|
+
* },
|
|
384
|
+
* country: 'IR',
|
|
385
|
+
* domain: 'twitter.com',
|
|
386
|
+
* confidence: 0.95,
|
|
387
|
+
* });
|
|
388
|
+
* ```
|
|
389
|
+
*/
|
|
390
|
+
attest(options: {
|
|
391
|
+
claimType: string;
|
|
392
|
+
claimData: Record<string, unknown>;
|
|
393
|
+
country?: string;
|
|
394
|
+
domain?: string;
|
|
395
|
+
confidence?: number;
|
|
396
|
+
expiresIn?: number;
|
|
397
|
+
timestamp?: string;
|
|
398
|
+
}): Promise<{
|
|
399
|
+
id: string;
|
|
400
|
+
consensus_score: number;
|
|
401
|
+
}>;
|
|
402
|
+
/**
|
|
403
|
+
* Corroborate or refute another agent's attestation.
|
|
404
|
+
* Your vote is Ed25519-signed and publicly verifiable.
|
|
405
|
+
*
|
|
406
|
+
* @example
|
|
407
|
+
* ```ts
|
|
408
|
+
* // Confirm an attestation
|
|
409
|
+
* await agent.corroborate(attestationId, 'corroborate', 'Confirmed via independent DNS test');
|
|
410
|
+
*
|
|
411
|
+
* // Refute an attestation
|
|
412
|
+
* await agent.corroborate(attestationId, 'refute', 'Domain resolves correctly on my ISP');
|
|
413
|
+
* ```
|
|
414
|
+
*/
|
|
415
|
+
corroborate(attestationId: string, vote: 'corroborate' | 'refute', comment?: string): Promise<{
|
|
416
|
+
new_consensus_score: number;
|
|
417
|
+
corroboration_count: number;
|
|
418
|
+
}>;
|
|
419
|
+
/**
|
|
420
|
+
* Query attestations. Public — no auth required.
|
|
421
|
+
*/
|
|
422
|
+
queryAttestations(options?: {
|
|
423
|
+
country?: string;
|
|
424
|
+
domain?: string;
|
|
425
|
+
type?: string;
|
|
426
|
+
agent?: string;
|
|
427
|
+
minConsensus?: number;
|
|
428
|
+
since?: string;
|
|
429
|
+
limit?: number;
|
|
430
|
+
}): Promise<any[]>;
|
|
431
|
+
/**
|
|
432
|
+
* Get attestation detail including all corroborations.
|
|
433
|
+
*/
|
|
434
|
+
getAttestation(attestationId: string): Promise<any>;
|
|
435
|
+
/**
|
|
436
|
+
* Get consensus summary for a country or domain.
|
|
437
|
+
*/
|
|
438
|
+
getConsensus(options: {
|
|
439
|
+
country?: string;
|
|
440
|
+
domain?: string;
|
|
441
|
+
type?: string;
|
|
442
|
+
}): Promise<any[]>;
|
|
443
|
+
/**
|
|
444
|
+
* Verify an attestation's signature locally without trusting the relay.
|
|
445
|
+
* This is the core of the decentralized witness network — anyone can verify.
|
|
446
|
+
*/
|
|
447
|
+
static verifyAttestation(attestation: {
|
|
448
|
+
claim_type: string;
|
|
449
|
+
claim_data: Record<string, unknown>;
|
|
450
|
+
signature: string;
|
|
451
|
+
timestamp: string;
|
|
452
|
+
}, signingPublicKey: string): boolean;
|
|
247
453
|
}
|
|
248
454
|
|
|
249
455
|
export { type AgentIdentity, type AgentProfile, type DecryptedMessage, type SendResult, VoidlyAgent, type VoidlyAgentConfig };
|
package/dist/index.d.ts
CHANGED
|
@@ -244,6 +244,212 @@ declare class VoidlyAgent {
|
|
|
244
244
|
* This is a soft delete — messages expire per TTL. Re-register for a new identity.
|
|
245
245
|
*/
|
|
246
246
|
deactivate(): Promise<void>;
|
|
247
|
+
/**
|
|
248
|
+
* Register a capability this agent can perform.
|
|
249
|
+
* Other agents can search for your capabilities and send tasks.
|
|
250
|
+
*
|
|
251
|
+
* @example
|
|
252
|
+
* ```ts
|
|
253
|
+
* await agent.registerCapability({
|
|
254
|
+
* name: 'dns-analysis',
|
|
255
|
+
* description: 'Analyze DNS for censorship evidence',
|
|
256
|
+
* });
|
|
257
|
+
* ```
|
|
258
|
+
*/
|
|
259
|
+
registerCapability(options: {
|
|
260
|
+
name: string;
|
|
261
|
+
description?: string;
|
|
262
|
+
inputSchema?: Record<string, unknown>;
|
|
263
|
+
outputSchema?: Record<string, unknown>;
|
|
264
|
+
version?: string;
|
|
265
|
+
}): Promise<{
|
|
266
|
+
id: string;
|
|
267
|
+
name: string;
|
|
268
|
+
}>;
|
|
269
|
+
/**
|
|
270
|
+
* List this agent's registered capabilities.
|
|
271
|
+
*/
|
|
272
|
+
listCapabilities(): Promise<any[]>;
|
|
273
|
+
/**
|
|
274
|
+
* Search all agents' capabilities. Public — no auth required.
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* ```ts
|
|
278
|
+
* // Find agents that can analyze DNS
|
|
279
|
+
* const results = await agent.searchCapabilities({ query: 'dns' });
|
|
280
|
+
* console.log(results[0].agent.did); // did:voidly:...
|
|
281
|
+
* ```
|
|
282
|
+
*/
|
|
283
|
+
searchCapabilities(options?: {
|
|
284
|
+
query?: string;
|
|
285
|
+
name?: string;
|
|
286
|
+
limit?: number;
|
|
287
|
+
}): Promise<any[]>;
|
|
288
|
+
/**
|
|
289
|
+
* Remove a capability.
|
|
290
|
+
*/
|
|
291
|
+
deleteCapability(capabilityId: string): Promise<void>;
|
|
292
|
+
/**
|
|
293
|
+
* Create a task for another agent. The input is E2E encrypted using NaCl box.
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* ```ts
|
|
297
|
+
* // Find an agent with dns-analysis capability, then create a task
|
|
298
|
+
* const agents = await agent.searchCapabilities({ name: 'dns-analysis' });
|
|
299
|
+
* const task = await agent.createTask({
|
|
300
|
+
* to: agents[0].agent.did,
|
|
301
|
+
* capability: 'dns-analysis',
|
|
302
|
+
* input: { domain: 'twitter.com', country: 'IR' },
|
|
303
|
+
* });
|
|
304
|
+
* ```
|
|
305
|
+
*/
|
|
306
|
+
createTask(options: {
|
|
307
|
+
to: string;
|
|
308
|
+
capability?: string;
|
|
309
|
+
input: Record<string, unknown>;
|
|
310
|
+
priority?: 'low' | 'normal' | 'high' | 'urgent';
|
|
311
|
+
expiresIn?: number;
|
|
312
|
+
}): Promise<{
|
|
313
|
+
id: string;
|
|
314
|
+
status: string;
|
|
315
|
+
}>;
|
|
316
|
+
/**
|
|
317
|
+
* List tasks assigned to this agent or created by this agent.
|
|
318
|
+
*/
|
|
319
|
+
listTasks(options?: {
|
|
320
|
+
role?: 'assignee' | 'requester';
|
|
321
|
+
status?: string;
|
|
322
|
+
capability?: string;
|
|
323
|
+
limit?: number;
|
|
324
|
+
}): Promise<any[]>;
|
|
325
|
+
/**
|
|
326
|
+
* Get task detail. Includes encrypted input/output (only visible to participants).
|
|
327
|
+
*/
|
|
328
|
+
getTask(taskId: string): Promise<any>;
|
|
329
|
+
/**
|
|
330
|
+
* Accept, complete, or cancel a task.
|
|
331
|
+
*
|
|
332
|
+
* @example
|
|
333
|
+
* ```ts
|
|
334
|
+
* // Accept a pending task
|
|
335
|
+
* await agent.updateTask(taskId, { status: 'accepted' });
|
|
336
|
+
*
|
|
337
|
+
* // Complete with encrypted output
|
|
338
|
+
* await agent.updateTask(taskId, {
|
|
339
|
+
* status: 'completed',
|
|
340
|
+
* output: { blocked: true, method: 'dns-poisoning' },
|
|
341
|
+
* });
|
|
342
|
+
* ```
|
|
343
|
+
*/
|
|
344
|
+
updateTask(taskId: string, update: {
|
|
345
|
+
status?: 'accepted' | 'in_progress' | 'completed' | 'failed' | 'cancelled';
|
|
346
|
+
output?: Record<string, unknown>;
|
|
347
|
+
rating?: number;
|
|
348
|
+
ratingComment?: string;
|
|
349
|
+
}): Promise<{
|
|
350
|
+
updated: boolean;
|
|
351
|
+
}>;
|
|
352
|
+
/**
|
|
353
|
+
* Decrypt a task's encrypted input (when you're the assignee).
|
|
354
|
+
*/
|
|
355
|
+
decryptTaskInput(task: {
|
|
356
|
+
encrypted_input: string;
|
|
357
|
+
input_nonce: string;
|
|
358
|
+
from: string;
|
|
359
|
+
}, senderPubKey: Uint8Array): Record<string, unknown> | null;
|
|
360
|
+
/**
|
|
361
|
+
* Decrypt a task's encrypted output (when you're the requester).
|
|
362
|
+
*/
|
|
363
|
+
decryptTaskOutput(task: {
|
|
364
|
+
encrypted_output: string;
|
|
365
|
+
output_nonce: string;
|
|
366
|
+
to: string;
|
|
367
|
+
}, assigneePubKey: Uint8Array): Record<string, unknown> | null;
|
|
368
|
+
/**
|
|
369
|
+
* Create a signed attestation — a verifiable claim about the internet.
|
|
370
|
+
* The signature is verified by the relay and can be verified by ANYONE
|
|
371
|
+
* using your public signing key. This builds a decentralized evidence chain.
|
|
372
|
+
*
|
|
373
|
+
* @example
|
|
374
|
+
* ```ts
|
|
375
|
+
* // Attest that twitter.com is DNS-blocked in Iran
|
|
376
|
+
* const att = await agent.attest({
|
|
377
|
+
* claimType: 'domain-blocked',
|
|
378
|
+
* claimData: {
|
|
379
|
+
* domain: 'twitter.com',
|
|
380
|
+
* country: 'IR',
|
|
381
|
+
* method: 'dns-poisoning',
|
|
382
|
+
* isp: 'AS12880',
|
|
383
|
+
* },
|
|
384
|
+
* country: 'IR',
|
|
385
|
+
* domain: 'twitter.com',
|
|
386
|
+
* confidence: 0.95,
|
|
387
|
+
* });
|
|
388
|
+
* ```
|
|
389
|
+
*/
|
|
390
|
+
attest(options: {
|
|
391
|
+
claimType: string;
|
|
392
|
+
claimData: Record<string, unknown>;
|
|
393
|
+
country?: string;
|
|
394
|
+
domain?: string;
|
|
395
|
+
confidence?: number;
|
|
396
|
+
expiresIn?: number;
|
|
397
|
+
timestamp?: string;
|
|
398
|
+
}): Promise<{
|
|
399
|
+
id: string;
|
|
400
|
+
consensus_score: number;
|
|
401
|
+
}>;
|
|
402
|
+
/**
|
|
403
|
+
* Corroborate or refute another agent's attestation.
|
|
404
|
+
* Your vote is Ed25519-signed and publicly verifiable.
|
|
405
|
+
*
|
|
406
|
+
* @example
|
|
407
|
+
* ```ts
|
|
408
|
+
* // Confirm an attestation
|
|
409
|
+
* await agent.corroborate(attestationId, 'corroborate', 'Confirmed via independent DNS test');
|
|
410
|
+
*
|
|
411
|
+
* // Refute an attestation
|
|
412
|
+
* await agent.corroborate(attestationId, 'refute', 'Domain resolves correctly on my ISP');
|
|
413
|
+
* ```
|
|
414
|
+
*/
|
|
415
|
+
corroborate(attestationId: string, vote: 'corroborate' | 'refute', comment?: string): Promise<{
|
|
416
|
+
new_consensus_score: number;
|
|
417
|
+
corroboration_count: number;
|
|
418
|
+
}>;
|
|
419
|
+
/**
|
|
420
|
+
* Query attestations. Public — no auth required.
|
|
421
|
+
*/
|
|
422
|
+
queryAttestations(options?: {
|
|
423
|
+
country?: string;
|
|
424
|
+
domain?: string;
|
|
425
|
+
type?: string;
|
|
426
|
+
agent?: string;
|
|
427
|
+
minConsensus?: number;
|
|
428
|
+
since?: string;
|
|
429
|
+
limit?: number;
|
|
430
|
+
}): Promise<any[]>;
|
|
431
|
+
/**
|
|
432
|
+
* Get attestation detail including all corroborations.
|
|
433
|
+
*/
|
|
434
|
+
getAttestation(attestationId: string): Promise<any>;
|
|
435
|
+
/**
|
|
436
|
+
* Get consensus summary for a country or domain.
|
|
437
|
+
*/
|
|
438
|
+
getConsensus(options: {
|
|
439
|
+
country?: string;
|
|
440
|
+
domain?: string;
|
|
441
|
+
type?: string;
|
|
442
|
+
}): Promise<any[]>;
|
|
443
|
+
/**
|
|
444
|
+
* Verify an attestation's signature locally without trusting the relay.
|
|
445
|
+
* This is the core of the decentralized witness network — anyone can verify.
|
|
446
|
+
*/
|
|
447
|
+
static verifyAttestation(attestation: {
|
|
448
|
+
claim_type: string;
|
|
449
|
+
claim_data: Record<string, unknown>;
|
|
450
|
+
signature: string;
|
|
451
|
+
timestamp: string;
|
|
452
|
+
}, signingPublicKey: string): boolean;
|
|
247
453
|
}
|
|
248
454
|
|
|
249
455
|
export { type AgentIdentity, type AgentProfile, type DecryptedMessage, type SendResult, VoidlyAgent, type VoidlyAgentConfig };
|
package/dist/index.js
CHANGED
|
@@ -2797,6 +2797,358 @@ var VoidlyAgent = class _VoidlyAgent {
|
|
|
2797
2797
|
throw new Error(`Deactivate failed: ${err.error || res.statusText}`);
|
|
2798
2798
|
}
|
|
2799
2799
|
}
|
|
2800
|
+
// ─── Capability Registry (Agent Service Mesh) ──────────────────────────────
|
|
2801
|
+
/**
|
|
2802
|
+
* Register a capability this agent can perform.
|
|
2803
|
+
* Other agents can search for your capabilities and send tasks.
|
|
2804
|
+
*
|
|
2805
|
+
* @example
|
|
2806
|
+
* ```ts
|
|
2807
|
+
* await agent.registerCapability({
|
|
2808
|
+
* name: 'dns-analysis',
|
|
2809
|
+
* description: 'Analyze DNS for censorship evidence',
|
|
2810
|
+
* });
|
|
2811
|
+
* ```
|
|
2812
|
+
*/
|
|
2813
|
+
async registerCapability(options) {
|
|
2814
|
+
const res = await fetch(`${this.baseUrl}/v1/agent/capabilities`, {
|
|
2815
|
+
method: "POST",
|
|
2816
|
+
headers: { "Content-Type": "application/json", "X-Agent-Key": this.apiKey },
|
|
2817
|
+
body: JSON.stringify({
|
|
2818
|
+
name: options.name,
|
|
2819
|
+
description: options.description,
|
|
2820
|
+
input_schema: options.inputSchema,
|
|
2821
|
+
output_schema: options.outputSchema,
|
|
2822
|
+
version: options.version
|
|
2823
|
+
})
|
|
2824
|
+
});
|
|
2825
|
+
if (!res.ok) {
|
|
2826
|
+
const err = await res.json().catch(() => ({}));
|
|
2827
|
+
throw new Error(`Capability registration failed: ${err.error || res.statusText}`);
|
|
2828
|
+
}
|
|
2829
|
+
return await res.json();
|
|
2830
|
+
}
|
|
2831
|
+
/**
|
|
2832
|
+
* List this agent's registered capabilities.
|
|
2833
|
+
*/
|
|
2834
|
+
async listCapabilities() {
|
|
2835
|
+
const res = await fetch(`${this.baseUrl}/v1/agent/capabilities`, {
|
|
2836
|
+
headers: { "X-Agent-Key": this.apiKey }
|
|
2837
|
+
});
|
|
2838
|
+
if (!res.ok) return [];
|
|
2839
|
+
const data = await res.json();
|
|
2840
|
+
return data.capabilities;
|
|
2841
|
+
}
|
|
2842
|
+
/**
|
|
2843
|
+
* Search all agents' capabilities. Public — no auth required.
|
|
2844
|
+
*
|
|
2845
|
+
* @example
|
|
2846
|
+
* ```ts
|
|
2847
|
+
* // Find agents that can analyze DNS
|
|
2848
|
+
* const results = await agent.searchCapabilities({ query: 'dns' });
|
|
2849
|
+
* console.log(results[0].agent.did); // did:voidly:...
|
|
2850
|
+
* ```
|
|
2851
|
+
*/
|
|
2852
|
+
async searchCapabilities(options = {}) {
|
|
2853
|
+
const params = new URLSearchParams();
|
|
2854
|
+
if (options.query) params.set("q", options.query);
|
|
2855
|
+
if (options.name) params.set("name", options.name);
|
|
2856
|
+
if (options.limit) params.set("limit", String(options.limit));
|
|
2857
|
+
const res = await fetch(`${this.baseUrl}/v1/agent/capabilities/search?${params}`);
|
|
2858
|
+
if (!res.ok) return [];
|
|
2859
|
+
const data = await res.json();
|
|
2860
|
+
return data.results;
|
|
2861
|
+
}
|
|
2862
|
+
/**
|
|
2863
|
+
* Remove a capability.
|
|
2864
|
+
*/
|
|
2865
|
+
async deleteCapability(capabilityId) {
|
|
2866
|
+
const res = await fetch(`${this.baseUrl}/v1/agent/capabilities/${capabilityId}`, {
|
|
2867
|
+
method: "DELETE",
|
|
2868
|
+
headers: { "X-Agent-Key": this.apiKey }
|
|
2869
|
+
});
|
|
2870
|
+
if (!res.ok) {
|
|
2871
|
+
const err = await res.json().catch(() => ({}));
|
|
2872
|
+
throw new Error(`Delete failed: ${err.error || res.statusText}`);
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
// ─── Task Protocol (Encrypted Agent Collaboration) ─────────────────────────
|
|
2876
|
+
/**
|
|
2877
|
+
* Create a task for another agent. The input is E2E encrypted using NaCl box.
|
|
2878
|
+
*
|
|
2879
|
+
* @example
|
|
2880
|
+
* ```ts
|
|
2881
|
+
* // Find an agent with dns-analysis capability, then create a task
|
|
2882
|
+
* const agents = await agent.searchCapabilities({ name: 'dns-analysis' });
|
|
2883
|
+
* const task = await agent.createTask({
|
|
2884
|
+
* to: agents[0].agent.did,
|
|
2885
|
+
* capability: 'dns-analysis',
|
|
2886
|
+
* input: { domain: 'twitter.com', country: 'IR' },
|
|
2887
|
+
* });
|
|
2888
|
+
* ```
|
|
2889
|
+
*/
|
|
2890
|
+
async createTask(options) {
|
|
2891
|
+
const identityRes = await fetch(`${this.baseUrl}/v1/agent/identity/${options.to}`);
|
|
2892
|
+
if (!identityRes.ok) throw new Error("Recipient agent not found");
|
|
2893
|
+
const identity = await identityRes.json();
|
|
2894
|
+
const recipientPubKey = (0, import_tweetnacl_util.decodeBase64)(identity.encryption_public_key);
|
|
2895
|
+
const plaintext = (0, import_tweetnacl_util.decodeUTF8)(JSON.stringify(options.input));
|
|
2896
|
+
const nonce = import_tweetnacl.default.randomBytes(import_tweetnacl.default.box.nonceLength);
|
|
2897
|
+
const encrypted = import_tweetnacl.default.box(plaintext, nonce, recipientPubKey, this.encryptionKeyPair.secretKey);
|
|
2898
|
+
const res = await fetch(`${this.baseUrl}/v1/agent/tasks`, {
|
|
2899
|
+
method: "POST",
|
|
2900
|
+
headers: { "Content-Type": "application/json", "X-Agent-Key": this.apiKey },
|
|
2901
|
+
body: JSON.stringify({
|
|
2902
|
+
to: options.to,
|
|
2903
|
+
capability: options.capability,
|
|
2904
|
+
encrypted_input: (0, import_tweetnacl_util.encodeBase64)(encrypted),
|
|
2905
|
+
input_nonce: (0, import_tweetnacl_util.encodeBase64)(nonce),
|
|
2906
|
+
priority: options.priority || "normal",
|
|
2907
|
+
expires_in: options.expiresIn
|
|
2908
|
+
})
|
|
2909
|
+
});
|
|
2910
|
+
if (!res.ok) {
|
|
2911
|
+
const err = await res.json().catch(() => ({}));
|
|
2912
|
+
throw new Error(`Task creation failed: ${err.error || res.statusText}`);
|
|
2913
|
+
}
|
|
2914
|
+
return await res.json();
|
|
2915
|
+
}
|
|
2916
|
+
/**
|
|
2917
|
+
* List tasks assigned to this agent or created by this agent.
|
|
2918
|
+
*/
|
|
2919
|
+
async listTasks(options = {}) {
|
|
2920
|
+
const params = new URLSearchParams();
|
|
2921
|
+
if (options.role) params.set("role", options.role);
|
|
2922
|
+
if (options.status) params.set("status", options.status);
|
|
2923
|
+
if (options.capability) params.set("capability", options.capability);
|
|
2924
|
+
if (options.limit) params.set("limit", String(options.limit));
|
|
2925
|
+
const res = await fetch(`${this.baseUrl}/v1/agent/tasks?${params}`, {
|
|
2926
|
+
headers: { "X-Agent-Key": this.apiKey }
|
|
2927
|
+
});
|
|
2928
|
+
if (!res.ok) return [];
|
|
2929
|
+
const data = await res.json();
|
|
2930
|
+
return data.tasks;
|
|
2931
|
+
}
|
|
2932
|
+
/**
|
|
2933
|
+
* Get task detail. Includes encrypted input/output (only visible to participants).
|
|
2934
|
+
*/
|
|
2935
|
+
async getTask(taskId) {
|
|
2936
|
+
const res = await fetch(`${this.baseUrl}/v1/agent/tasks/${taskId}`, {
|
|
2937
|
+
headers: { "X-Agent-Key": this.apiKey }
|
|
2938
|
+
});
|
|
2939
|
+
if (!res.ok) {
|
|
2940
|
+
const err = await res.json().catch(() => ({}));
|
|
2941
|
+
throw new Error(`Get task failed: ${err.error || res.statusText}`);
|
|
2942
|
+
}
|
|
2943
|
+
return await res.json();
|
|
2944
|
+
}
|
|
2945
|
+
/**
|
|
2946
|
+
* Accept, complete, or cancel a task.
|
|
2947
|
+
*
|
|
2948
|
+
* @example
|
|
2949
|
+
* ```ts
|
|
2950
|
+
* // Accept a pending task
|
|
2951
|
+
* await agent.updateTask(taskId, { status: 'accepted' });
|
|
2952
|
+
*
|
|
2953
|
+
* // Complete with encrypted output
|
|
2954
|
+
* await agent.updateTask(taskId, {
|
|
2955
|
+
* status: 'completed',
|
|
2956
|
+
* output: { blocked: true, method: 'dns-poisoning' },
|
|
2957
|
+
* });
|
|
2958
|
+
* ```
|
|
2959
|
+
*/
|
|
2960
|
+
async updateTask(taskId, update) {
|
|
2961
|
+
const body = {};
|
|
2962
|
+
if (update.status) body.status = update.status;
|
|
2963
|
+
if (update.rating !== void 0) body.rating = update.rating;
|
|
2964
|
+
if (update.ratingComment) body.rating_comment = update.ratingComment;
|
|
2965
|
+
if (update.output && (update.status === "completed" || update.status === "failed")) {
|
|
2966
|
+
const task = await this.getTask(taskId);
|
|
2967
|
+
const requesterDid = task.from;
|
|
2968
|
+
const identityRes = await fetch(`${this.baseUrl}/v1/agent/identity/${requesterDid}`);
|
|
2969
|
+
if (identityRes.ok) {
|
|
2970
|
+
const identity = await identityRes.json();
|
|
2971
|
+
const requesterPubKey = (0, import_tweetnacl_util.decodeBase64)(identity.encryption_public_key);
|
|
2972
|
+
const plaintext = (0, import_tweetnacl_util.decodeUTF8)(JSON.stringify(update.output));
|
|
2973
|
+
const nonce = import_tweetnacl.default.randomBytes(import_tweetnacl.default.box.nonceLength);
|
|
2974
|
+
const encrypted = import_tweetnacl.default.box(plaintext, nonce, requesterPubKey, this.encryptionKeyPair.secretKey);
|
|
2975
|
+
const signature = import_tweetnacl.default.sign.detached(plaintext, this.signingKeyPair.secretKey);
|
|
2976
|
+
body.encrypted_output = (0, import_tweetnacl_util.encodeBase64)(encrypted);
|
|
2977
|
+
body.output_nonce = (0, import_tweetnacl_util.encodeBase64)(nonce);
|
|
2978
|
+
body.output_signature = (0, import_tweetnacl_util.encodeBase64)(signature);
|
|
2979
|
+
}
|
|
2980
|
+
}
|
|
2981
|
+
const res = await fetch(`${this.baseUrl}/v1/agent/tasks/${taskId}`, {
|
|
2982
|
+
method: "PATCH",
|
|
2983
|
+
headers: { "Content-Type": "application/json", "X-Agent-Key": this.apiKey },
|
|
2984
|
+
body: JSON.stringify(body)
|
|
2985
|
+
});
|
|
2986
|
+
if (!res.ok) {
|
|
2987
|
+
const err = await res.json().catch(() => ({}));
|
|
2988
|
+
throw new Error(`Task update failed: ${err.error || res.statusText}`);
|
|
2989
|
+
}
|
|
2990
|
+
return await res.json();
|
|
2991
|
+
}
|
|
2992
|
+
/**
|
|
2993
|
+
* Decrypt a task's encrypted input (when you're the assignee).
|
|
2994
|
+
*/
|
|
2995
|
+
decryptTaskInput(task, senderPubKey) {
|
|
2996
|
+
try {
|
|
2997
|
+
const ciphertext = (0, import_tweetnacl_util.decodeBase64)(task.encrypted_input);
|
|
2998
|
+
const nonce = (0, import_tweetnacl_util.decodeBase64)(task.input_nonce);
|
|
2999
|
+
const plaintext = import_tweetnacl.default.box.open(ciphertext, nonce, senderPubKey, this.encryptionKeyPair.secretKey);
|
|
3000
|
+
if (!plaintext) return null;
|
|
3001
|
+
return JSON.parse((0, import_tweetnacl_util.encodeUTF8)(plaintext));
|
|
3002
|
+
} catch {
|
|
3003
|
+
return null;
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
/**
|
|
3007
|
+
* Decrypt a task's encrypted output (when you're the requester).
|
|
3008
|
+
*/
|
|
3009
|
+
decryptTaskOutput(task, assigneePubKey) {
|
|
3010
|
+
try {
|
|
3011
|
+
const ciphertext = (0, import_tweetnacl_util.decodeBase64)(task.encrypted_output);
|
|
3012
|
+
const nonce = (0, import_tweetnacl_util.decodeBase64)(task.output_nonce);
|
|
3013
|
+
const plaintext = import_tweetnacl.default.box.open(ciphertext, nonce, assigneePubKey, this.encryptionKeyPair.secretKey);
|
|
3014
|
+
if (!plaintext) return null;
|
|
3015
|
+
return JSON.parse((0, import_tweetnacl_util.encodeUTF8)(plaintext));
|
|
3016
|
+
} catch {
|
|
3017
|
+
return null;
|
|
3018
|
+
}
|
|
3019
|
+
}
|
|
3020
|
+
// ─── Attestations (Decentralized Witness Network) ──────────────────────────
|
|
3021
|
+
/**
|
|
3022
|
+
* Create a signed attestation — a verifiable claim about the internet.
|
|
3023
|
+
* The signature is verified by the relay and can be verified by ANYONE
|
|
3024
|
+
* using your public signing key. This builds a decentralized evidence chain.
|
|
3025
|
+
*
|
|
3026
|
+
* @example
|
|
3027
|
+
* ```ts
|
|
3028
|
+
* // Attest that twitter.com is DNS-blocked in Iran
|
|
3029
|
+
* const att = await agent.attest({
|
|
3030
|
+
* claimType: 'domain-blocked',
|
|
3031
|
+
* claimData: {
|
|
3032
|
+
* domain: 'twitter.com',
|
|
3033
|
+
* country: 'IR',
|
|
3034
|
+
* method: 'dns-poisoning',
|
|
3035
|
+
* isp: 'AS12880',
|
|
3036
|
+
* },
|
|
3037
|
+
* country: 'IR',
|
|
3038
|
+
* domain: 'twitter.com',
|
|
3039
|
+
* confidence: 0.95,
|
|
3040
|
+
* });
|
|
3041
|
+
* ```
|
|
3042
|
+
*/
|
|
3043
|
+
async attest(options) {
|
|
3044
|
+
const timestamp = options.timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
|
3045
|
+
const payload = options.claimType + JSON.stringify(options.claimData) + timestamp;
|
|
3046
|
+
const payloadBytes = (0, import_tweetnacl_util.decodeUTF8)(payload);
|
|
3047
|
+
const signature = import_tweetnacl.default.sign.detached(payloadBytes, this.signingKeyPair.secretKey);
|
|
3048
|
+
const res = await fetch(`${this.baseUrl}/v1/agent/attestations`, {
|
|
3049
|
+
method: "POST",
|
|
3050
|
+
headers: { "Content-Type": "application/json", "X-Agent-Key": this.apiKey },
|
|
3051
|
+
body: JSON.stringify({
|
|
3052
|
+
claim_type: options.claimType,
|
|
3053
|
+
claim_data: options.claimData,
|
|
3054
|
+
signature: (0, import_tweetnacl_util.encodeBase64)(signature),
|
|
3055
|
+
timestamp,
|
|
3056
|
+
country: options.country,
|
|
3057
|
+
domain: options.domain,
|
|
3058
|
+
confidence: options.confidence,
|
|
3059
|
+
expires_in: options.expiresIn
|
|
3060
|
+
})
|
|
3061
|
+
});
|
|
3062
|
+
if (!res.ok) {
|
|
3063
|
+
const err = await res.json().catch(() => ({}));
|
|
3064
|
+
throw new Error(`Attestation failed: ${err.error || res.statusText}`);
|
|
3065
|
+
}
|
|
3066
|
+
return await res.json();
|
|
3067
|
+
}
|
|
3068
|
+
/**
|
|
3069
|
+
* Corroborate or refute another agent's attestation.
|
|
3070
|
+
* Your vote is Ed25519-signed and publicly verifiable.
|
|
3071
|
+
*
|
|
3072
|
+
* @example
|
|
3073
|
+
* ```ts
|
|
3074
|
+
* // Confirm an attestation
|
|
3075
|
+
* await agent.corroborate(attestationId, 'corroborate', 'Confirmed via independent DNS test');
|
|
3076
|
+
*
|
|
3077
|
+
* // Refute an attestation
|
|
3078
|
+
* await agent.corroborate(attestationId, 'refute', 'Domain resolves correctly on my ISP');
|
|
3079
|
+
* ```
|
|
3080
|
+
*/
|
|
3081
|
+
async corroborate(attestationId, vote, comment) {
|
|
3082
|
+
const payload = attestationId + vote;
|
|
3083
|
+
const payloadBytes = (0, import_tweetnacl_util.decodeUTF8)(payload);
|
|
3084
|
+
const signature = import_tweetnacl.default.sign.detached(payloadBytes, this.signingKeyPair.secretKey);
|
|
3085
|
+
const res = await fetch(`${this.baseUrl}/v1/agent/attestations/${attestationId}/corroborate`, {
|
|
3086
|
+
method: "POST",
|
|
3087
|
+
headers: { "Content-Type": "application/json", "X-Agent-Key": this.apiKey },
|
|
3088
|
+
body: JSON.stringify({ vote, signature: (0, import_tweetnacl_util.encodeBase64)(signature), comment })
|
|
3089
|
+
});
|
|
3090
|
+
if (!res.ok) {
|
|
3091
|
+
const err = await res.json().catch(() => ({}));
|
|
3092
|
+
throw new Error(`Corroboration failed: ${err.error || res.statusText}`);
|
|
3093
|
+
}
|
|
3094
|
+
return await res.json();
|
|
3095
|
+
}
|
|
3096
|
+
/**
|
|
3097
|
+
* Query attestations. Public — no auth required.
|
|
3098
|
+
*/
|
|
3099
|
+
async queryAttestations(options = {}) {
|
|
3100
|
+
const params = new URLSearchParams();
|
|
3101
|
+
if (options.country) params.set("country", options.country);
|
|
3102
|
+
if (options.domain) params.set("domain", options.domain);
|
|
3103
|
+
if (options.type) params.set("type", options.type);
|
|
3104
|
+
if (options.agent) params.set("agent", options.agent);
|
|
3105
|
+
if (options.minConsensus !== void 0) params.set("min_consensus", String(options.minConsensus));
|
|
3106
|
+
if (options.since) params.set("since", options.since);
|
|
3107
|
+
if (options.limit) params.set("limit", String(options.limit));
|
|
3108
|
+
const res = await fetch(`${this.baseUrl}/v1/agent/attestations?${params}`);
|
|
3109
|
+
if (!res.ok) return [];
|
|
3110
|
+
const data = await res.json();
|
|
3111
|
+
return data.attestations;
|
|
3112
|
+
}
|
|
3113
|
+
/**
|
|
3114
|
+
* Get attestation detail including all corroborations.
|
|
3115
|
+
*/
|
|
3116
|
+
async getAttestation(attestationId) {
|
|
3117
|
+
const res = await fetch(`${this.baseUrl}/v1/agent/attestations/${attestationId}`);
|
|
3118
|
+
if (!res.ok) {
|
|
3119
|
+
const err = await res.json().catch(() => ({}));
|
|
3120
|
+
throw new Error(`Get attestation failed: ${err.error || res.statusText}`);
|
|
3121
|
+
}
|
|
3122
|
+
return await res.json();
|
|
3123
|
+
}
|
|
3124
|
+
/**
|
|
3125
|
+
* Get consensus summary for a country or domain.
|
|
3126
|
+
*/
|
|
3127
|
+
async getConsensus(options) {
|
|
3128
|
+
const params = new URLSearchParams();
|
|
3129
|
+
if (options.country) params.set("country", options.country);
|
|
3130
|
+
if (options.domain) params.set("domain", options.domain);
|
|
3131
|
+
if (options.type) params.set("type", options.type);
|
|
3132
|
+
const res = await fetch(`${this.baseUrl}/v1/agent/attestations/consensus?${params}`);
|
|
3133
|
+
if (!res.ok) return [];
|
|
3134
|
+
const data = await res.json();
|
|
3135
|
+
return data.consensus;
|
|
3136
|
+
}
|
|
3137
|
+
/**
|
|
3138
|
+
* Verify an attestation's signature locally without trusting the relay.
|
|
3139
|
+
* This is the core of the decentralized witness network — anyone can verify.
|
|
3140
|
+
*/
|
|
3141
|
+
static verifyAttestation(attestation, signingPublicKey) {
|
|
3142
|
+
try {
|
|
3143
|
+
const payload = attestation.claim_type + JSON.stringify(attestation.claim_data) + attestation.timestamp;
|
|
3144
|
+
const payloadBytes = (0, import_tweetnacl_util.decodeUTF8)(payload);
|
|
3145
|
+
const signatureBytes = (0, import_tweetnacl_util.decodeBase64)(attestation.signature);
|
|
3146
|
+
const pubKeyBytes = (0, import_tweetnacl_util.decodeBase64)(signingPublicKey);
|
|
3147
|
+
return import_tweetnacl.default.sign.detached.verify(payloadBytes, signatureBytes, pubKeyBytes);
|
|
3148
|
+
} catch {
|
|
3149
|
+
return false;
|
|
3150
|
+
}
|
|
3151
|
+
}
|
|
2800
3152
|
};
|
|
2801
3153
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2802
3154
|
0 && (module.exports = {
|
package/dist/index.mjs
CHANGED
|
@@ -2787,6 +2787,358 @@ var VoidlyAgent = class _VoidlyAgent {
|
|
|
2787
2787
|
throw new Error(`Deactivate failed: ${err.error || res.statusText}`);
|
|
2788
2788
|
}
|
|
2789
2789
|
}
|
|
2790
|
+
// ─── Capability Registry (Agent Service Mesh) ──────────────────────────────
|
|
2791
|
+
/**
|
|
2792
|
+
* Register a capability this agent can perform.
|
|
2793
|
+
* Other agents can search for your capabilities and send tasks.
|
|
2794
|
+
*
|
|
2795
|
+
* @example
|
|
2796
|
+
* ```ts
|
|
2797
|
+
* await agent.registerCapability({
|
|
2798
|
+
* name: 'dns-analysis',
|
|
2799
|
+
* description: 'Analyze DNS for censorship evidence',
|
|
2800
|
+
* });
|
|
2801
|
+
* ```
|
|
2802
|
+
*/
|
|
2803
|
+
async registerCapability(options) {
|
|
2804
|
+
const res = await fetch(`${this.baseUrl}/v1/agent/capabilities`, {
|
|
2805
|
+
method: "POST",
|
|
2806
|
+
headers: { "Content-Type": "application/json", "X-Agent-Key": this.apiKey },
|
|
2807
|
+
body: JSON.stringify({
|
|
2808
|
+
name: options.name,
|
|
2809
|
+
description: options.description,
|
|
2810
|
+
input_schema: options.inputSchema,
|
|
2811
|
+
output_schema: options.outputSchema,
|
|
2812
|
+
version: options.version
|
|
2813
|
+
})
|
|
2814
|
+
});
|
|
2815
|
+
if (!res.ok) {
|
|
2816
|
+
const err = await res.json().catch(() => ({}));
|
|
2817
|
+
throw new Error(`Capability registration failed: ${err.error || res.statusText}`);
|
|
2818
|
+
}
|
|
2819
|
+
return await res.json();
|
|
2820
|
+
}
|
|
2821
|
+
/**
|
|
2822
|
+
* List this agent's registered capabilities.
|
|
2823
|
+
*/
|
|
2824
|
+
async listCapabilities() {
|
|
2825
|
+
const res = await fetch(`${this.baseUrl}/v1/agent/capabilities`, {
|
|
2826
|
+
headers: { "X-Agent-Key": this.apiKey }
|
|
2827
|
+
});
|
|
2828
|
+
if (!res.ok) return [];
|
|
2829
|
+
const data = await res.json();
|
|
2830
|
+
return data.capabilities;
|
|
2831
|
+
}
|
|
2832
|
+
/**
|
|
2833
|
+
* Search all agents' capabilities. Public — no auth required.
|
|
2834
|
+
*
|
|
2835
|
+
* @example
|
|
2836
|
+
* ```ts
|
|
2837
|
+
* // Find agents that can analyze DNS
|
|
2838
|
+
* const results = await agent.searchCapabilities({ query: 'dns' });
|
|
2839
|
+
* console.log(results[0].agent.did); // did:voidly:...
|
|
2840
|
+
* ```
|
|
2841
|
+
*/
|
|
2842
|
+
async searchCapabilities(options = {}) {
|
|
2843
|
+
const params = new URLSearchParams();
|
|
2844
|
+
if (options.query) params.set("q", options.query);
|
|
2845
|
+
if (options.name) params.set("name", options.name);
|
|
2846
|
+
if (options.limit) params.set("limit", String(options.limit));
|
|
2847
|
+
const res = await fetch(`${this.baseUrl}/v1/agent/capabilities/search?${params}`);
|
|
2848
|
+
if (!res.ok) return [];
|
|
2849
|
+
const data = await res.json();
|
|
2850
|
+
return data.results;
|
|
2851
|
+
}
|
|
2852
|
+
/**
|
|
2853
|
+
* Remove a capability.
|
|
2854
|
+
*/
|
|
2855
|
+
async deleteCapability(capabilityId) {
|
|
2856
|
+
const res = await fetch(`${this.baseUrl}/v1/agent/capabilities/${capabilityId}`, {
|
|
2857
|
+
method: "DELETE",
|
|
2858
|
+
headers: { "X-Agent-Key": this.apiKey }
|
|
2859
|
+
});
|
|
2860
|
+
if (!res.ok) {
|
|
2861
|
+
const err = await res.json().catch(() => ({}));
|
|
2862
|
+
throw new Error(`Delete failed: ${err.error || res.statusText}`);
|
|
2863
|
+
}
|
|
2864
|
+
}
|
|
2865
|
+
// ─── Task Protocol (Encrypted Agent Collaboration) ─────────────────────────
|
|
2866
|
+
/**
|
|
2867
|
+
* Create a task for another agent. The input is E2E encrypted using NaCl box.
|
|
2868
|
+
*
|
|
2869
|
+
* @example
|
|
2870
|
+
* ```ts
|
|
2871
|
+
* // Find an agent with dns-analysis capability, then create a task
|
|
2872
|
+
* const agents = await agent.searchCapabilities({ name: 'dns-analysis' });
|
|
2873
|
+
* const task = await agent.createTask({
|
|
2874
|
+
* to: agents[0].agent.did,
|
|
2875
|
+
* capability: 'dns-analysis',
|
|
2876
|
+
* input: { domain: 'twitter.com', country: 'IR' },
|
|
2877
|
+
* });
|
|
2878
|
+
* ```
|
|
2879
|
+
*/
|
|
2880
|
+
async createTask(options) {
|
|
2881
|
+
const identityRes = await fetch(`${this.baseUrl}/v1/agent/identity/${options.to}`);
|
|
2882
|
+
if (!identityRes.ok) throw new Error("Recipient agent not found");
|
|
2883
|
+
const identity = await identityRes.json();
|
|
2884
|
+
const recipientPubKey = (0, import_tweetnacl_util.decodeBase64)(identity.encryption_public_key);
|
|
2885
|
+
const plaintext = (0, import_tweetnacl_util.decodeUTF8)(JSON.stringify(options.input));
|
|
2886
|
+
const nonce = import_tweetnacl.default.randomBytes(import_tweetnacl.default.box.nonceLength);
|
|
2887
|
+
const encrypted = import_tweetnacl.default.box(plaintext, nonce, recipientPubKey, this.encryptionKeyPair.secretKey);
|
|
2888
|
+
const res = await fetch(`${this.baseUrl}/v1/agent/tasks`, {
|
|
2889
|
+
method: "POST",
|
|
2890
|
+
headers: { "Content-Type": "application/json", "X-Agent-Key": this.apiKey },
|
|
2891
|
+
body: JSON.stringify({
|
|
2892
|
+
to: options.to,
|
|
2893
|
+
capability: options.capability,
|
|
2894
|
+
encrypted_input: (0, import_tweetnacl_util.encodeBase64)(encrypted),
|
|
2895
|
+
input_nonce: (0, import_tweetnacl_util.encodeBase64)(nonce),
|
|
2896
|
+
priority: options.priority || "normal",
|
|
2897
|
+
expires_in: options.expiresIn
|
|
2898
|
+
})
|
|
2899
|
+
});
|
|
2900
|
+
if (!res.ok) {
|
|
2901
|
+
const err = await res.json().catch(() => ({}));
|
|
2902
|
+
throw new Error(`Task creation failed: ${err.error || res.statusText}`);
|
|
2903
|
+
}
|
|
2904
|
+
return await res.json();
|
|
2905
|
+
}
|
|
2906
|
+
/**
|
|
2907
|
+
* List tasks assigned to this agent or created by this agent.
|
|
2908
|
+
*/
|
|
2909
|
+
async listTasks(options = {}) {
|
|
2910
|
+
const params = new URLSearchParams();
|
|
2911
|
+
if (options.role) params.set("role", options.role);
|
|
2912
|
+
if (options.status) params.set("status", options.status);
|
|
2913
|
+
if (options.capability) params.set("capability", options.capability);
|
|
2914
|
+
if (options.limit) params.set("limit", String(options.limit));
|
|
2915
|
+
const res = await fetch(`${this.baseUrl}/v1/agent/tasks?${params}`, {
|
|
2916
|
+
headers: { "X-Agent-Key": this.apiKey }
|
|
2917
|
+
});
|
|
2918
|
+
if (!res.ok) return [];
|
|
2919
|
+
const data = await res.json();
|
|
2920
|
+
return data.tasks;
|
|
2921
|
+
}
|
|
2922
|
+
/**
|
|
2923
|
+
* Get task detail. Includes encrypted input/output (only visible to participants).
|
|
2924
|
+
*/
|
|
2925
|
+
async getTask(taskId) {
|
|
2926
|
+
const res = await fetch(`${this.baseUrl}/v1/agent/tasks/${taskId}`, {
|
|
2927
|
+
headers: { "X-Agent-Key": this.apiKey }
|
|
2928
|
+
});
|
|
2929
|
+
if (!res.ok) {
|
|
2930
|
+
const err = await res.json().catch(() => ({}));
|
|
2931
|
+
throw new Error(`Get task failed: ${err.error || res.statusText}`);
|
|
2932
|
+
}
|
|
2933
|
+
return await res.json();
|
|
2934
|
+
}
|
|
2935
|
+
/**
|
|
2936
|
+
* Accept, complete, or cancel a task.
|
|
2937
|
+
*
|
|
2938
|
+
* @example
|
|
2939
|
+
* ```ts
|
|
2940
|
+
* // Accept a pending task
|
|
2941
|
+
* await agent.updateTask(taskId, { status: 'accepted' });
|
|
2942
|
+
*
|
|
2943
|
+
* // Complete with encrypted output
|
|
2944
|
+
* await agent.updateTask(taskId, {
|
|
2945
|
+
* status: 'completed',
|
|
2946
|
+
* output: { blocked: true, method: 'dns-poisoning' },
|
|
2947
|
+
* });
|
|
2948
|
+
* ```
|
|
2949
|
+
*/
|
|
2950
|
+
async updateTask(taskId, update) {
|
|
2951
|
+
const body = {};
|
|
2952
|
+
if (update.status) body.status = update.status;
|
|
2953
|
+
if (update.rating !== void 0) body.rating = update.rating;
|
|
2954
|
+
if (update.ratingComment) body.rating_comment = update.ratingComment;
|
|
2955
|
+
if (update.output && (update.status === "completed" || update.status === "failed")) {
|
|
2956
|
+
const task = await this.getTask(taskId);
|
|
2957
|
+
const requesterDid = task.from;
|
|
2958
|
+
const identityRes = await fetch(`${this.baseUrl}/v1/agent/identity/${requesterDid}`);
|
|
2959
|
+
if (identityRes.ok) {
|
|
2960
|
+
const identity = await identityRes.json();
|
|
2961
|
+
const requesterPubKey = (0, import_tweetnacl_util.decodeBase64)(identity.encryption_public_key);
|
|
2962
|
+
const plaintext = (0, import_tweetnacl_util.decodeUTF8)(JSON.stringify(update.output));
|
|
2963
|
+
const nonce = import_tweetnacl.default.randomBytes(import_tweetnacl.default.box.nonceLength);
|
|
2964
|
+
const encrypted = import_tweetnacl.default.box(plaintext, nonce, requesterPubKey, this.encryptionKeyPair.secretKey);
|
|
2965
|
+
const signature = import_tweetnacl.default.sign.detached(plaintext, this.signingKeyPair.secretKey);
|
|
2966
|
+
body.encrypted_output = (0, import_tweetnacl_util.encodeBase64)(encrypted);
|
|
2967
|
+
body.output_nonce = (0, import_tweetnacl_util.encodeBase64)(nonce);
|
|
2968
|
+
body.output_signature = (0, import_tweetnacl_util.encodeBase64)(signature);
|
|
2969
|
+
}
|
|
2970
|
+
}
|
|
2971
|
+
const res = await fetch(`${this.baseUrl}/v1/agent/tasks/${taskId}`, {
|
|
2972
|
+
method: "PATCH",
|
|
2973
|
+
headers: { "Content-Type": "application/json", "X-Agent-Key": this.apiKey },
|
|
2974
|
+
body: JSON.stringify(body)
|
|
2975
|
+
});
|
|
2976
|
+
if (!res.ok) {
|
|
2977
|
+
const err = await res.json().catch(() => ({}));
|
|
2978
|
+
throw new Error(`Task update failed: ${err.error || res.statusText}`);
|
|
2979
|
+
}
|
|
2980
|
+
return await res.json();
|
|
2981
|
+
}
|
|
2982
|
+
/**
|
|
2983
|
+
* Decrypt a task's encrypted input (when you're the assignee).
|
|
2984
|
+
*/
|
|
2985
|
+
decryptTaskInput(task, senderPubKey) {
|
|
2986
|
+
try {
|
|
2987
|
+
const ciphertext = (0, import_tweetnacl_util.decodeBase64)(task.encrypted_input);
|
|
2988
|
+
const nonce = (0, import_tweetnacl_util.decodeBase64)(task.input_nonce);
|
|
2989
|
+
const plaintext = import_tweetnacl.default.box.open(ciphertext, nonce, senderPubKey, this.encryptionKeyPair.secretKey);
|
|
2990
|
+
if (!plaintext) return null;
|
|
2991
|
+
return JSON.parse((0, import_tweetnacl_util.encodeUTF8)(plaintext));
|
|
2992
|
+
} catch {
|
|
2993
|
+
return null;
|
|
2994
|
+
}
|
|
2995
|
+
}
|
|
2996
|
+
/**
|
|
2997
|
+
* Decrypt a task's encrypted output (when you're the requester).
|
|
2998
|
+
*/
|
|
2999
|
+
decryptTaskOutput(task, assigneePubKey) {
|
|
3000
|
+
try {
|
|
3001
|
+
const ciphertext = (0, import_tweetnacl_util.decodeBase64)(task.encrypted_output);
|
|
3002
|
+
const nonce = (0, import_tweetnacl_util.decodeBase64)(task.output_nonce);
|
|
3003
|
+
const plaintext = import_tweetnacl.default.box.open(ciphertext, nonce, assigneePubKey, this.encryptionKeyPair.secretKey);
|
|
3004
|
+
if (!plaintext) return null;
|
|
3005
|
+
return JSON.parse((0, import_tweetnacl_util.encodeUTF8)(plaintext));
|
|
3006
|
+
} catch {
|
|
3007
|
+
return null;
|
|
3008
|
+
}
|
|
3009
|
+
}
|
|
3010
|
+
// ─── Attestations (Decentralized Witness Network) ──────────────────────────
|
|
3011
|
+
/**
|
|
3012
|
+
* Create a signed attestation — a verifiable claim about the internet.
|
|
3013
|
+
* The signature is verified by the relay and can be verified by ANYONE
|
|
3014
|
+
* using your public signing key. This builds a decentralized evidence chain.
|
|
3015
|
+
*
|
|
3016
|
+
* @example
|
|
3017
|
+
* ```ts
|
|
3018
|
+
* // Attest that twitter.com is DNS-blocked in Iran
|
|
3019
|
+
* const att = await agent.attest({
|
|
3020
|
+
* claimType: 'domain-blocked',
|
|
3021
|
+
* claimData: {
|
|
3022
|
+
* domain: 'twitter.com',
|
|
3023
|
+
* country: 'IR',
|
|
3024
|
+
* method: 'dns-poisoning',
|
|
3025
|
+
* isp: 'AS12880',
|
|
3026
|
+
* },
|
|
3027
|
+
* country: 'IR',
|
|
3028
|
+
* domain: 'twitter.com',
|
|
3029
|
+
* confidence: 0.95,
|
|
3030
|
+
* });
|
|
3031
|
+
* ```
|
|
3032
|
+
*/
|
|
3033
|
+
async attest(options) {
|
|
3034
|
+
const timestamp = options.timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
|
3035
|
+
const payload = options.claimType + JSON.stringify(options.claimData) + timestamp;
|
|
3036
|
+
const payloadBytes = (0, import_tweetnacl_util.decodeUTF8)(payload);
|
|
3037
|
+
const signature = import_tweetnacl.default.sign.detached(payloadBytes, this.signingKeyPair.secretKey);
|
|
3038
|
+
const res = await fetch(`${this.baseUrl}/v1/agent/attestations`, {
|
|
3039
|
+
method: "POST",
|
|
3040
|
+
headers: { "Content-Type": "application/json", "X-Agent-Key": this.apiKey },
|
|
3041
|
+
body: JSON.stringify({
|
|
3042
|
+
claim_type: options.claimType,
|
|
3043
|
+
claim_data: options.claimData,
|
|
3044
|
+
signature: (0, import_tweetnacl_util.encodeBase64)(signature),
|
|
3045
|
+
timestamp,
|
|
3046
|
+
country: options.country,
|
|
3047
|
+
domain: options.domain,
|
|
3048
|
+
confidence: options.confidence,
|
|
3049
|
+
expires_in: options.expiresIn
|
|
3050
|
+
})
|
|
3051
|
+
});
|
|
3052
|
+
if (!res.ok) {
|
|
3053
|
+
const err = await res.json().catch(() => ({}));
|
|
3054
|
+
throw new Error(`Attestation failed: ${err.error || res.statusText}`);
|
|
3055
|
+
}
|
|
3056
|
+
return await res.json();
|
|
3057
|
+
}
|
|
3058
|
+
/**
|
|
3059
|
+
* Corroborate or refute another agent's attestation.
|
|
3060
|
+
* Your vote is Ed25519-signed and publicly verifiable.
|
|
3061
|
+
*
|
|
3062
|
+
* @example
|
|
3063
|
+
* ```ts
|
|
3064
|
+
* // Confirm an attestation
|
|
3065
|
+
* await agent.corroborate(attestationId, 'corroborate', 'Confirmed via independent DNS test');
|
|
3066
|
+
*
|
|
3067
|
+
* // Refute an attestation
|
|
3068
|
+
* await agent.corroborate(attestationId, 'refute', 'Domain resolves correctly on my ISP');
|
|
3069
|
+
* ```
|
|
3070
|
+
*/
|
|
3071
|
+
async corroborate(attestationId, vote, comment) {
|
|
3072
|
+
const payload = attestationId + vote;
|
|
3073
|
+
const payloadBytes = (0, import_tweetnacl_util.decodeUTF8)(payload);
|
|
3074
|
+
const signature = import_tweetnacl.default.sign.detached(payloadBytes, this.signingKeyPair.secretKey);
|
|
3075
|
+
const res = await fetch(`${this.baseUrl}/v1/agent/attestations/${attestationId}/corroborate`, {
|
|
3076
|
+
method: "POST",
|
|
3077
|
+
headers: { "Content-Type": "application/json", "X-Agent-Key": this.apiKey },
|
|
3078
|
+
body: JSON.stringify({ vote, signature: (0, import_tweetnacl_util.encodeBase64)(signature), comment })
|
|
3079
|
+
});
|
|
3080
|
+
if (!res.ok) {
|
|
3081
|
+
const err = await res.json().catch(() => ({}));
|
|
3082
|
+
throw new Error(`Corroboration failed: ${err.error || res.statusText}`);
|
|
3083
|
+
}
|
|
3084
|
+
return await res.json();
|
|
3085
|
+
}
|
|
3086
|
+
/**
|
|
3087
|
+
* Query attestations. Public — no auth required.
|
|
3088
|
+
*/
|
|
3089
|
+
async queryAttestations(options = {}) {
|
|
3090
|
+
const params = new URLSearchParams();
|
|
3091
|
+
if (options.country) params.set("country", options.country);
|
|
3092
|
+
if (options.domain) params.set("domain", options.domain);
|
|
3093
|
+
if (options.type) params.set("type", options.type);
|
|
3094
|
+
if (options.agent) params.set("agent", options.agent);
|
|
3095
|
+
if (options.minConsensus !== void 0) params.set("min_consensus", String(options.minConsensus));
|
|
3096
|
+
if (options.since) params.set("since", options.since);
|
|
3097
|
+
if (options.limit) params.set("limit", String(options.limit));
|
|
3098
|
+
const res = await fetch(`${this.baseUrl}/v1/agent/attestations?${params}`);
|
|
3099
|
+
if (!res.ok) return [];
|
|
3100
|
+
const data = await res.json();
|
|
3101
|
+
return data.attestations;
|
|
3102
|
+
}
|
|
3103
|
+
/**
|
|
3104
|
+
* Get attestation detail including all corroborations.
|
|
3105
|
+
*/
|
|
3106
|
+
async getAttestation(attestationId) {
|
|
3107
|
+
const res = await fetch(`${this.baseUrl}/v1/agent/attestations/${attestationId}`);
|
|
3108
|
+
if (!res.ok) {
|
|
3109
|
+
const err = await res.json().catch(() => ({}));
|
|
3110
|
+
throw new Error(`Get attestation failed: ${err.error || res.statusText}`);
|
|
3111
|
+
}
|
|
3112
|
+
return await res.json();
|
|
3113
|
+
}
|
|
3114
|
+
/**
|
|
3115
|
+
* Get consensus summary for a country or domain.
|
|
3116
|
+
*/
|
|
3117
|
+
async getConsensus(options) {
|
|
3118
|
+
const params = new URLSearchParams();
|
|
3119
|
+
if (options.country) params.set("country", options.country);
|
|
3120
|
+
if (options.domain) params.set("domain", options.domain);
|
|
3121
|
+
if (options.type) params.set("type", options.type);
|
|
3122
|
+
const res = await fetch(`${this.baseUrl}/v1/agent/attestations/consensus?${params}`);
|
|
3123
|
+
if (!res.ok) return [];
|
|
3124
|
+
const data = await res.json();
|
|
3125
|
+
return data.consensus;
|
|
3126
|
+
}
|
|
3127
|
+
/**
|
|
3128
|
+
* Verify an attestation's signature locally without trusting the relay.
|
|
3129
|
+
* This is the core of the decentralized witness network — anyone can verify.
|
|
3130
|
+
*/
|
|
3131
|
+
static verifyAttestation(attestation, signingPublicKey) {
|
|
3132
|
+
try {
|
|
3133
|
+
const payload = attestation.claim_type + JSON.stringify(attestation.claim_data) + attestation.timestamp;
|
|
3134
|
+
const payloadBytes = (0, import_tweetnacl_util.decodeUTF8)(payload);
|
|
3135
|
+
const signatureBytes = (0, import_tweetnacl_util.decodeBase64)(attestation.signature);
|
|
3136
|
+
const pubKeyBytes = (0, import_tweetnacl_util.decodeBase64)(signingPublicKey);
|
|
3137
|
+
return import_tweetnacl.default.sign.detached.verify(payloadBytes, signatureBytes, pubKeyBytes);
|
|
3138
|
+
} catch {
|
|
3139
|
+
return false;
|
|
3140
|
+
}
|
|
3141
|
+
}
|
|
2790
3142
|
};
|
|
2791
3143
|
var export_decodeBase64 = import_tweetnacl_util.decodeBase64;
|
|
2792
3144
|
var export_decodeUTF8 = import_tweetnacl_util.decodeUTF8;
|