@agrentingai/paperclip-adapter 0.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.
@@ -0,0 +1,1406 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // server/src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ AgrentingClient: () => AgrentingClient,
34
+ MAX_POLLS: () => MAX_POLLS,
35
+ POLL_INTERVALS_MS: () => POLL_INTERVALS_MS,
36
+ autoSelectAgent: () => autoSelectAgent,
37
+ canSubmitTask: () => canSubmitTask,
38
+ checkBalance: () => checkBalance,
39
+ createServerAdapter: () => createServerAdapter,
40
+ createWebhookHandler: () => createWebhookHandler,
41
+ deregisterWebhook: () => deregisterWebhook,
42
+ executeWithRetry: () => executeWithRetry,
43
+ formatAgentResponse: () => formatAgentResponse,
44
+ formatInsufficientBalanceComment: () => formatInsufficientBalanceComment,
45
+ formatLowBalanceComment: () => formatLowBalanceComment,
46
+ forwardCommentToAgrenting: () => forwardCommentToAgrenting,
47
+ getActiveTaskMappings: () => getActiveTaskMappings,
48
+ getAgentProfile: () => getAgentProfile,
49
+ getBackoffMs: () => getBackoffMs,
50
+ getHiring: () => getHiring,
51
+ getHiringMessages: () => getHiringMessages,
52
+ getTaskMessages: () => getTaskMessages,
53
+ getWebhookGracePeriodMs: () => getWebhookGracePeriodMs,
54
+ hireAgent: () => hireAgent,
55
+ listCapabilities: () => listCapabilities,
56
+ listHirings: () => listHirings,
57
+ pollTaskUntilDone: () => pollTaskUntilDone,
58
+ processIncomingMessage: () => processIncomingMessage,
59
+ reassignTask: () => reassignTask,
60
+ registerTaskMapping: () => registerTaskMapping,
61
+ registerWebhook: () => registerWebhook,
62
+ retryHiring: () => retryHiring,
63
+ sendMessageToHiring: () => sendMessageToHiring,
64
+ sendMessageToTask: () => sendMessageToTask,
65
+ unregisterTaskMapping: () => unregisterTaskMapping,
66
+ verifyWebhookSignature: () => verifyWebhookSignature
67
+ });
68
+ module.exports = __toCommonJS(index_exports);
69
+
70
+ // server/src/client.ts
71
+ var DEFAULT_TIMEOUT_MS = 3e4;
72
+ var MAX_RETRIES = 3;
73
+ var AgrentingClient = class {
74
+ baseUrl;
75
+ apiKey;
76
+ constructor(config) {
77
+ this.baseUrl = config.agrentingUrl.replace(/\/+$/, "");
78
+ this.apiKey = config.apiKey;
79
+ }
80
+ headers() {
81
+ return {
82
+ "Content-Type": "application/json",
83
+ "X-API-Key": this.apiKey
84
+ };
85
+ }
86
+ async request(method, path, body) {
87
+ let lastError = null;
88
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
89
+ const controller = new AbortController();
90
+ const timer = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS);
91
+ try {
92
+ const response = await fetch(`${this.baseUrl}${path}`, {
93
+ method,
94
+ headers: this.headers(),
95
+ body: body ? JSON.stringify(body) : void 0,
96
+ signal: controller.signal
97
+ });
98
+ if (!response.ok) {
99
+ const text = await response.text();
100
+ const shouldRetry = response.status === 429 || response.status >= 500;
101
+ if (shouldRetry && attempt < MAX_RETRIES) {
102
+ clearTimeout(timer);
103
+ const retryAfter = response.headers.get("Retry-After");
104
+ let delayMs = Math.min(1e3 * 2 ** attempt, 3e4);
105
+ if (retryAfter) {
106
+ const seconds = parseInt(retryAfter, 10);
107
+ if (!Number.isNaN(seconds) && seconds > 0) {
108
+ delayMs = seconds * 1e3;
109
+ } else {
110
+ const dateMs = Date.parse(retryAfter);
111
+ if (!Number.isNaN(dateMs)) {
112
+ delayMs = Math.max(0, dateMs - Date.now());
113
+ }
114
+ }
115
+ }
116
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
117
+ continue;
118
+ }
119
+ throw new Error(
120
+ `Agrenting API ${response.status}: ${text.slice(0, 500)}`
121
+ );
122
+ }
123
+ const envelope = await response.json();
124
+ if (Array.isArray(envelope.errors) && envelope.errors.length) {
125
+ throw new Error(`API errors: ${envelope.errors.join(", ")}`);
126
+ }
127
+ return envelope.data ?? envelope;
128
+ } catch (err) {
129
+ lastError = err instanceof Error ? err : new Error(String(err));
130
+ if (attempt < MAX_RETRIES) {
131
+ clearTimeout(timer);
132
+ const delayMs = Math.min(1e3 * 2 ** attempt, 3e4);
133
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
134
+ continue;
135
+ }
136
+ throw lastError;
137
+ } finally {
138
+ clearTimeout(timer);
139
+ }
140
+ }
141
+ throw lastError ?? new Error("Unexpected retry loop exit");
142
+ }
143
+ /** Submit a new task to Agrenting.
144
+ * When `maxPrice` is set, the task includes pricing info.
145
+ * Call `createTaskPayment` after this to actually lock escrow funds.
146
+ */
147
+ async createTask(params) {
148
+ const body = {
149
+ provider_agent_id: params.providerAgentId,
150
+ capability: params.capability,
151
+ input: params.input
152
+ };
153
+ if (params.maxPrice) {
154
+ body.max_price = params.maxPrice;
155
+ }
156
+ return this.request("POST", "/api/v1/tasks", body);
157
+ }
158
+ /** Create a payment for an existing task to lock escrow funds.
159
+ * This is the step that actually deducts funds from the client's balance
160
+ * and places them in escrow for the task.
161
+ */
162
+ async createTaskPayment(taskId, options = {}) {
163
+ const body = {
164
+ task_id: taskId
165
+ };
166
+ if (options.cryptoCurrency) body.crypto_currency = options.cryptoCurrency;
167
+ if (options.paymentType) body.payment_type = options.paymentType;
168
+ return this.request("POST", `/api/v1/tasks/${taskId}/payments`, body);
169
+ }
170
+ /** Get payment info for a task */
171
+ async getTaskPayment(taskId) {
172
+ return this.request("GET", `/api/v1/tasks/${taskId}/payments`);
173
+ }
174
+ /** Get the status and result of a task */
175
+ async getTask(taskId) {
176
+ return this.request("GET", `/api/v1/tasks/${taskId}`);
177
+ }
178
+ /** Get task timeline events (progress, attempts, status changes) */
179
+ async getTaskTimeline(taskId) {
180
+ return this.request("GET", `/api/v1/tasks/${taskId}/timeline`);
181
+ }
182
+ /** Get attempt history for a task */
183
+ async getTaskAttempts(taskId) {
184
+ return this.request("GET", `/api/v1/tasks/${taskId}/attempts`);
185
+ }
186
+ /** Get current progress of a task */
187
+ async getTaskProgress(taskId) {
188
+ const task = await this.getTask(taskId);
189
+ return {
190
+ status: task.status,
191
+ progress_percent: task.progress_percent ?? 0,
192
+ progress_message: task.progress_message,
193
+ updated_at: task.updated_at
194
+ };
195
+ }
196
+ /** Register a webhook to receive task lifecycle events.
197
+ * Sends a flat request body matching the backend's expected shape.
198
+ */
199
+ async registerWebhook(params) {
200
+ return this.request("POST", "/api/v1/webhooks", {
201
+ callback_url: params.callbackUrl,
202
+ event_types: params.eventTypes ?? [
203
+ "task.created",
204
+ "task.claimed",
205
+ "task.in_progress",
206
+ "task.completed",
207
+ "task.failed",
208
+ "task.cancelled"
209
+ ]
210
+ });
211
+ }
212
+ /** List registered webhooks */
213
+ async listWebhooks() {
214
+ return this.request("GET", "/api/v1/webhooks");
215
+ }
216
+ /** Delete a registered webhook */
217
+ async deleteWebhook(webhookId) {
218
+ return this.request("DELETE", `/api/v1/webhooks/${webhookId}`);
219
+ }
220
+ /** Cancel a task by ID */
221
+ async cancelTask(taskId) {
222
+ return this.request("POST", `/api/v1/tasks/${taskId}/cancel`);
223
+ }
224
+ /** Discover marketplace agents available for hire.
225
+ * Filters by capability, price range, reputation, and availability.
226
+ */
227
+ async discoverAgents(options = {}) {
228
+ const params = new URLSearchParams();
229
+ if (options.capability) params.set("capability", options.capability);
230
+ if (options.minPrice) params.set("min_price", options.minPrice.toFixed(2));
231
+ if (options.maxPrice) params.set("max_price", options.maxPrice.toFixed(2));
232
+ if (options.minReputation)
233
+ params.set("min_reputation", String(options.minReputation));
234
+ if (options.sortBy) params.set("sort_by", options.sortBy);
235
+ if (options.limit) params.set("limit", String(options.limit));
236
+ return this.request(
237
+ "GET",
238
+ `/api/v1/agents/discover?${params}`
239
+ );
240
+ }
241
+ /** Fetch the current platform balance including available, escrowed, and total. */
242
+ async getBalance() {
243
+ return this.request("GET", "/api/v1/ledger/balance");
244
+ }
245
+ /** List recent transactions for the authenticated agent. */
246
+ async getTransactions(options = {}) {
247
+ const params = new URLSearchParams();
248
+ if (options.limit) params.set("limit", String(options.limit));
249
+ if (options.offset) params.set("offset", String(options.offset));
250
+ if (options.type) params.set("type", options.type);
251
+ return this.request(
252
+ "GET",
253
+ `/api/v1/ledger/transactions?${params}`
254
+ );
255
+ }
256
+ /** Deposit funds into the Agrenting ledger. */
257
+ async deposit(params) {
258
+ return this.request("POST", "/api/v1/ledger/deposit", {
259
+ amount: params.amount,
260
+ currency: params.currency ?? "USD",
261
+ payment_method: params.paymentMethod ?? "crypto"
262
+ });
263
+ }
264
+ /** Withdraw funds from the Agrenting ledger to an external wallet. */
265
+ async withdraw(params) {
266
+ return this.request("POST", "/api/v1/ledger/withdraw", {
267
+ amount: params.amount,
268
+ currency: params.currency ?? "USD",
269
+ withdrawal_address_id: params.withdrawalAddressId
270
+ });
271
+ }
272
+ /** Create a payment intent for off-platform payment processing. */
273
+ async createPaymentIntent(params) {
274
+ return this.request("POST", "/api/v1/payments/create-intent", {
275
+ amount: params.amount,
276
+ currency: params.currency ?? "USD",
277
+ payment_type: params.paymentType ?? "crypto"
278
+ });
279
+ }
280
+ /** Validate connectivity and API key by fetching the account balance */
281
+ async testConnection() {
282
+ try {
283
+ const data = await this.request("GET", "/api/v1/ledger/balance");
284
+ return {
285
+ ok: true,
286
+ message: `Connected. Balance: ${data.total ?? data.available ?? "N/A"} ${data.currency ?? "USD"} (Available: ${data.available ?? "N/A"})`
287
+ };
288
+ } catch (err) {
289
+ return {
290
+ ok: false,
291
+ message: err instanceof Error ? err.message : "Unknown connection error"
292
+ };
293
+ }
294
+ }
295
+ /** Upload a document (e.g. instructions) to Agrenting.
296
+ * Uses the dedicated uploads endpoint which accepts base64-encoded content,
297
+ * separate from the deal-scoped `/api/v1/documents` endpoint.
298
+ */
299
+ async uploadDocument(params) {
300
+ const contentBase64 = Buffer.from(params.content).toString("base64");
301
+ return this.request("POST", "/api/v1/uploads", {
302
+ name: params.name,
303
+ content: contentBase64,
304
+ content_type: params.contentType ?? "text/plain",
305
+ document_type: params.documentType ?? "instructions",
306
+ task_id: params.taskId
307
+ });
308
+ }
309
+ /** Get the full profile of an agent by DID.
310
+ * Returns capabilities, pricing tiers, reviews, and availability.
311
+ */
312
+ async getAgentProfile(agentDid) {
313
+ return this.request("GET", `/api/v1/agents/${encodeURIComponent(agentDid)}`);
314
+ }
315
+ /** Hire/bind an agent to your account.
316
+ * Returns adapter config so Paperclip can auto-provision the agent.
317
+ */
318
+ async hireAgent(agentDid, options = {}) {
319
+ const body = {};
320
+ if (options.pricingModel) body.pricing_model = options.pricingModel;
321
+ return this.request(
322
+ "POST",
323
+ `/api/v1/agents/${encodeURIComponent(agentDid)}/hire`,
324
+ body
325
+ );
326
+ }
327
+ /** Send a message to a running task (mid-task instructions, feedback, or questions).
328
+ * Enables bidirectional communication between the Paperclip user and the remote agent.
329
+ */
330
+ async sendMessageToTask(taskId, options) {
331
+ return this.request("POST", `/api/v1/tasks/${taskId}/messages`, {
332
+ message: options.message,
333
+ message_type: options.messageType ?? "instruction"
334
+ });
335
+ }
336
+ /** Get messages for a task.
337
+ * GET /api/v1/tasks/:id/messages
338
+ */
339
+ async getTaskMessages(taskId) {
340
+ return this.request(
341
+ "GET",
342
+ `/api/v1/tasks/${taskId}/messages`
343
+ );
344
+ }
345
+ /** Reassign a failed or cancelled task to a different agent.
346
+ * If `newAgentDid` is omitted, the platform picks the best available agent.
347
+ */
348
+ async reassignTask(taskId, newAgentDid) {
349
+ const body = {};
350
+ if (newAgentDid) {
351
+ body.new_provider_agent_did = newAgentDid;
352
+ }
353
+ return this.request(
354
+ "POST",
355
+ `/api/v1/tasks/${taskId}/reassign`,
356
+ body
357
+ );
358
+ }
359
+ /** List all available capabilities with descriptions and usage stats.
360
+ * GET /api/v1/capabilities
361
+ */
362
+ async listCapabilities() {
363
+ return this.request("GET", "/api/v1/capabilities");
364
+ }
365
+ /** Send a message to a hiring for communication with the hired agent.
366
+ * POST /api/v1/hirings/:id/messages
367
+ */
368
+ async sendMessageToHiring(hiringId, content) {
369
+ if (content.length > 5e3) {
370
+ throw new Error("Message content exceeds 5000 character limit");
371
+ }
372
+ return this.request(
373
+ "POST",
374
+ `/api/v1/hirings/${hiringId}/messages`,
375
+ { content }
376
+ );
377
+ }
378
+ /** Get messages for a hiring.
379
+ * GET /api/v1/hirings/:id/messages
380
+ */
381
+ async getHiringMessages(hiringId) {
382
+ return this.request(
383
+ "GET",
384
+ `/api/v1/hirings/${hiringId}/messages`
385
+ );
386
+ }
387
+ /** Retry a failed hiring.
388
+ * POST /api/v1/hirings/:id/retry
389
+ */
390
+ async retryHiring(hiringId, options = {}) {
391
+ const body = {};
392
+ if (options.reason) {
393
+ body.reason = options.reason;
394
+ }
395
+ return this.request(
396
+ "POST",
397
+ `/api/v1/hirings/${hiringId}/retry`,
398
+ body
399
+ );
400
+ }
401
+ /** Get a hiring by ID.
402
+ * GET /api/v1/hirings/:id
403
+ */
404
+ async getHiring(hiringId) {
405
+ return this.request("GET", `/api/v1/hirings/${hiringId}`);
406
+ }
407
+ /** List hirings for the authenticated agent.
408
+ * GET /api/v1/hirings
409
+ */
410
+ async listHirings(options = {}) {
411
+ const params = new URLSearchParams();
412
+ if (options.status) params.set("status", options.status);
413
+ if (options.limit) params.set("limit", String(options.limit));
414
+ if (options.offset) params.set("offset", String(options.offset));
415
+ const query = params.toString() ? `?${params}` : "";
416
+ return this.request("GET", `/api/v1/hirings${query}`);
417
+ }
418
+ /** List agents filtered by capability for auto-select.
419
+ * GET /api/v1/agents?capability=X
420
+ */
421
+ async listAgentsByCapability(capability) {
422
+ return this.request(
423
+ "GET",
424
+ `/api/v1/agents?capability=${encodeURIComponent(capability)}`
425
+ );
426
+ }
427
+ };
428
+
429
+ // server/src/crypto.ts
430
+ async function verifyWebhookSignature(rawBody, signature, secret) {
431
+ const crypto = await import("crypto");
432
+ const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("base64");
433
+ const sigBuf = Buffer.from(signature);
434
+ const expectedBuf = Buffer.from(expected);
435
+ if (sigBuf.byteLength !== expectedBuf.byteLength) {
436
+ return false;
437
+ }
438
+ return crypto.timingSafeEqual(sigBuf, expectedBuf);
439
+ }
440
+
441
+ // server/src/webhook-handler.ts
442
+ var taskRegistry = /* @__PURE__ */ new Map();
443
+ var TASK_REGISTRY_TTL_MS = 2 * 60 * 60 * 1e3;
444
+ var TASK_REGISTRY_CLEANUP_INTERVAL_MS = 6e4;
445
+ var registryCleanupTimer = null;
446
+ function sweepStaleRegistryEntries() {
447
+ const now = Date.now();
448
+ for (const [id, entry] of taskRegistry) {
449
+ if (now - entry.startedAt > TASK_REGISTRY_TTL_MS) {
450
+ taskRegistry.delete(id);
451
+ }
452
+ }
453
+ }
454
+ function startRegistryCleanup() {
455
+ if (registryCleanupTimer) return;
456
+ registryCleanupTimer = setInterval(sweepStaleRegistryEntries, TASK_REGISTRY_CLEANUP_INTERVAL_MS);
457
+ registryCleanupTimer.unref();
458
+ }
459
+ function registerTaskMapping(taskId, issueId, companyId, config) {
460
+ taskRegistry.set(taskId, {
461
+ issueId,
462
+ companyId,
463
+ config,
464
+ startedAt: Date.now(),
465
+ status: "pending"
466
+ });
467
+ }
468
+ function unregisterTaskMapping(taskId) {
469
+ taskRegistry.delete(taskId);
470
+ }
471
+ function getActiveTaskMappings() {
472
+ return taskRegistry;
473
+ }
474
+ var eventHandlers = {
475
+ "task.created": async (ctx) => {
476
+ ctx.mapping.status = "pending";
477
+ await ctx.api.postComment(
478
+ ctx.mapping.issueId,
479
+ `**Agrenting task created** \u2014 Task \`${ctx.payload.task_id}\` submitted and awaiting claim.`
480
+ );
481
+ },
482
+ "task.claimed": async (ctx) => {
483
+ ctx.mapping.status = "claimed";
484
+ await ctx.api.postComment(
485
+ ctx.mapping.issueId,
486
+ `**Agrenting task claimed** \u2014 An agent has picked up the task and is preparing to work.`
487
+ );
488
+ },
489
+ "task.in_progress": async (ctx) => {
490
+ ctx.mapping.status = "in_progress";
491
+ const progress = ctx.payload.progress_percent ? ` (${ctx.payload.progress_percent}%)` : "";
492
+ const message = ctx.payload.progress_message ? ` \u2014 ${ctx.payload.progress_message}` : "";
493
+ await ctx.api.postComment(
494
+ ctx.mapping.issueId,
495
+ `**Task in progress**${progress}${message}`
496
+ );
497
+ },
498
+ "task.completed": async (ctx) => {
499
+ ctx.mapping.status = "completed";
500
+ const duration = ((Date.now() - ctx.mapping.startedAt) / 1e3).toFixed(1);
501
+ await ctx.api.updateIssue(ctx.mapping.issueId, {
502
+ status: "done",
503
+ comment: `**Agrenting task completed** \u2014 Task \`${ctx.payload.task_id}\` finished in ${duration}s.${ctx.payload.output ? `
504
+
505
+ ### Output
506
+
507
+ ${ctx.payload.output}` : ""}`
508
+ });
509
+ unregisterTaskMapping(ctx.payload.task_id);
510
+ },
511
+ "task.failed": async (ctx) => {
512
+ ctx.mapping.status = "failed";
513
+ await ctx.api.updateIssue(ctx.mapping.issueId, {
514
+ status: "blocked",
515
+ comment: `**Agrenting task failed** \u2014 Task \`${ctx.payload.task_id}\` encountered an error.
516
+
517
+ **Error:** ${ctx.payload.error_reason ?? "No error reason provided."}`
518
+ });
519
+ unregisterTaskMapping(ctx.payload.task_id);
520
+ },
521
+ "task.cancelled": async (ctx) => {
522
+ ctx.mapping.status = "cancelled";
523
+ await ctx.api.updateIssue(ctx.mapping.issueId, {
524
+ status: "cancelled",
525
+ comment: `**Agrenting task cancelled** \u2014 Task \`${ctx.payload.task_id}\` was cancelled.`
526
+ });
527
+ unregisterTaskMapping(ctx.payload.task_id);
528
+ }
529
+ };
530
+ function createWebhookHandler(options) {
531
+ startRegistryCleanup();
532
+ return async function handleWebhookRequest(rawBody, headers) {
533
+ const signature = headers["x-webhook-signature"] ?? headers["X-Webhook-Signature"] ?? "";
534
+ const eventType = headers["x-webhook-event"] ?? headers["X-Webhook-Event"] ?? "";
535
+ let payload;
536
+ try {
537
+ payload = JSON.parse(rawBody);
538
+ } catch {
539
+ return { status: 400, body: "Invalid JSON" };
540
+ }
541
+ const valid = await verifyWebhookSignature(
542
+ rawBody,
543
+ signature,
544
+ options.webhookSecret
545
+ );
546
+ if (!valid) {
547
+ return { status: 401, body: "Invalid signature" };
548
+ }
549
+ const taskId = payload.task_id;
550
+ if (!taskId) {
551
+ return { status: 400, body: "Missing task_id in payload" };
552
+ }
553
+ const mapping = taskRegistry.get(taskId);
554
+ if (!mapping) {
555
+ return { status: 200, body: "OK (no active mapping)" };
556
+ }
557
+ const handler = eventHandlers[eventType];
558
+ if (handler) {
559
+ try {
560
+ await handler({ api: options.api, mapping, payload });
561
+ } catch (err) {
562
+ console.error("[adapter-agrenting] Webhook handler error:", err);
563
+ return {
564
+ status: 500,
565
+ body: "Internal error"
566
+ };
567
+ }
568
+ } else {
569
+ options.onUnknownEvent?.(eventType, payload);
570
+ }
571
+ return { status: 200, body: "OK" };
572
+ };
573
+ }
574
+
575
+ // server/src/polling.ts
576
+ var POLL_INTERVALS_MS = [1e4, 3e4, 6e4, 12e4];
577
+ var MAX_POLLS = 10;
578
+ function getBackoffMs(attempt) {
579
+ const index = Math.min(attempt, POLL_INTERVALS_MS.length - 1);
580
+ return POLL_INTERVALS_MS[index];
581
+ }
582
+ async function pollTaskUntilDone(options) {
583
+ const client = new AgrentingClient(options.config);
584
+ let pollAttempt = options.startAttempt ?? 0;
585
+ const startTime = Date.now();
586
+ while (pollAttempt < MAX_POLLS) {
587
+ if (options.signal?.aborted) {
588
+ return {
589
+ result: {
590
+ success: false,
591
+ error: "Polling aborted",
592
+ taskId: options.taskId,
593
+ durationMs: Date.now() - startTime
594
+ },
595
+ pollCount: pollAttempt,
596
+ viaPolling: true
597
+ };
598
+ }
599
+ const now = Date.now();
600
+ if (now >= options.deadline) {
601
+ return {
602
+ result: {
603
+ success: false,
604
+ error: `Task timed out after ${options.config.timeoutSec ?? 600}s`,
605
+ taskId: options.taskId,
606
+ durationMs: now - startTime
607
+ },
608
+ pollCount: pollAttempt,
609
+ viaPolling: true
610
+ };
611
+ }
612
+ if (pollAttempt > 0) {
613
+ const waitMs = getBackoffMs(pollAttempt);
614
+ await new Promise((resolve) => {
615
+ const timer = setTimeout(resolve, waitMs);
616
+ if (options.signal) {
617
+ options.signal.addEventListener("abort", () => {
618
+ clearTimeout(timer);
619
+ resolve();
620
+ }, { once: true });
621
+ }
622
+ });
623
+ }
624
+ const task = await client.getTask(options.taskId);
625
+ pollAttempt++;
626
+ options.onStatusUpdate?.(task, pollAttempt);
627
+ if (task.status === "completed") {
628
+ return {
629
+ result: {
630
+ success: true,
631
+ output: task.output,
632
+ taskId: options.taskId,
633
+ durationMs: Date.now() - startTime
634
+ },
635
+ pollCount: pollAttempt,
636
+ viaPolling: true
637
+ };
638
+ }
639
+ if (task.status === "failed") {
640
+ return {
641
+ result: {
642
+ success: false,
643
+ error: task.error_reason ?? "Task failed with no reason provided",
644
+ taskId: options.taskId,
645
+ durationMs: Date.now() - startTime
646
+ },
647
+ pollCount: pollAttempt,
648
+ viaPolling: true
649
+ };
650
+ }
651
+ if (task.status === "cancelled") {
652
+ return {
653
+ result: {
654
+ success: false,
655
+ error: "Task was cancelled",
656
+ taskId: options.taskId,
657
+ durationMs: Date.now() - startTime
658
+ },
659
+ pollCount: pollAttempt,
660
+ viaPolling: true
661
+ };
662
+ }
663
+ }
664
+ const finalTask = await client.getTask(options.taskId);
665
+ if (finalTask.status === "completed") {
666
+ return {
667
+ result: {
668
+ success: true,
669
+ output: finalTask.output,
670
+ taskId: options.taskId,
671
+ durationMs: Date.now() - startTime
672
+ },
673
+ pollCount: pollAttempt + 1,
674
+ viaPolling: true
675
+ };
676
+ }
677
+ return {
678
+ result: {
679
+ success: false,
680
+ error: `Task did not complete after ${pollAttempt} polls (max ${MAX_POLLS})`,
681
+ taskId: options.taskId,
682
+ durationMs: Date.now() - startTime
683
+ },
684
+ pollCount: pollAttempt,
685
+ viaPolling: true
686
+ };
687
+ }
688
+ function getWebhookGracePeriodMs(config) {
689
+ const timeoutMs = (config.timeoutSec ?? 600) * 1e3;
690
+ return Math.min(6e4, timeoutMs * 0.1);
691
+ }
692
+
693
+ // server/src/balance-monitor.ts
694
+ async function checkBalance(options) {
695
+ const client = new AgrentingClient(options.config);
696
+ const lowThreshold = options.lowBalanceThresholdUsd ?? 10;
697
+ const minSubmission = options.minSubmissionBalanceUsd ?? 1;
698
+ try {
699
+ const raw = await client.getBalance();
700
+ const available = parseFloat(raw.available);
701
+ const escrow = parseFloat(raw.escrow);
702
+ const total = parseFloat(raw.total);
703
+ const anyNaN = Number.isNaN(available) || Number.isNaN(escrow) || Number.isNaN(total);
704
+ if (anyNaN) {
705
+ return {
706
+ available: 0,
707
+ escrow: 0,
708
+ total: 0,
709
+ currency: raw.currency ?? "USD",
710
+ isLow: true,
711
+ isInsufficient: true
712
+ };
713
+ }
714
+ return {
715
+ available,
716
+ escrow,
717
+ total,
718
+ currency: raw.currency ?? "USD",
719
+ isLow: available < lowThreshold,
720
+ isInsufficient: available < minSubmission
721
+ };
722
+ } catch {
723
+ return {
724
+ available: 0,
725
+ escrow: 0,
726
+ total: 0,
727
+ currency: "USD",
728
+ isLow: true,
729
+ isInsufficient: true
730
+ };
731
+ }
732
+ }
733
+ async function canSubmitTask(options) {
734
+ const balanceInfo = await checkBalance(options);
735
+ if (balanceInfo.isInsufficient) {
736
+ return {
737
+ ok: false,
738
+ reason: `Insufficient balance: ${formatUsd(balanceInfo.available)}. Minimum required: ${formatUsd(options.minSubmissionBalanceUsd ?? 1)}.`
739
+ };
740
+ }
741
+ return { ok: true };
742
+ }
743
+ function formatUsd(amount) {
744
+ return `$${amount.toFixed(2)}`;
745
+ }
746
+ function formatLowBalanceComment(balanceInfo) {
747
+ return `**Agrenting balance warning** \u2014 Available: ${formatUsd(balanceInfo.available)} ${balanceInfo.currency} (Escrowed: ${formatUsd(balanceInfo.escrow)}). Funds are running low. Add credits to avoid task submission failures.`;
748
+ }
749
+ function formatInsufficientBalanceComment(balanceInfo) {
750
+ return `**Agrenting task blocked** \u2014 Insufficient balance: ${formatUsd(balanceInfo.available)} ${balanceInfo.currency}. Please add credits to your Agrenting account before retrying.`;
751
+ }
752
+
753
+ // server/src/comment-sync.ts
754
+ function formatAgentResponse(agentName, message) {
755
+ return `**${agentName} says:**
756
+
757
+ ${message}`;
758
+ }
759
+ async function forwardCommentToAgrenting(config, taskId, comment, authorName) {
760
+ const client = new AgrentingClient(config);
761
+ const formattedComment = authorName ? `[${authorName}]: ${comment}` : comment;
762
+ try {
763
+ return await client.sendMessageToTask(taskId, { message: formattedComment });
764
+ } catch (err) {
765
+ console.error(
766
+ `[adapter-agrenting] Failed to forward comment to task ${taskId}:`,
767
+ err instanceof Error ? err.message : String(err)
768
+ );
769
+ return null;
770
+ }
771
+ }
772
+ function processIncomingMessage(message) {
773
+ const senderName = message.sender_name ?? "Agent";
774
+ return formatAgentResponse(senderName, message.content);
775
+ }
776
+
777
+ // server/src/adapter.ts
778
+ var DEFAULT_WEBHOOK_PORT = 8765;
779
+ var MAX_WEBHOOK_BODY_SIZE = 1024 * 1024;
780
+ var STALE_TASK_CLEANUP_INTERVAL_MS = 6e4;
781
+ var STALE_TASK_TTL_MS = 2 * 60 * 60 * 1e3;
782
+ var pendingTasks = /* @__PURE__ */ new Map();
783
+ var webhookServer = null;
784
+ var staleCleanupTimer = null;
785
+ function sweepStaleTasks() {
786
+ const now = Date.now();
787
+ for (const [id, entry] of pendingTasks) {
788
+ if (entry.settled || now - entry.createdAt > STALE_TASK_TTL_MS) {
789
+ pendingTasks.delete(id);
790
+ }
791
+ }
792
+ }
793
+ function getConfigSchema() {
794
+ return {
795
+ type: "object",
796
+ required: ["agrentingUrl", "apiKey", "agentDid"],
797
+ properties: {
798
+ agrentingUrl: {
799
+ type: "string",
800
+ format: "uri",
801
+ description: "Agrenting platform URL (e.g. https://www.agrenting.com)",
802
+ default: "https://www.agrenting.com"
803
+ },
804
+ apiKey: {
805
+ type: "string",
806
+ description: "Agrenting API key for authentication",
807
+ sensitive: true
808
+ },
809
+ agentDid: {
810
+ type: "string",
811
+ description: "Decentralized identifier of the target agent (did:agrenting:...)"
812
+ },
813
+ webhookSecret: {
814
+ type: "string",
815
+ description: "Webhook signing secret for task completion callbacks",
816
+ sensitive: true
817
+ },
818
+ webhookCallbackUrl: {
819
+ type: "string",
820
+ format: "uri",
821
+ description: "URL where Agrenting should POST task events (e.g. https://your-host:8765/webhook)"
822
+ },
823
+ pricingModel: {
824
+ type: "string",
825
+ enum: ["fixed", "per-token", "subscription"],
826
+ description: "Pricing model for this agent",
827
+ default: "fixed"
828
+ },
829
+ timeoutSec: {
830
+ type: "integer",
831
+ minimum: 10,
832
+ maximum: 3600,
833
+ description: "Task timeout in seconds",
834
+ default: 600
835
+ },
836
+ instructionsBundleMode: {
837
+ type: "string",
838
+ enum: ["managed", "inline"],
839
+ description: "How agent instructions are delivered",
840
+ default: "inline"
841
+ }
842
+ }
843
+ };
844
+ }
845
+ async function testEnvironment(config) {
846
+ const client = new AgrentingClient(config);
847
+ return client.testConnection();
848
+ }
849
+ async function startWebhookListener(config) {
850
+ if (webhookServer) {
851
+ const addr = webhookServer.address();
852
+ const port2 = typeof addr === "object" && addr ? addr.port : DEFAULT_WEBHOOK_PORT;
853
+ return `http://localhost:${port2}/webhook`;
854
+ }
855
+ const http = await import("http");
856
+ const port = process.env.PAPERCLIP_WEBHOOK_PORT ? parseInt(process.env.PAPERCLIP_WEBHOOK_PORT, 10) : DEFAULT_WEBHOOK_PORT;
857
+ return new Promise((resolve, reject) => {
858
+ const server = http.createServer(async (req, res) => {
859
+ if (req.method !== "POST" || req.url !== "/webhook") {
860
+ res.writeHead(404);
861
+ res.end("Not found");
862
+ return;
863
+ }
864
+ let bodyChunks = [];
865
+ let bodyLength = 0;
866
+ let bodyTooLarge = false;
867
+ req.on("data", (chunk) => {
868
+ bodyLength += chunk.length;
869
+ if (bodyLength > MAX_WEBHOOK_BODY_SIZE) {
870
+ bodyTooLarge = true;
871
+ res.writeHead(413);
872
+ res.end("Request body too large");
873
+ req.destroy();
874
+ return;
875
+ }
876
+ bodyChunks.push(chunk);
877
+ });
878
+ req.on("end", async () => {
879
+ if (bodyTooLarge) return;
880
+ const rawBody = Buffer.concat(bodyChunks).toString("utf8");
881
+ bodyChunks = [];
882
+ const taskId = req.headers["x-webhook-task-id"];
883
+ const signature = req.headers["x-webhook-signature"] || "";
884
+ let payload;
885
+ try {
886
+ payload = JSON.parse(rawBody);
887
+ } catch {
888
+ res.writeHead(400);
889
+ res.end("Invalid JSON");
890
+ return;
891
+ }
892
+ if (config.webhookSecret) {
893
+ if (!signature) {
894
+ res.writeHead(401);
895
+ res.end("Missing signature");
896
+ return;
897
+ }
898
+ const valid = await verifyWebhookSignature(
899
+ rawBody,
900
+ signature,
901
+ config.webhookSecret
902
+ );
903
+ if (!valid) {
904
+ res.writeHead(401);
905
+ res.end("Invalid signature");
906
+ return;
907
+ }
908
+ }
909
+ const resolvedTaskId = taskId ?? payload.task_id ?? payload.taskId;
910
+ if (resolvedTaskId) {
911
+ const pending = pendingTasks.get(resolvedTaskId);
912
+ if (pending && !pending.settled) {
913
+ const status = payload.status ?? pending.status;
914
+ pending.status = status;
915
+ pending.progressPercent = payload.progress_percent ?? pending.progressPercent;
916
+ pending.progressMessage = payload.progress_message ?? pending.progressMessage;
917
+ if (status === "completed") {
918
+ pending.settled = true;
919
+ pending.resolve({
920
+ success: true,
921
+ output: payload.output ?? JSON.stringify(payload.output ?? {}),
922
+ taskId: resolvedTaskId,
923
+ durationMs: Date.now() - pending.startedAt
924
+ });
925
+ } else if (status === "failed") {
926
+ pending.settled = true;
927
+ pending.resolve({
928
+ success: false,
929
+ error: payload.error_reason ?? "Task failed with no reason provided",
930
+ taskId: resolvedTaskId,
931
+ durationMs: Date.now() - pending.startedAt
932
+ });
933
+ } else if (status === "cancelled") {
934
+ pending.settled = true;
935
+ pending.resolve({
936
+ success: false,
937
+ error: "Task was cancelled",
938
+ taskId: resolvedTaskId,
939
+ durationMs: Date.now() - pending.startedAt
940
+ });
941
+ }
942
+ }
943
+ }
944
+ res.writeHead(200, { "Content-Type": "text/plain" });
945
+ res.end("OK");
946
+ });
947
+ });
948
+ server.listen(port, () => {
949
+ webhookServer = server;
950
+ if (!staleCleanupTimer) {
951
+ staleCleanupTimer = setInterval(sweepStaleTasks, STALE_TASK_CLEANUP_INTERVAL_MS);
952
+ staleCleanupTimer.unref();
953
+ }
954
+ resolve(`http://localhost:${port}/webhook`);
955
+ });
956
+ server.on("error", reject);
957
+ });
958
+ }
959
+ var WEBHOOK_STOP_TIMEOUT_MS = 5e3;
960
+ async function stopWebhookListener() {
961
+ if (!webhookServer) {
962
+ return;
963
+ }
964
+ if (staleCleanupTimer) {
965
+ clearInterval(staleCleanupTimer);
966
+ staleCleanupTimer = null;
967
+ }
968
+ const server = webhookServer;
969
+ webhookServer = null;
970
+ if ("closeAllConnections" in server && typeof server.closeAllConnections === "function") {
971
+ server.closeAllConnections();
972
+ }
973
+ return new Promise((resolve) => {
974
+ const timeout = setTimeout(() => {
975
+ resolve();
976
+ }, WEBHOOK_STOP_TIMEOUT_MS);
977
+ timeout.unref();
978
+ server.close(() => {
979
+ clearTimeout(timeout);
980
+ resolve();
981
+ });
982
+ });
983
+ }
984
+ async function registerWebhook(config, callbackUrl) {
985
+ const client = new AgrentingClient(config);
986
+ const url = callbackUrl ?? await startWebhookListener(config);
987
+ const result = await client.registerWebhook({
988
+ callbackUrl: url,
989
+ eventTypes: [
990
+ "task.created",
991
+ "task.claimed",
992
+ "task.in_progress",
993
+ "task.completed",
994
+ "task.failed",
995
+ "task.cancelled"
996
+ ]
997
+ });
998
+ return {
999
+ id: result.id,
1000
+ secretKey: result.secret_key,
1001
+ callbackUrl: result.callback_url
1002
+ };
1003
+ }
1004
+ async function deregisterWebhook(config, webhookId) {
1005
+ const client = new AgrentingClient(config);
1006
+ await client.deleteWebhook(webhookId);
1007
+ }
1008
+ async function execute(config, params) {
1009
+ const client = new AgrentingClient(config);
1010
+ const startTime = Date.now();
1011
+ if (config.instructionsBundleMode === "managed" && params.instructions) {
1012
+ await client.uploadDocument({
1013
+ name: "instructions",
1014
+ content: params.instructions,
1015
+ documentType: "instructions"
1016
+ });
1017
+ }
1018
+ const balanceCheck = await canSubmitTask({ config });
1019
+ if (!balanceCheck.ok) {
1020
+ console.warn(`[adapter-agrenting] ${balanceCheck.reason}`);
1021
+ }
1022
+ const task = await client.createTask({
1023
+ providerAgentId: config.agentDid,
1024
+ capability: params.capability,
1025
+ input: params.input,
1026
+ maxPrice: params.maxPrice,
1027
+ paymentType: params.paymentType
1028
+ });
1029
+ const taskId = task.id;
1030
+ let payment;
1031
+ if (params.maxPrice) {
1032
+ try {
1033
+ const paymentOptions = {};
1034
+ if (params.paymentType) paymentOptions.paymentType = params.paymentType;
1035
+ payment = await client.createTaskPayment(taskId, paymentOptions);
1036
+ console.log(`[adapter-agrenting] Escrow locked for task ${taskId}: ${payment.amount} ${payment.currency} (${payment.status})`);
1037
+ } catch (err) {
1038
+ console.error(`[adapter-agrenting] Failed to lock escrow for task ${taskId}, cancelling orphaned task:`, err);
1039
+ try {
1040
+ await client.cancelTask(taskId);
1041
+ } catch {
1042
+ }
1043
+ return {
1044
+ success: false,
1045
+ error: `Escrow payment failed: ${err instanceof Error ? err.message : String(err)}`,
1046
+ taskId,
1047
+ durationMs: Date.now() - startTime
1048
+ };
1049
+ }
1050
+ }
1051
+ if (config.webhookCallbackUrl || config.webhookSecret) {
1052
+ registerTaskMapping(taskId, taskId, config.agrentingUrl, config);
1053
+ return executeWithWebhook(client, config, taskId, startTime);
1054
+ }
1055
+ return executeWithPolling(config, taskId, startTime);
1056
+ }
1057
+ async function executeWithWebhook(_client, config, taskId, startTime) {
1058
+ await startWebhookListener(config);
1059
+ const deadline = startTime + (config.timeoutSec ?? 600) * 1e3;
1060
+ const abortController = new AbortController();
1061
+ const pending = new Promise((resolve) => {
1062
+ pendingTasks.set(taskId, {
1063
+ resolve,
1064
+ status: "pending",
1065
+ progressPercent: 0,
1066
+ startedAt: startTime,
1067
+ createdAt: Date.now(),
1068
+ settled: false
1069
+ });
1070
+ }).then((result) => {
1071
+ abortController.abort();
1072
+ return result;
1073
+ });
1074
+ const timeout = new Promise((resolve) => {
1075
+ const ms = deadline - Date.now();
1076
+ setTimeout(() => {
1077
+ const entry = pendingTasks.get(taskId);
1078
+ if (entry && !entry.settled) {
1079
+ entry.settled = true;
1080
+ }
1081
+ resolve({
1082
+ success: false,
1083
+ error: `Task timed out after ${config.timeoutSec ?? 600}s`,
1084
+ taskId,
1085
+ durationMs: Date.now() - startTime
1086
+ });
1087
+ }, Math.max(ms, 0));
1088
+ });
1089
+ return Promise.race([pending, timeout.then(async (result) => {
1090
+ if (!result.success && result.error?.includes("timed out")) {
1091
+ const pollingFallback = await pollTaskUntilDone({
1092
+ config,
1093
+ taskId,
1094
+ deadline,
1095
+ signal: abortController.signal
1096
+ });
1097
+ return pollingFallback.result;
1098
+ }
1099
+ return result;
1100
+ })]);
1101
+ }
1102
+ async function executeWithPolling(config, taskId, startTime) {
1103
+ const deadline = startTime + (config.timeoutSec ?? 600) * 1e3;
1104
+ const { result } = await pollTaskUntilDone({ config, taskId, deadline });
1105
+ return result;
1106
+ }
1107
+ async function getTaskProgress(config, taskId) {
1108
+ const client = new AgrentingClient(config);
1109
+ const [progress, timeline] = await Promise.all([
1110
+ client.getTaskProgress(taskId),
1111
+ client.getTaskTimeline(taskId)
1112
+ ]);
1113
+ return {
1114
+ status: progress.status,
1115
+ progressPercent: progress.progress_percent,
1116
+ progressMessage: progress.progress_message,
1117
+ timeline: timeline.events
1118
+ };
1119
+ }
1120
+ async function discoverAgents(config, options = {}) {
1121
+ const client = new AgrentingClient(config);
1122
+ return client.discoverAgents(options);
1123
+ }
1124
+ async function getBalance(config) {
1125
+ const client = new AgrentingClient(config);
1126
+ return client.getBalance();
1127
+ }
1128
+ async function getTransactions(config, options = {}) {
1129
+ const client = new AgrentingClient(config);
1130
+ return client.getTransactions(options);
1131
+ }
1132
+ async function deposit(config, params) {
1133
+ const client = new AgrentingClient(config);
1134
+ return client.deposit(params);
1135
+ }
1136
+ async function withdraw(config, params) {
1137
+ const client = new AgrentingClient(config);
1138
+ return client.withdraw(params);
1139
+ }
1140
+ async function getTaskPayment(config, taskId) {
1141
+ const client = new AgrentingClient(config);
1142
+ try {
1143
+ return await client.getTaskPayment(taskId);
1144
+ } catch {
1145
+ return void 0;
1146
+ }
1147
+ }
1148
+ async function cancelTask(config, taskId) {
1149
+ const client = new AgrentingClient(config);
1150
+ try {
1151
+ await client.cancelTask(taskId);
1152
+ pendingTasks.delete(taskId);
1153
+ return { success: true };
1154
+ } catch (err) {
1155
+ return {
1156
+ success: false,
1157
+ error: err instanceof Error ? err.message : "Failed to cancel task"
1158
+ };
1159
+ }
1160
+ }
1161
+ var TASK_MAX_RETRIES = 2;
1162
+ var TASK_RETRY_BASE_DELAY_MS = 1e3;
1163
+ var TASK_RETRY_MAX_DELAY_MS = 3e4;
1164
+ async function hireAgent(config, agentDid) {
1165
+ const client = new AgrentingClient(config);
1166
+ return client.hireAgent(agentDid);
1167
+ }
1168
+ async function getAgentProfile(config, agentDid) {
1169
+ const client = new AgrentingClient(config);
1170
+ return client.getAgentProfile(agentDid);
1171
+ }
1172
+ async function sendMessageToTask(config, taskId, message) {
1173
+ const client = new AgrentingClient(config);
1174
+ return client.sendMessageToTask(taskId, { message });
1175
+ }
1176
+ async function getTaskMessages(config, taskId) {
1177
+ const client = new AgrentingClient(config);
1178
+ return client.getTaskMessages(taskId);
1179
+ }
1180
+ async function reassignTask(config, taskId, newAgentDid) {
1181
+ const client = new AgrentingClient(config);
1182
+ return client.reassignTask(taskId, newAgentDid);
1183
+ }
1184
+ async function listCapabilities(config) {
1185
+ const client = new AgrentingClient(config);
1186
+ return client.listCapabilities();
1187
+ }
1188
+ async function sendMessageToHiring(config, hiringId, message) {
1189
+ const client = new AgrentingClient(config);
1190
+ return client.sendMessageToHiring(hiringId, message);
1191
+ }
1192
+ async function getHiringMessages(config, hiringId) {
1193
+ const client = new AgrentingClient(config);
1194
+ return client.getHiringMessages(hiringId);
1195
+ }
1196
+ async function retryHiring(config, hiringId, options) {
1197
+ const client = new AgrentingClient(config);
1198
+ return client.retryHiring(hiringId, options);
1199
+ }
1200
+ async function getHiring(config, hiringId) {
1201
+ const client = new AgrentingClient(config);
1202
+ return client.getHiring(hiringId);
1203
+ }
1204
+ async function listHirings(config, options) {
1205
+ const client = new AgrentingClient(config);
1206
+ return client.listHirings(options);
1207
+ }
1208
+ async function autoSelectAgent(config, options) {
1209
+ const client = new AgrentingClient(config);
1210
+ const capabilities = await client.listCapabilities();
1211
+ const capabilityExists = capabilities.some(
1212
+ (c) => c.name === options.capability || c.name.toLowerCase() === options.capability.toLowerCase()
1213
+ );
1214
+ if (!capabilityExists) {
1215
+ throw new Error(`Capability "${options.capability}" not found. Available: ${capabilities.map((c) => c.name).join(", ")}`);
1216
+ }
1217
+ const agents = await client.listAgentsByCapability(options.capability);
1218
+ if (agents.length === 0) {
1219
+ throw new Error(`No agents available for capability "${options.capability}"`);
1220
+ }
1221
+ let filtered = agents;
1222
+ if (options.maxPrice) {
1223
+ const maxPriceNum = parseFloat(options.maxPrice);
1224
+ filtered = filtered.filter((a) => {
1225
+ if (!a.base_price) return true;
1226
+ return parseFloat(a.base_price) <= maxPriceNum;
1227
+ });
1228
+ }
1229
+ if (options.minReputation) {
1230
+ const minRep = options.minReputation;
1231
+ filtered = filtered.filter((a) => {
1232
+ if (!a.reputation_score) return false;
1233
+ return a.reputation_score >= minRep;
1234
+ });
1235
+ }
1236
+ if (filtered.length === 0) {
1237
+ throw new Error(
1238
+ `No agents match criteria for capability "${options.capability}" (maxPrice=${options.maxPrice ?? "any"}, minReputation=${options.minReputation ?? "any"})`
1239
+ );
1240
+ }
1241
+ const sortBy = options.sortBy ?? "reputation_score";
1242
+ if (options.preferAvailable ?? true) {
1243
+ filtered.sort((a, b) => {
1244
+ const aAvail = a.availability_status === "available" ? 0 : 1;
1245
+ const bAvail = b.availability_status === "available" ? 0 : 1;
1246
+ if (aAvail !== bAvail) return aAvail - bAvail;
1247
+ return 0;
1248
+ });
1249
+ }
1250
+ filtered.sort((a, b) => {
1251
+ if (sortBy === "reputation_score") {
1252
+ return (b.reputation_score ?? 0) - (a.reputation_score ?? 0);
1253
+ }
1254
+ if (sortBy === "base_price") {
1255
+ const aPrice = parseFloat(a.base_price ?? "999999");
1256
+ const bPrice = parseFloat(b.base_price ?? "999999");
1257
+ return aPrice - bPrice;
1258
+ }
1259
+ if (sortBy === "availability") {
1260
+ const aAvail = a.availability_status === "available" ? 0 : 1;
1261
+ const bAvail = b.availability_status === "available" ? 0 : 1;
1262
+ return aAvail - bAvail;
1263
+ }
1264
+ return 0;
1265
+ });
1266
+ const selectedAgent = filtered[0];
1267
+ const hireResult = await client.hireAgent(selectedAgent.did);
1268
+ return {
1269
+ ...hireResult,
1270
+ selectedAgent
1271
+ };
1272
+ }
1273
+ async function executeWithRetry(config, params) {
1274
+ const maxRetries = params.maxRetries ?? TASK_MAX_RETRIES;
1275
+ let lastResult;
1276
+ let lastError = null;
1277
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
1278
+ try {
1279
+ lastResult = await execute(config, params);
1280
+ if (lastResult.success) {
1281
+ return lastResult;
1282
+ }
1283
+ if (attempt < maxRetries && lastResult.error) {
1284
+ const delayMs = Math.min(
1285
+ TASK_RETRY_BASE_DELAY_MS * Math.pow(2, attempt),
1286
+ TASK_RETRY_MAX_DELAY_MS
1287
+ );
1288
+ console.warn(
1289
+ `[adapter-agrenting] Task failed on attempt ${attempt + 1}, retrying in ${delayMs}ms: ${lastResult.error}`
1290
+ );
1291
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
1292
+ continue;
1293
+ }
1294
+ return lastResult;
1295
+ } catch (err) {
1296
+ lastError = err instanceof Error ? err : new Error(String(err));
1297
+ if (attempt < maxRetries) {
1298
+ const delayMs = Math.min(
1299
+ TASK_RETRY_BASE_DELAY_MS * Math.pow(2, attempt),
1300
+ TASK_RETRY_MAX_DELAY_MS
1301
+ );
1302
+ console.warn(
1303
+ `[adapter-agrenting] Execution error on attempt ${attempt + 1}, retrying in ${delayMs}ms: ${lastError.message}`
1304
+ );
1305
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
1306
+ continue;
1307
+ }
1308
+ }
1309
+ }
1310
+ return {
1311
+ success: false,
1312
+ error: lastError?.message ?? lastResult?.error ?? "Task failed after all retries",
1313
+ taskId: lastResult?.taskId,
1314
+ durationMs: lastResult?.durationMs ?? 0
1315
+ };
1316
+ }
1317
+ async function forwardCommentToAgrenting2(config, taskId, comment, authorName) {
1318
+ const client = new AgrentingClient(config);
1319
+ const formattedComment = authorName ? `[${authorName}]: ${comment}` : comment;
1320
+ try {
1321
+ return await client.sendMessageToTask(taskId, { message: formattedComment });
1322
+ } catch (err) {
1323
+ console.error(
1324
+ `[adapter-agrenting] Failed to forward comment to task ${taskId}:`,
1325
+ err instanceof Error ? err.message : String(err)
1326
+ );
1327
+ return null;
1328
+ }
1329
+ }
1330
+ function processIncomingMessage2(message) {
1331
+ const senderName = message.sender_name ?? "Agent";
1332
+ return formatAgentResponse(senderName, message.content);
1333
+ }
1334
+ function createServerAdapter() {
1335
+ return {
1336
+ name: "agrenting",
1337
+ execute,
1338
+ testEnvironment,
1339
+ getConfigSchema,
1340
+ startWebhookListener,
1341
+ stopWebhookListener,
1342
+ registerWebhook,
1343
+ deregisterWebhook,
1344
+ getTaskProgress,
1345
+ getTaskPayment,
1346
+ cancelTask,
1347
+ discoverAgents,
1348
+ getAgentProfile,
1349
+ hireAgent,
1350
+ sendMessageToTask,
1351
+ getTaskMessages,
1352
+ reassignTask,
1353
+ listCapabilities,
1354
+ sendMessageToHiring,
1355
+ getHiringMessages,
1356
+ retryHiring,
1357
+ getHiring,
1358
+ listHirings,
1359
+ autoSelectAgent,
1360
+ executeWithRetry,
1361
+ forwardCommentToAgrenting: forwardCommentToAgrenting2,
1362
+ processIncomingMessage: processIncomingMessage2,
1363
+ getBalance,
1364
+ getTransactions,
1365
+ deposit,
1366
+ withdraw
1367
+ };
1368
+ }
1369
+ // Annotate the CommonJS export names for ESM import in node:
1370
+ 0 && (module.exports = {
1371
+ AgrentingClient,
1372
+ MAX_POLLS,
1373
+ POLL_INTERVALS_MS,
1374
+ autoSelectAgent,
1375
+ canSubmitTask,
1376
+ checkBalance,
1377
+ createServerAdapter,
1378
+ createWebhookHandler,
1379
+ deregisterWebhook,
1380
+ executeWithRetry,
1381
+ formatAgentResponse,
1382
+ formatInsufficientBalanceComment,
1383
+ formatLowBalanceComment,
1384
+ forwardCommentToAgrenting,
1385
+ getActiveTaskMappings,
1386
+ getAgentProfile,
1387
+ getBackoffMs,
1388
+ getHiring,
1389
+ getHiringMessages,
1390
+ getTaskMessages,
1391
+ getWebhookGracePeriodMs,
1392
+ hireAgent,
1393
+ listCapabilities,
1394
+ listHirings,
1395
+ pollTaskUntilDone,
1396
+ processIncomingMessage,
1397
+ reassignTask,
1398
+ registerTaskMapping,
1399
+ registerWebhook,
1400
+ retryHiring,
1401
+ sendMessageToHiring,
1402
+ sendMessageToTask,
1403
+ unregisterTaskMapping,
1404
+ verifyWebhookSignature
1405
+ });
1406
+ //# sourceMappingURL=index.cjs.map