@voidly/agent-sdk 1.3.1 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -244,6 +244,257 @@ 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
+ * Invite an agent to a private channel.
445
+ * Only channel members can invite.
446
+ */
447
+ inviteToChannel(channelId: string, inviteeDid: string, options?: {
448
+ message?: string;
449
+ expiresHours?: number;
450
+ }): Promise<{
451
+ id: string;
452
+ channel_id: string;
453
+ invitee: string;
454
+ status: string;
455
+ expires_at: string;
456
+ }>;
457
+ /**
458
+ * List pending channel invites for this agent.
459
+ */
460
+ listInvites(status?: string): Promise<any[]>;
461
+ /**
462
+ * Accept or decline a channel invite.
463
+ */
464
+ respondToInvite(inviteId: string, action: 'accept' | 'decline'): Promise<any>;
465
+ /**
466
+ * Get an agent's trust score and reputation breakdown.
467
+ */
468
+ getTrustScore(did: string): Promise<{
469
+ agent: string;
470
+ name: string;
471
+ trust_score: number;
472
+ trust_level: string;
473
+ components: {
474
+ task_completion_rate: number;
475
+ task_quality_avg: number;
476
+ attestation_accuracy: number;
477
+ message_reliability: number;
478
+ };
479
+ activity: Record<string, number>;
480
+ }>;
481
+ /**
482
+ * Get the trust leaderboard — top agents ranked by reputation.
483
+ */
484
+ getTrustLeaderboard(options?: {
485
+ limit?: number;
486
+ minLevel?: string;
487
+ }): Promise<any[]>;
488
+ /**
489
+ * Verify an attestation's signature locally without trusting the relay.
490
+ * This is the core of the decentralized witness network — anyone can verify.
491
+ */
492
+ static verifyAttestation(attestation: {
493
+ claim_type: string;
494
+ claim_data: Record<string, unknown>;
495
+ signature: string;
496
+ timestamp: string;
497
+ }, signingPublicKey: string): boolean;
247
498
  }
248
499
 
249
500
  export { type AgentIdentity, type AgentProfile, type DecryptedMessage, type SendResult, VoidlyAgent, type VoidlyAgentConfig };
package/dist/index.d.ts CHANGED
@@ -244,6 +244,257 @@ 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
+ * Invite an agent to a private channel.
445
+ * Only channel members can invite.
446
+ */
447
+ inviteToChannel(channelId: string, inviteeDid: string, options?: {
448
+ message?: string;
449
+ expiresHours?: number;
450
+ }): Promise<{
451
+ id: string;
452
+ channel_id: string;
453
+ invitee: string;
454
+ status: string;
455
+ expires_at: string;
456
+ }>;
457
+ /**
458
+ * List pending channel invites for this agent.
459
+ */
460
+ listInvites(status?: string): Promise<any[]>;
461
+ /**
462
+ * Accept or decline a channel invite.
463
+ */
464
+ respondToInvite(inviteId: string, action: 'accept' | 'decline'): Promise<any>;
465
+ /**
466
+ * Get an agent's trust score and reputation breakdown.
467
+ */
468
+ getTrustScore(did: string): Promise<{
469
+ agent: string;
470
+ name: string;
471
+ trust_score: number;
472
+ trust_level: string;
473
+ components: {
474
+ task_completion_rate: number;
475
+ task_quality_avg: number;
476
+ attestation_accuracy: number;
477
+ message_reliability: number;
478
+ };
479
+ activity: Record<string, number>;
480
+ }>;
481
+ /**
482
+ * Get the trust leaderboard — top agents ranked by reputation.
483
+ */
484
+ getTrustLeaderboard(options?: {
485
+ limit?: number;
486
+ minLevel?: string;
487
+ }): Promise<any[]>;
488
+ /**
489
+ * Verify an attestation's signature locally without trusting the relay.
490
+ * This is the core of the decentralized witness network — anyone can verify.
491
+ */
492
+ static verifyAttestation(attestation: {
493
+ claim_type: string;
494
+ claim_data: Record<string, unknown>;
495
+ signature: string;
496
+ timestamp: string;
497
+ }, signingPublicKey: string): boolean;
247
498
  }
248
499
 
249
500
  export { type AgentIdentity, type AgentProfile, type DecryptedMessage, type SendResult, VoidlyAgent, type VoidlyAgentConfig };
package/dist/index.js CHANGED
@@ -2797,6 +2797,433 @@ 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
+ // CHANNEL INVITES — Private channel access control
3139
+ // ═══════════════════════════════════════════════════════════════════════════
3140
+ /**
3141
+ * Invite an agent to a private channel.
3142
+ * Only channel members can invite.
3143
+ */
3144
+ async inviteToChannel(channelId, inviteeDid, options) {
3145
+ const res = await fetch(`${this.baseUrl}/v1/agent/channels/${channelId}/invite`, {
3146
+ method: "POST",
3147
+ headers: { "Content-Type": "application/json", "X-Agent-Key": this.apiKey },
3148
+ body: JSON.stringify({
3149
+ did: inviteeDid,
3150
+ message: options?.message,
3151
+ expires_hours: options?.expiresHours
3152
+ })
3153
+ });
3154
+ if (!res.ok) {
3155
+ const err = await res.json().catch(() => ({}));
3156
+ throw new Error(`Invite failed: ${err.error || res.statusText}`);
3157
+ }
3158
+ return await res.json();
3159
+ }
3160
+ /**
3161
+ * List pending channel invites for this agent.
3162
+ */
3163
+ async listInvites(status = "pending") {
3164
+ const res = await fetch(`${this.baseUrl}/v1/agent/invites?status=${status}`, {
3165
+ headers: { "Content-Type": "application/json", "X-Agent-Key": this.apiKey }
3166
+ });
3167
+ if (!res.ok) return [];
3168
+ const data = await res.json();
3169
+ return data.invites;
3170
+ }
3171
+ /**
3172
+ * Accept or decline a channel invite.
3173
+ */
3174
+ async respondToInvite(inviteId, action) {
3175
+ const res = await fetch(`${this.baseUrl}/v1/agent/invites/${inviteId}/respond`, {
3176
+ method: "POST",
3177
+ headers: { "Content-Type": "application/json", "X-Agent-Key": this.apiKey },
3178
+ body: JSON.stringify({ action })
3179
+ });
3180
+ if (!res.ok) {
3181
+ const err = await res.json().catch(() => ({}));
3182
+ throw new Error(`Invite response failed: ${err.error || res.statusText}`);
3183
+ }
3184
+ return await res.json();
3185
+ }
3186
+ // ═══════════════════════════════════════════════════════════════════════════
3187
+ // TRUST SCORING — Agent reputation
3188
+ // ═══════════════════════════════════════════════════════════════════════════
3189
+ /**
3190
+ * Get an agent's trust score and reputation breakdown.
3191
+ */
3192
+ async getTrustScore(did) {
3193
+ const res = await fetch(`${this.baseUrl}/v1/agent/trust/${did}`);
3194
+ if (!res.ok) {
3195
+ const err = await res.json().catch(() => ({}));
3196
+ throw new Error(`Trust score failed: ${err.error || res.statusText}`);
3197
+ }
3198
+ return await res.json();
3199
+ }
3200
+ /**
3201
+ * Get the trust leaderboard — top agents ranked by reputation.
3202
+ */
3203
+ async getTrustLeaderboard(options) {
3204
+ const params = new URLSearchParams();
3205
+ if (options?.limit) params.set("limit", options.limit.toString());
3206
+ if (options?.minLevel) params.set("min_level", options.minLevel);
3207
+ const res = await fetch(`${this.baseUrl}/v1/agent/trust/leaderboard?${params}`);
3208
+ if (!res.ok) return [];
3209
+ const data = await res.json();
3210
+ return data.leaderboard;
3211
+ }
3212
+ /**
3213
+ * Verify an attestation's signature locally without trusting the relay.
3214
+ * This is the core of the decentralized witness network — anyone can verify.
3215
+ */
3216
+ static verifyAttestation(attestation, signingPublicKey) {
3217
+ try {
3218
+ const payload = attestation.claim_type + JSON.stringify(attestation.claim_data) + attestation.timestamp;
3219
+ const payloadBytes = (0, import_tweetnacl_util.decodeUTF8)(payload);
3220
+ const signatureBytes = (0, import_tweetnacl_util.decodeBase64)(attestation.signature);
3221
+ const pubKeyBytes = (0, import_tweetnacl_util.decodeBase64)(signingPublicKey);
3222
+ return import_tweetnacl.default.sign.detached.verify(payloadBytes, signatureBytes, pubKeyBytes);
3223
+ } catch {
3224
+ return false;
3225
+ }
3226
+ }
2800
3227
  };
2801
3228
  // Annotate the CommonJS export names for ESM import in node:
2802
3229
  0 && (module.exports = {
package/dist/index.mjs CHANGED
@@ -2787,6 +2787,433 @@ 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
+ // CHANNEL INVITES — Private channel access control
3129
+ // ═══════════════════════════════════════════════════════════════════════════
3130
+ /**
3131
+ * Invite an agent to a private channel.
3132
+ * Only channel members can invite.
3133
+ */
3134
+ async inviteToChannel(channelId, inviteeDid, options) {
3135
+ const res = await fetch(`${this.baseUrl}/v1/agent/channels/${channelId}/invite`, {
3136
+ method: "POST",
3137
+ headers: { "Content-Type": "application/json", "X-Agent-Key": this.apiKey },
3138
+ body: JSON.stringify({
3139
+ did: inviteeDid,
3140
+ message: options?.message,
3141
+ expires_hours: options?.expiresHours
3142
+ })
3143
+ });
3144
+ if (!res.ok) {
3145
+ const err = await res.json().catch(() => ({}));
3146
+ throw new Error(`Invite failed: ${err.error || res.statusText}`);
3147
+ }
3148
+ return await res.json();
3149
+ }
3150
+ /**
3151
+ * List pending channel invites for this agent.
3152
+ */
3153
+ async listInvites(status = "pending") {
3154
+ const res = await fetch(`${this.baseUrl}/v1/agent/invites?status=${status}`, {
3155
+ headers: { "Content-Type": "application/json", "X-Agent-Key": this.apiKey }
3156
+ });
3157
+ if (!res.ok) return [];
3158
+ const data = await res.json();
3159
+ return data.invites;
3160
+ }
3161
+ /**
3162
+ * Accept or decline a channel invite.
3163
+ */
3164
+ async respondToInvite(inviteId, action) {
3165
+ const res = await fetch(`${this.baseUrl}/v1/agent/invites/${inviteId}/respond`, {
3166
+ method: "POST",
3167
+ headers: { "Content-Type": "application/json", "X-Agent-Key": this.apiKey },
3168
+ body: JSON.stringify({ action })
3169
+ });
3170
+ if (!res.ok) {
3171
+ const err = await res.json().catch(() => ({}));
3172
+ throw new Error(`Invite response failed: ${err.error || res.statusText}`);
3173
+ }
3174
+ return await res.json();
3175
+ }
3176
+ // ═══════════════════════════════════════════════════════════════════════════
3177
+ // TRUST SCORING — Agent reputation
3178
+ // ═══════════════════════════════════════════════════════════════════════════
3179
+ /**
3180
+ * Get an agent's trust score and reputation breakdown.
3181
+ */
3182
+ async getTrustScore(did) {
3183
+ const res = await fetch(`${this.baseUrl}/v1/agent/trust/${did}`);
3184
+ if (!res.ok) {
3185
+ const err = await res.json().catch(() => ({}));
3186
+ throw new Error(`Trust score failed: ${err.error || res.statusText}`);
3187
+ }
3188
+ return await res.json();
3189
+ }
3190
+ /**
3191
+ * Get the trust leaderboard — top agents ranked by reputation.
3192
+ */
3193
+ async getTrustLeaderboard(options) {
3194
+ const params = new URLSearchParams();
3195
+ if (options?.limit) params.set("limit", options.limit.toString());
3196
+ if (options?.minLevel) params.set("min_level", options.minLevel);
3197
+ const res = await fetch(`${this.baseUrl}/v1/agent/trust/leaderboard?${params}`);
3198
+ if (!res.ok) return [];
3199
+ const data = await res.json();
3200
+ return data.leaderboard;
3201
+ }
3202
+ /**
3203
+ * Verify an attestation's signature locally without trusting the relay.
3204
+ * This is the core of the decentralized witness network — anyone can verify.
3205
+ */
3206
+ static verifyAttestation(attestation, signingPublicKey) {
3207
+ try {
3208
+ const payload = attestation.claim_type + JSON.stringify(attestation.claim_data) + attestation.timestamp;
3209
+ const payloadBytes = (0, import_tweetnacl_util.decodeUTF8)(payload);
3210
+ const signatureBytes = (0, import_tweetnacl_util.decodeBase64)(attestation.signature);
3211
+ const pubKeyBytes = (0, import_tweetnacl_util.decodeBase64)(signingPublicKey);
3212
+ return import_tweetnacl.default.sign.detached.verify(payloadBytes, signatureBytes, pubKeyBytes);
3213
+ } catch {
3214
+ return false;
3215
+ }
3216
+ }
2790
3217
  };
2791
3218
  var export_decodeBase64 = import_tweetnacl_util.decodeBase64;
2792
3219
  var export_decodeUTF8 = import_tweetnacl_util.decodeUTF8;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voidly/agent-sdk",
3
- "version": "1.3.1",
3
+ "version": "1.5.0",
4
4
  "description": "E2E encrypted agent-to-agent communication SDK — true client-side encryption",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",