@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.
- package/README.md +306 -0
- package/dist/server/index.cjs +1406 -0
- package/dist/server/index.cjs.map +1 -0
- package/dist/server/index.d.cts +895 -0
- package/dist/server/index.d.ts +895 -0
- package/dist/server/index.js +1336 -0
- package/dist/server/index.js.map +1 -0
- package/dist/ui/index.cjs +125 -0
- package/dist/ui/index.cjs.map +1 -0
- package/dist/ui/index.d.cts +36 -0
- package/dist/ui/index.d.ts +36 -0
- package/dist/ui/index.js +98 -0
- package/dist/ui/index.js.map +1 -0
- package/package.json +65 -0
- package/server/src/adapter.test.ts +497 -0
- package/server/src/adapter.ts +1044 -0
- package/server/src/balance-monitor.test.ts +147 -0
- package/server/src/balance-monitor.ts +118 -0
- package/server/src/client.test.ts +949 -0
- package/server/src/client.ts +550 -0
- package/server/src/comment-sync.test.ts +25 -0
- package/server/src/comment-sync.ts +71 -0
- package/server/src/crypto.test.ts +62 -0
- package/server/src/crypto.ts +25 -0
- package/server/src/index.ts +67 -0
- package/server/src/polling.test.ts +208 -0
- package/server/src/polling.ts +183 -0
- package/server/src/types.ts +244 -0
- package/server/src/webhook-handler.test.ts +379 -0
- package/server/src/webhook-handler.ts +292 -0
- package/ui/src/adapter.ts +131 -0
- package/ui/src/index.ts +2 -0
|
@@ -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
|