@noosphere/agent-core 0.1.0-alpha.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.
@@ -0,0 +1,600 @@
1
+ import { EventEmitter } from 'events';
2
+ import { Provider, Contract } from 'ethers';
3
+ import { WalletManager } from '@noosphere/crypto';
4
+ export { KeystoreInfo, KeystoreManager, NoosphereKeystore, PaymentWalletInfo, WalletManager } from '@noosphere/crypto';
5
+ export { RegistryConfig, ContainerMetadata as RegistryContainerMetadata, RegistryManager, VerifierMetadata as RegistryVerifierMetadata } from '@noosphere/registry';
6
+
7
+ interface Commitment {
8
+ requestId: string;
9
+ subscriptionId: bigint;
10
+ containerId: string;
11
+ interval: number;
12
+ redundancy: number;
13
+ useDeliveryInbox: boolean;
14
+ feeToken: string;
15
+ feeAmount: bigint;
16
+ walletAddress: string;
17
+ verifier: string;
18
+ coordinator: string;
19
+ }
20
+ interface Payment {
21
+ recipient: string;
22
+ feeToken: string;
23
+ feeAmount: bigint;
24
+ }
25
+ interface ProofVerificationRequest {
26
+ subscriptionId: bigint;
27
+ interval: number;
28
+ submitterAddress: string;
29
+ escrowedAmount: bigint;
30
+ }
31
+ interface RequestStartedEvent {
32
+ requestId: string;
33
+ subscriptionId: bigint;
34
+ containerId: string;
35
+ interval: number;
36
+ redundancy: number;
37
+ useDeliveryInbox: boolean;
38
+ feeAmount: bigint;
39
+ feeToken: string;
40
+ verifier: string;
41
+ coordinator: string;
42
+ walletAddress: string;
43
+ blockNumber: number;
44
+ }
45
+ interface ComputeSubscription {
46
+ owner: string;
47
+ wallet: string;
48
+ containerId: string;
49
+ intervalSeconds: number;
50
+ maxExecutions: number;
51
+ redundancy: number;
52
+ feeToken: string;
53
+ feeAmount: bigint;
54
+ verifier: string;
55
+ routeId: string;
56
+ activeAt: number;
57
+ useDeliveryInbox: boolean;
58
+ }
59
+ interface AgentConfig {
60
+ rpcUrl: string;
61
+ wsRpcUrl?: string;
62
+ privateKey?: string;
63
+ routerAddress: string;
64
+ coordinatorAddress: string;
65
+ deploymentBlock?: number;
66
+ pollingInterval?: number;
67
+ }
68
+ interface NoosphereAgentConfig {
69
+ forwardStats?: boolean;
70
+ manageContainers?: boolean;
71
+ startupWait?: number;
72
+ agent?: {
73
+ name: string;
74
+ apiKey?: string;
75
+ email?: string;
76
+ };
77
+ server?: {
78
+ port: number;
79
+ rateLimit?: {
80
+ numRequests: number;
81
+ period: number;
82
+ };
83
+ };
84
+ hub?: {
85
+ register: boolean;
86
+ url: string;
87
+ keepAlive?: {
88
+ enabled: boolean;
89
+ intervalMs: number;
90
+ batchSize?: number;
91
+ };
92
+ };
93
+ chain: {
94
+ enabled: boolean;
95
+ rpcUrl: string;
96
+ wsRpcUrl?: string;
97
+ trailHeadBlocks?: number;
98
+ routerAddress: string;
99
+ coordinatorAddress?: string;
100
+ deploymentBlock?: number;
101
+ processingInterval?: number;
102
+ wallet: {
103
+ maxGasLimit?: number;
104
+ paymentAddress?: string;
105
+ allowedSimErrors?: string[];
106
+ keystore?: {
107
+ path: string;
108
+ password: string;
109
+ keys?: {
110
+ eth?: string;
111
+ };
112
+ };
113
+ };
114
+ snapshotSync?: {
115
+ sleep?: number;
116
+ batchSize?: number;
117
+ startingSubId?: number;
118
+ syncPeriod?: number;
119
+ };
120
+ connection?: {
121
+ timeout?: number;
122
+ readTimeout?: number;
123
+ writeTimeout?: number;
124
+ };
125
+ gasConfig?: {
126
+ priceMultiplier?: number;
127
+ limitMultiplier?: number;
128
+ };
129
+ };
130
+ docker?: {
131
+ username?: string;
132
+ password?: string;
133
+ };
134
+ containers: ContainerConfig[];
135
+ }
136
+ interface ContainerConfig {
137
+ id: string;
138
+ image: string;
139
+ port?: string;
140
+ env?: Record<string, string>;
141
+ volumes?: string[];
142
+ verifierAddress?: string;
143
+ acceptedPayments?: Record<string, number>;
144
+ }
145
+ interface ContainerMetadata {
146
+ id: string;
147
+ name: string;
148
+ image: string;
149
+ tag?: string;
150
+ port?: string;
151
+ env?: Record<string, string>;
152
+ requirements?: {
153
+ memory?: string;
154
+ cpu?: number;
155
+ gpu?: boolean;
156
+ };
157
+ payments?: {
158
+ basePrice: string;
159
+ unit: string;
160
+ per: string;
161
+ };
162
+ verified?: boolean;
163
+ }
164
+ interface VerifierMetadata {
165
+ address: string;
166
+ name: string;
167
+ image: string;
168
+ tag?: string;
169
+ }
170
+ declare enum FulfillResult {
171
+ FULFILLED = 0,
172
+ INVALID_REQUEST_ID = 1,
173
+ INVALID_COMMITMENT = 2,
174
+ SUBSCRIPTION_BALANCE_INVARIANT_VIOLATION = 3,
175
+ INSUFFICIENT_SUBSCRIPTION_BALANCE = 4,
176
+ COST_EXCEEDS_COMMITMENT = 5
177
+ }
178
+ /**
179
+ * Event emitted when scheduler successfully prepares next interval
180
+ */
181
+ interface CommitmentSuccessEvent {
182
+ subscriptionId: bigint;
183
+ interval: bigint;
184
+ txHash: string;
185
+ blockNumber: number;
186
+ gasUsed: string;
187
+ gasPrice: string;
188
+ gasCost: string;
189
+ requestStartedEvent?: RequestStartedEvent;
190
+ }
191
+
192
+ interface CheckpointData$1 {
193
+ blockNumber: number;
194
+ blockHash?: string;
195
+ blockTimestamp?: number;
196
+ }
197
+ interface EventMonitorOptions {
198
+ loadCheckpoint?: () => CheckpointData$1 | undefined;
199
+ saveCheckpoint?: (checkpoint: CheckpointData$1) => void;
200
+ }
201
+ declare class EventMonitor extends EventEmitter {
202
+ private config;
203
+ private routerAbi;
204
+ private coordinatorAbi;
205
+ private provider;
206
+ private router;
207
+ private coordinator;
208
+ private lastProcessedBlock;
209
+ private useWebSocket;
210
+ private reconnectAttempts;
211
+ private maxReconnectAttempts;
212
+ private isReconnecting;
213
+ private heartbeatInterval;
214
+ private lastEventTime;
215
+ private checkpointCallbacks?;
216
+ constructor(config: AgentConfig, routerAbi: any[], coordinatorAbi: any[], options?: EventMonitorOptions);
217
+ connect(): Promise<void>;
218
+ start(): Promise<void>;
219
+ private replayEvents;
220
+ private startWebSocketListening;
221
+ private startHeartbeat;
222
+ private replayMissedEvents;
223
+ private handleDisconnect;
224
+ private startPolling;
225
+ private processEvent;
226
+ private reconnect;
227
+ private saveCheckpoint;
228
+ stop(): Promise<void>;
229
+ }
230
+
231
+ interface ContainerExecutionResult {
232
+ output: string;
233
+ exitCode: number;
234
+ executionTime: number;
235
+ }
236
+ declare class ContainerManager {
237
+ private docker;
238
+ private runningContainers;
239
+ private persistentContainers;
240
+ private containerPorts;
241
+ constructor();
242
+ runContainer(container: ContainerMetadata, input: string, timeout?: number): Promise<ContainerExecutionResult>;
243
+ private collectContainerResult;
244
+ private pullImage;
245
+ private waitForContainer;
246
+ private timeout;
247
+ private parseMemory;
248
+ checkDockerAvailable(): Promise<boolean>;
249
+ getDockerInfo(): Promise<any>;
250
+ /**
251
+ * Cleanup all running containers
252
+ * Called when agent is shutting down
253
+ */
254
+ cleanup(): Promise<void>;
255
+ /**
256
+ * Get number of running containers
257
+ */
258
+ getRunningContainerCount(): number;
259
+ /**
260
+ * Pre-pull container images and start persistent containers on startup
261
+ * This speeds up request handling by having containers ready
262
+ */
263
+ prepareContainers(containers: Map<string, ContainerMetadata>): Promise<void>;
264
+ /**
265
+ * Start a persistent container that stays running
266
+ */
267
+ private startPersistentContainer;
268
+ /**
269
+ * Stop and remove all persistent containers
270
+ */
271
+ stopPersistentContainers(): Promise<void>;
272
+ }
273
+
274
+ /**
275
+ * SchedulerService
276
+ *
277
+ * Manages periodic commitment generation for active subscriptions.
278
+ * Equivalent to Java's CommitmentGenerationService with cron scheduling.
279
+ */
280
+
281
+ interface SubscriptionState {
282
+ subscriptionId: bigint;
283
+ routeId: string;
284
+ containerId: string;
285
+ client: string;
286
+ wallet: string;
287
+ activeAt: bigint;
288
+ intervalSeconds: bigint;
289
+ maxExecutions: bigint;
290
+ redundancy: number;
291
+ verifier?: string;
292
+ currentInterval: bigint;
293
+ lastProcessedAt: number;
294
+ pendingTx?: string;
295
+ txAttempts: number;
296
+ }
297
+ interface SchedulerConfig {
298
+ cronIntervalMs: number;
299
+ maxRetryAttempts: number;
300
+ syncPeriodMs: number;
301
+ loadCommittedIntervals?: () => string[];
302
+ saveCommittedInterval?: (key: string) => void;
303
+ }
304
+ declare class SchedulerService extends EventEmitter {
305
+ private provider;
306
+ private router;
307
+ private coordinator;
308
+ private agentWallet;
309
+ private subscriptions;
310
+ private committedIntervals;
311
+ private pendingTxs;
312
+ private intervalTimer?;
313
+ private syncTimer?;
314
+ private config;
315
+ private batchReader?;
316
+ private lastSyncedId;
317
+ private maxSubscriptionId?;
318
+ private getContainer?;
319
+ constructor(provider: Provider, router: Contract, coordinator: Contract, agentWallet: string, batchReaderAddress?: string, config?: Partial<SchedulerConfig>, getContainer?: (containerId: string) => any);
320
+ /**
321
+ * Start the scheduler service
322
+ */
323
+ start(): void;
324
+ /**
325
+ * Stop the scheduler service
326
+ */
327
+ stop(): void;
328
+ /**
329
+ * Track a new subscription
330
+ */
331
+ trackSubscription(subscription: Omit<SubscriptionState, 'currentInterval' | 'lastProcessedAt' | 'txAttempts'>): void;
332
+ /**
333
+ * Remove a subscription from tracking
334
+ */
335
+ untrackSubscription(subscriptionId: bigint): void;
336
+ /**
337
+ * Mark an interval as committed (for RequestStarted events)
338
+ * Also persists to storage if callback is configured
339
+ */
340
+ markIntervalCommitted(subscriptionId: bigint, interval: bigint): void;
341
+ /**
342
+ * Add to committed intervals set and persist to storage
343
+ */
344
+ private addCommittedInterval;
345
+ /**
346
+ * Main commitment generation loop (runs every cronIntervalMs)
347
+ * Equivalent to Java's CommitmentGenerationService.generateCommitment()
348
+ */
349
+ private generateCommitments;
350
+ /**
351
+ * Process all active subscriptions
352
+ */
353
+ private processActiveSubscriptions;
354
+ /**
355
+ * Check if subscription should be processed
356
+ */
357
+ private shouldProcess;
358
+ /**
359
+ * Check if interval already has commitments on-chain
360
+ */
361
+ private hasRequestCommitments;
362
+ /**
363
+ * Prepare next interval by calling coordinator contract
364
+ * Equivalent to Java's CoordinatorService.prepareNextInterval()
365
+ */
366
+ private prepareNextInterval;
367
+ /**
368
+ * Prune transactions that have failed
369
+ */
370
+ private pruneFailedTxs;
371
+ /**
372
+ * Sync subscriptions (placeholder for blockchain event listening)
373
+ */
374
+ /**
375
+ * Sync subscriptions from blockchain
376
+ * Reads subscriptions in batches and tracks active ones
377
+ */
378
+ private syncSubscriptions;
379
+ /**
380
+ * Check if subscription is currently active
381
+ */
382
+ private isSubscriptionActive;
383
+ /**
384
+ * Track subscription from ComputeSubscription config
385
+ * Returns true if subscription was tracked, false if skipped
386
+ */
387
+ private trackSubscriptionFromConfig;
388
+ /**
389
+ * Get request ID (hash of subscription ID and interval)
390
+ */
391
+ private getRequestId;
392
+ /**
393
+ * Get scheduler statistics
394
+ */
395
+ getStats(): {
396
+ totalSubscriptions: number;
397
+ activeSubscriptions: number;
398
+ committedIntervals: number;
399
+ pendingTransactions: number;
400
+ };
401
+ /**
402
+ * Get all tracked subscriptions
403
+ */
404
+ getSubscriptions(): SubscriptionState[];
405
+ /**
406
+ * Parse RequestStarted event from transaction receipt
407
+ * This allows the agent to process the event immediately without waiting for WebSocket
408
+ */
409
+ private parseRequestStartedFromReceipt;
410
+ }
411
+
412
+ interface ComputeDeliveredEvent {
413
+ requestId: string;
414
+ subscriptionId: number;
415
+ interval: number;
416
+ containerId: string;
417
+ redundancy: number;
418
+ feeAmount: string;
419
+ feeToken: string;
420
+ input: string;
421
+ output: string;
422
+ txHash: string;
423
+ blockNumber: number;
424
+ gasUsed: bigint;
425
+ gasPrice: bigint;
426
+ }
427
+ interface RequestStartedCallbackEvent {
428
+ requestId: string;
429
+ subscriptionId: number;
430
+ interval: number;
431
+ containerId: string;
432
+ redundancy: number;
433
+ feeAmount: string;
434
+ feeToken: string;
435
+ verifier: string;
436
+ walletAddress: string;
437
+ blockNumber: number;
438
+ }
439
+ /**
440
+ * Event data for commitment success (scheduler prepareNextInterval)
441
+ */
442
+ interface CommitmentSuccessCallbackEvent {
443
+ subscriptionId: bigint;
444
+ interval: bigint;
445
+ txHash: string;
446
+ blockNumber: number;
447
+ gasUsed: string;
448
+ gasPrice: string;
449
+ gasCost: string;
450
+ }
451
+ interface CheckpointData {
452
+ blockNumber: number;
453
+ blockHash?: string;
454
+ blockTimestamp?: number;
455
+ }
456
+ interface NoosphereAgentOptions {
457
+ config: AgentConfig;
458
+ routerAbi?: any[];
459
+ coordinatorAbi?: any[];
460
+ getContainer?: (containerId: string) => ContainerMetadata | undefined;
461
+ containers?: Map<string, ContainerMetadata>;
462
+ walletManager?: WalletManager;
463
+ paymentWallet?: string;
464
+ schedulerConfig?: Partial<SchedulerConfig>;
465
+ onRequestStarted?: (event: RequestStartedCallbackEvent) => void;
466
+ onRequestProcessing?: (requestId: string) => void;
467
+ onRequestSkipped?: (requestId: string, reason: string) => void;
468
+ onRequestFailed?: (requestId: string, error: string) => void;
469
+ onComputeDelivered?: (event: ComputeDeliveredEvent) => void;
470
+ onCommitmentSuccess?: (event: CommitmentSuccessCallbackEvent) => void;
471
+ isRequestProcessed?: (requestId: string) => boolean;
472
+ loadCheckpoint?: () => CheckpointData | undefined;
473
+ saveCheckpoint?: (checkpoint: CheckpointData) => void;
474
+ }
475
+ declare class NoosphereAgent {
476
+ private options;
477
+ private eventMonitor;
478
+ private containerManager;
479
+ private scheduler;
480
+ private walletManager;
481
+ private registryManager;
482
+ private router;
483
+ private coordinator;
484
+ private provider;
485
+ private config;
486
+ private getContainer?;
487
+ private containers?;
488
+ private paymentWallet?;
489
+ private isRunning;
490
+ private processingRequests;
491
+ constructor(options: NoosphereAgentOptions);
492
+ /**
493
+ * Initialize NoosphereAgent from config.json (RECOMMENDED)
494
+ * This loads all configuration including containers from a config file
495
+ *
496
+ * @param configPath - Path to config.json file
497
+ * @param routerAbi - Router contract ABI (optional - defaults to ABIs.Router)
498
+ * @param coordinatorAbi - Coordinator contract ABI (optional - defaults to ABIs.Coordinator)
499
+ * @returns Initialized NoosphereAgent
500
+ */
501
+ static fromConfig(configPath: string, routerAbi?: any[], coordinatorAbi?: any[]): Promise<NoosphereAgent>;
502
+ /**
503
+ * Initialize NoosphereAgent from keystore (RECOMMENDED)
504
+ * This is the secure way to initialize an agent in production
505
+ *
506
+ * @param keystorePath - Path to keystore file
507
+ * @param password - Keystore password
508
+ * @param options - Agent configuration options
509
+ * @returns Initialized NoosphereAgent
510
+ */
511
+ static fromKeystore(keystorePath: string, password: string, options: Omit<NoosphereAgentOptions, 'walletManager'>): Promise<NoosphereAgent>;
512
+ start(): Promise<void>;
513
+ /**
514
+ * Convert registry ContainerMetadata to agent-core ContainerMetadata
515
+ */
516
+ private convertRegistryContainer;
517
+ private handleRequest;
518
+ /**
519
+ * Self-coordination: Calculate priority and wait
520
+ */
521
+ private waitForPriority;
522
+ /**
523
+ * Calculate deterministic priority for this agent and request
524
+ */
525
+ private calculatePriority;
526
+ stop(): Promise<void>;
527
+ getStatus(): {
528
+ running: boolean;
529
+ address: string;
530
+ scheduler: {
531
+ totalSubscriptions: number;
532
+ activeSubscriptions: number;
533
+ committedIntervals: number;
534
+ pendingTransactions: number;
535
+ };
536
+ containers: {
537
+ runningCount: number;
538
+ };
539
+ };
540
+ /**
541
+ * Get scheduler service (for advanced usage)
542
+ */
543
+ getScheduler(): SchedulerService;
544
+ }
545
+
546
+ declare class CommitmentUtils {
547
+ /**
548
+ * Calculate commitment hash
549
+ * Matches the keccak256(abi.encode(commitment)) in Solidity
550
+ */
551
+ static hash(commitment: Commitment): string;
552
+ /**
553
+ * Verify commitment hash matches expected value
554
+ */
555
+ static verify(commitment: Commitment, expectedHash: string): boolean;
556
+ /**
557
+ * Encode commitment data for reportComputeResult
558
+ * Returns ABI-encoded commitment struct
559
+ */
560
+ static encode(commitment: Commitment): string;
561
+ /**
562
+ * Create Commitment from RequestStartedEvent
563
+ */
564
+ static fromEvent(event: any, walletAddress: string): Commitment;
565
+ }
566
+
567
+ declare class RequestIdUtils {
568
+ /**
569
+ * Pack subscriptionId and interval into requestId
570
+ * Matches Solidity: keccak256(abi.encodePacked(subscriptionId, interval))
571
+ */
572
+ static pack(subscriptionId: bigint, interval: number): string;
573
+ /**
574
+ * Unpack requestId into subscriptionId and interval (if stored separately)
575
+ * Note: This is not possible from the hash alone - only for informational purposes
576
+ * In practice, subscriptionId and interval are stored in events
577
+ */
578
+ static format(requestId: string, subscriptionId: bigint, interval: number): string;
579
+ }
580
+
581
+ declare class ConfigLoader {
582
+ /**
583
+ * Load agent configuration from JSON file
584
+ */
585
+ static loadFromFile(configPath: string): NoosphereAgentConfig;
586
+ /**
587
+ * Convert ContainerConfig to ContainerMetadata format
588
+ */
589
+ static containerConfigToMetadata(containerConfig: NoosphereAgentConfig['containers'][0]): ContainerMetadata;
590
+ /**
591
+ * Get all containers from config as ContainerMetadata array
592
+ */
593
+ static getContainersFromConfig(config: NoosphereAgentConfig): Map<string, ContainerMetadata>;
594
+ /**
595
+ * Get container config by ID
596
+ */
597
+ static getContainerConfig(config: NoosphereAgentConfig, containerId: string): NoosphereAgentConfig['containers'][0] | undefined;
598
+ }
599
+
600
+ export { type AgentConfig, type CheckpointData$1 as CheckpointData, type Commitment, type CommitmentSuccessCallbackEvent, type CommitmentSuccessEvent, CommitmentUtils, type ComputeDeliveredEvent, type ComputeSubscription, ConfigLoader, type ContainerConfig, ContainerManager, type ContainerMetadata, EventMonitor, FulfillResult, NoosphereAgent, type NoosphereAgentConfig, type Payment, type ProofVerificationRequest, RequestIdUtils, type RequestStartedCallbackEvent, type RequestStartedEvent, SchedulerService, type VerifierMetadata };