@voidly/agent-sdk 3.0.0 → 3.2.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
@@ -85,6 +85,17 @@ interface VoidlyAgentConfig {
85
85
  jitterMs?: number;
86
86
  /** Use long-poll for listen() instead of short-interval polling (default: true) */
87
87
  longPoll?: boolean;
88
+ /** Auto-persist ratchet state after every send/receive (default: 'memory' = no persistence) */
89
+ persist?: 'memory' | 'localStorage' | 'indexedDB' | 'file' | 'relay' | 'custom';
90
+ /** Custom persistence: called after each ratchet step with encrypted blob */
91
+ onPersist?: (encryptedState: string) => void | Promise<void>;
92
+ /** Custom persistence: called on startup to load encrypted blob */
93
+ onLoad?: () => string | null | Promise<string | null>;
94
+ /** File path for persist: 'file' (Node.js environments only) */
95
+ persistPath?: string;
96
+ /** Transport preference for listen() — tries in order, falls back automatically
97
+ * Default: ['sse', 'long-poll']. Options: 'websocket' | 'sse' | 'long-poll' */
98
+ transport?: ('websocket' | 'sse' | 'long-poll')[];
88
99
  }
89
100
  interface ListenOptions {
90
101
  /** Milliseconds between polls (default: 2000, min: 500) */
@@ -181,6 +192,16 @@ declare class VoidlyAgent {
181
192
  private _identityCache;
182
193
  private _seenMessageIds;
183
194
  private _decryptFailCount;
195
+ private _rpcHandlers;
196
+ private _rpcPending;
197
+ private _coverTrafficTimer;
198
+ private _rpcListener;
199
+ private _persistMode;
200
+ private _onPersist?;
201
+ private _onLoad?;
202
+ private _persistPath?;
203
+ private _persistKey;
204
+ private _transportPrefs;
184
205
  private constructor();
185
206
  /**
186
207
  * Register a new agent on the Voidly relay.
@@ -244,6 +265,26 @@ declare class VoidlyAgent {
244
265
  signedPrekeyPublic?: string;
245
266
  signedPrekeyId?: number;
246
267
  };
268
+ /** Derive persistence encryption key from signing secret */
269
+ private _derivePersistKey;
270
+ /** Auto-persist ratchet state (called after every ratchet mutation) */
271
+ private _persistRatchetState;
272
+ /** Load persisted ratchet state and restore into memory */
273
+ private _loadPersistedRatchetState;
274
+ /** IndexedDB put helper (browser only) */
275
+ private _idbPut;
276
+ /** IndexedDB get helper (browser only) */
277
+ private _idbGet;
278
+ /**
279
+ * Force-persist current ratchet state.
280
+ * Useful to call before process exit to ensure state is saved.
281
+ */
282
+ flushRatchetState(): Promise<void>;
283
+ /**
284
+ * Restore an agent from credentials with async persistence loading.
285
+ * Use this instead of `fromCredentials()` when using file/relay/custom persistence.
286
+ */
287
+ static fromCredentialsAsync(creds: Parameters<typeof VoidlyAgent.fromCredentials>[0], config?: VoidlyAgentConfig): Promise<VoidlyAgent>;
247
288
  /**
248
289
  * Get the number of messages that failed to decrypt.
249
290
  * Useful for detecting key mismatches, attacks, or corruption.
@@ -296,6 +337,8 @@ declare class VoidlyAgent {
296
337
  messageType?: string;
297
338
  unreadOnly?: boolean;
298
339
  }): Promise<DecryptedMessage[]>;
340
+ /** Decrypt raw message objects (shared by receive(), SSE, WebSocket transports) */
341
+ private _decryptMessages;
299
342
  /**
300
343
  * Delete a message by ID (must be sender or recipient).
301
344
  */
@@ -996,6 +1039,100 @@ declare class VoidlyAgent {
996
1039
  * Stop all active listeners. Useful for clean shutdown.
997
1040
  */
998
1041
  stopAll(): void;
1042
+ /**
1043
+ * Invoke a function on a remote agent. Synchronous RPC over encrypted messaging.
1044
+ * The remote agent must have registered a handler via `onInvoke()`.
1045
+ *
1046
+ * @example
1047
+ * ```ts
1048
+ * // Call a translator agent
1049
+ * const result = await agent.invoke('did:voidly:translator', 'translate', {
1050
+ * text: 'Hello, world!',
1051
+ * to: 'ja',
1052
+ * });
1053
+ * console.log(result.translation); // こんにちは
1054
+ *
1055
+ * // With timeout
1056
+ * const data = await agent.invoke(peerDid, 'analyze', { url: '...' }, 15000);
1057
+ * ```
1058
+ */
1059
+ invoke(targetDid: string, method: string, params?: any, timeoutMs?: number): Promise<any>;
1060
+ /**
1061
+ * Register a handler for incoming RPC invocations.
1062
+ * When another agent calls `invoke(yourDid, method, params)`, your handler runs.
1063
+ *
1064
+ * @example
1065
+ * ```ts
1066
+ * // Register a translation capability
1067
+ * agent.onInvoke('translate', async (params, callerDid) => {
1068
+ * const result = await myTranslateFunction(params.text, params.to);
1069
+ * return { translation: result };
1070
+ * });
1071
+ *
1072
+ * // Register a search capability
1073
+ * agent.onInvoke('search', async (params) => {
1074
+ * return { results: await searchDatabase(params.query) };
1075
+ * });
1076
+ * ```
1077
+ */
1078
+ onInvoke(method: string, handler: (params: any, callerDid: string) => Promise<any>): void;
1079
+ /**
1080
+ * Remove an RPC handler.
1081
+ */
1082
+ offInvoke(method: string): void;
1083
+ /** @internal Start listening for RPC requests and responses */
1084
+ private _ensureRpcListener;
1085
+ /**
1086
+ * Send a message directly to a peer's webhook endpoint, bypassing the relay entirely.
1087
+ * The relay never sees the message — true peer-to-peer encrypted delivery.
1088
+ *
1089
+ * Falls back to relay-based send if direct delivery fails.
1090
+ *
1091
+ * @example
1092
+ * ```ts
1093
+ * // Try direct first, fall back to relay
1094
+ * const result = await agent.sendDirect('did:voidly:peer', 'Hello P2P!');
1095
+ * console.log(result.direct); // true if delivered directly, false if via relay
1096
+ * ```
1097
+ */
1098
+ sendDirect(recipientDid: string, message: string, options?: {
1099
+ contentType?: string;
1100
+ messageType?: string;
1101
+ threadId?: string;
1102
+ ttl?: number;
1103
+ }): Promise<SendResult & {
1104
+ direct: boolean;
1105
+ }>;
1106
+ /**
1107
+ * Enable cover traffic — sends encrypted noise at random intervals.
1108
+ * Makes real messages indistinguishable from cover traffic for any observer
1109
+ * monitoring message timing and frequency.
1110
+ *
1111
+ * Cover messages are encrypted and padded identically to real messages.
1112
+ * The relay cannot distinguish them from real traffic.
1113
+ *
1114
+ * @example
1115
+ * ```ts
1116
+ * // Send noise every ~30s (randomized ±50%)
1117
+ * agent.enableCoverTraffic({ intervalMs: 30000 });
1118
+ *
1119
+ * // Stop cover traffic
1120
+ * agent.disableCoverTraffic();
1121
+ * ```
1122
+ */
1123
+ enableCoverTraffic(options?: {
1124
+ intervalMs?: number;
1125
+ }): void;
1126
+ /**
1127
+ * Disable cover traffic.
1128
+ */
1129
+ disableCoverTraffic(): void;
1130
+ /**
1131
+ * Fetch from primary relay with fallback to alternate relays.
1132
+ * Unlike _timedFetch which only hits one URL, this tries all known relays.
1133
+ * @internal
1134
+ */
1135
+ private _resilientFetch;
999
1136
  /**
1000
1137
  * Start or resume a conversation with another agent.
1001
1138
  * Automatically manages thread IDs, message history, and reply chains.
package/dist/index.d.ts CHANGED
@@ -85,6 +85,17 @@ interface VoidlyAgentConfig {
85
85
  jitterMs?: number;
86
86
  /** Use long-poll for listen() instead of short-interval polling (default: true) */
87
87
  longPoll?: boolean;
88
+ /** Auto-persist ratchet state after every send/receive (default: 'memory' = no persistence) */
89
+ persist?: 'memory' | 'localStorage' | 'indexedDB' | 'file' | 'relay' | 'custom';
90
+ /** Custom persistence: called after each ratchet step with encrypted blob */
91
+ onPersist?: (encryptedState: string) => void | Promise<void>;
92
+ /** Custom persistence: called on startup to load encrypted blob */
93
+ onLoad?: () => string | null | Promise<string | null>;
94
+ /** File path for persist: 'file' (Node.js environments only) */
95
+ persistPath?: string;
96
+ /** Transport preference for listen() — tries in order, falls back automatically
97
+ * Default: ['sse', 'long-poll']. Options: 'websocket' | 'sse' | 'long-poll' */
98
+ transport?: ('websocket' | 'sse' | 'long-poll')[];
88
99
  }
89
100
  interface ListenOptions {
90
101
  /** Milliseconds between polls (default: 2000, min: 500) */
@@ -181,6 +192,16 @@ declare class VoidlyAgent {
181
192
  private _identityCache;
182
193
  private _seenMessageIds;
183
194
  private _decryptFailCount;
195
+ private _rpcHandlers;
196
+ private _rpcPending;
197
+ private _coverTrafficTimer;
198
+ private _rpcListener;
199
+ private _persistMode;
200
+ private _onPersist?;
201
+ private _onLoad?;
202
+ private _persistPath?;
203
+ private _persistKey;
204
+ private _transportPrefs;
184
205
  private constructor();
185
206
  /**
186
207
  * Register a new agent on the Voidly relay.
@@ -244,6 +265,26 @@ declare class VoidlyAgent {
244
265
  signedPrekeyPublic?: string;
245
266
  signedPrekeyId?: number;
246
267
  };
268
+ /** Derive persistence encryption key from signing secret */
269
+ private _derivePersistKey;
270
+ /** Auto-persist ratchet state (called after every ratchet mutation) */
271
+ private _persistRatchetState;
272
+ /** Load persisted ratchet state and restore into memory */
273
+ private _loadPersistedRatchetState;
274
+ /** IndexedDB put helper (browser only) */
275
+ private _idbPut;
276
+ /** IndexedDB get helper (browser only) */
277
+ private _idbGet;
278
+ /**
279
+ * Force-persist current ratchet state.
280
+ * Useful to call before process exit to ensure state is saved.
281
+ */
282
+ flushRatchetState(): Promise<void>;
283
+ /**
284
+ * Restore an agent from credentials with async persistence loading.
285
+ * Use this instead of `fromCredentials()` when using file/relay/custom persistence.
286
+ */
287
+ static fromCredentialsAsync(creds: Parameters<typeof VoidlyAgent.fromCredentials>[0], config?: VoidlyAgentConfig): Promise<VoidlyAgent>;
247
288
  /**
248
289
  * Get the number of messages that failed to decrypt.
249
290
  * Useful for detecting key mismatches, attacks, or corruption.
@@ -296,6 +337,8 @@ declare class VoidlyAgent {
296
337
  messageType?: string;
297
338
  unreadOnly?: boolean;
298
339
  }): Promise<DecryptedMessage[]>;
340
+ /** Decrypt raw message objects (shared by receive(), SSE, WebSocket transports) */
341
+ private _decryptMessages;
299
342
  /**
300
343
  * Delete a message by ID (must be sender or recipient).
301
344
  */
@@ -996,6 +1039,100 @@ declare class VoidlyAgent {
996
1039
  * Stop all active listeners. Useful for clean shutdown.
997
1040
  */
998
1041
  stopAll(): void;
1042
+ /**
1043
+ * Invoke a function on a remote agent. Synchronous RPC over encrypted messaging.
1044
+ * The remote agent must have registered a handler via `onInvoke()`.
1045
+ *
1046
+ * @example
1047
+ * ```ts
1048
+ * // Call a translator agent
1049
+ * const result = await agent.invoke('did:voidly:translator', 'translate', {
1050
+ * text: 'Hello, world!',
1051
+ * to: 'ja',
1052
+ * });
1053
+ * console.log(result.translation); // こんにちは
1054
+ *
1055
+ * // With timeout
1056
+ * const data = await agent.invoke(peerDid, 'analyze', { url: '...' }, 15000);
1057
+ * ```
1058
+ */
1059
+ invoke(targetDid: string, method: string, params?: any, timeoutMs?: number): Promise<any>;
1060
+ /**
1061
+ * Register a handler for incoming RPC invocations.
1062
+ * When another agent calls `invoke(yourDid, method, params)`, your handler runs.
1063
+ *
1064
+ * @example
1065
+ * ```ts
1066
+ * // Register a translation capability
1067
+ * agent.onInvoke('translate', async (params, callerDid) => {
1068
+ * const result = await myTranslateFunction(params.text, params.to);
1069
+ * return { translation: result };
1070
+ * });
1071
+ *
1072
+ * // Register a search capability
1073
+ * agent.onInvoke('search', async (params) => {
1074
+ * return { results: await searchDatabase(params.query) };
1075
+ * });
1076
+ * ```
1077
+ */
1078
+ onInvoke(method: string, handler: (params: any, callerDid: string) => Promise<any>): void;
1079
+ /**
1080
+ * Remove an RPC handler.
1081
+ */
1082
+ offInvoke(method: string): void;
1083
+ /** @internal Start listening for RPC requests and responses */
1084
+ private _ensureRpcListener;
1085
+ /**
1086
+ * Send a message directly to a peer's webhook endpoint, bypassing the relay entirely.
1087
+ * The relay never sees the message — true peer-to-peer encrypted delivery.
1088
+ *
1089
+ * Falls back to relay-based send if direct delivery fails.
1090
+ *
1091
+ * @example
1092
+ * ```ts
1093
+ * // Try direct first, fall back to relay
1094
+ * const result = await agent.sendDirect('did:voidly:peer', 'Hello P2P!');
1095
+ * console.log(result.direct); // true if delivered directly, false if via relay
1096
+ * ```
1097
+ */
1098
+ sendDirect(recipientDid: string, message: string, options?: {
1099
+ contentType?: string;
1100
+ messageType?: string;
1101
+ threadId?: string;
1102
+ ttl?: number;
1103
+ }): Promise<SendResult & {
1104
+ direct: boolean;
1105
+ }>;
1106
+ /**
1107
+ * Enable cover traffic — sends encrypted noise at random intervals.
1108
+ * Makes real messages indistinguishable from cover traffic for any observer
1109
+ * monitoring message timing and frequency.
1110
+ *
1111
+ * Cover messages are encrypted and padded identically to real messages.
1112
+ * The relay cannot distinguish them from real traffic.
1113
+ *
1114
+ * @example
1115
+ * ```ts
1116
+ * // Send noise every ~30s (randomized ±50%)
1117
+ * agent.enableCoverTraffic({ intervalMs: 30000 });
1118
+ *
1119
+ * // Stop cover traffic
1120
+ * agent.disableCoverTraffic();
1121
+ * ```
1122
+ */
1123
+ enableCoverTraffic(options?: {
1124
+ intervalMs?: number;
1125
+ }): void;
1126
+ /**
1127
+ * Disable cover traffic.
1128
+ */
1129
+ disableCoverTraffic(): void;
1130
+ /**
1131
+ * Fetch from primary relay with fallback to alternate relays.
1132
+ * Unlike _timedFetch which only hits one URL, this tries all known relays.
1133
+ * @internal
1134
+ */
1135
+ private _resilientFetch;
999
1136
  /**
1000
1137
  * Start or resume a conversation with another agent.
1001
1138
  * Automatically manages thread IDs, message history, and reply chains.