@agrentingai/paperclip-adapter 0.2.2 → 0.4.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/CHANGELOG.md +86 -0
- package/LICENSE +21 -0
- package/README.md +158 -243
- package/VERSION +1 -0
- package/dist/server/index.cjs +698 -32
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +217 -32
- package/dist/server/index.d.ts +217 -32
- package/dist/server/index.js +677 -32
- package/dist/server/index.js.map +1 -1
- package/package.json +25 -7
- package/server/src/adapter.test.ts +0 -497
- package/server/src/adapter.ts +0 -1044
- package/server/src/balance-monitor.test.ts +0 -147
- package/server/src/balance-monitor.ts +0 -118
- package/server/src/client.test.ts +0 -949
- package/server/src/client.ts +0 -550
- package/server/src/comment-sync.test.ts +0 -25
- package/server/src/comment-sync.ts +0 -71
- package/server/src/crypto.test.ts +0 -62
- package/server/src/crypto.ts +0 -25
- package/server/src/index.ts +0 -67
- package/server/src/polling.test.ts +0 -208
- package/server/src/polling.ts +0 -183
- package/server/src/types.ts +0 -244
- package/server/src/webhook-handler.test.ts +0 -379
- package/server/src/webhook-handler.ts +0 -292
- package/ui/src/adapter.ts +0 -131
- package/ui/src/index.ts +0 -2
package/dist/server/index.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
// server/src/client.ts
|
|
2
2
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
3
3
|
var MAX_RETRIES = 3;
|
|
4
|
+
var NonRetryableAgrentingError = class extends Error {
|
|
5
|
+
constructor(message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "NonRetryableAgrentingError";
|
|
8
|
+
}
|
|
9
|
+
};
|
|
4
10
|
var AgrentingClient = class {
|
|
5
11
|
baseUrl;
|
|
6
12
|
apiKey;
|
|
@@ -47,17 +53,22 @@ var AgrentingClient = class {
|
|
|
47
53
|
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
48
54
|
continue;
|
|
49
55
|
}
|
|
50
|
-
throw new
|
|
56
|
+
throw new NonRetryableAgrentingError(
|
|
51
57
|
`Agrenting API ${response.status}: ${text.slice(0, 500)}`
|
|
52
58
|
);
|
|
53
59
|
}
|
|
54
60
|
const envelope = await response.json();
|
|
55
61
|
if (Array.isArray(envelope.errors) && envelope.errors.length) {
|
|
56
|
-
throw new
|
|
62
|
+
throw new NonRetryableAgrentingError(
|
|
63
|
+
`API errors: ${envelope.errors.join(", ")}`
|
|
64
|
+
);
|
|
57
65
|
}
|
|
58
66
|
return envelope.data ?? envelope;
|
|
59
67
|
} catch (err) {
|
|
60
68
|
lastError = err instanceof Error ? err : new Error(String(err));
|
|
69
|
+
if (lastError instanceof NonRetryableAgrentingError) {
|
|
70
|
+
throw lastError;
|
|
71
|
+
}
|
|
61
72
|
if (attempt < MAX_RETRIES) {
|
|
62
73
|
clearTimeout(timer);
|
|
63
74
|
const delayMs = Math.min(1e3 * 2 ** attempt, 3e4);
|
|
@@ -243,12 +254,23 @@ var AgrentingClient = class {
|
|
|
243
254
|
async getAgentProfile(agentDid) {
|
|
244
255
|
return this.request("GET", `/api/v1/agents/${encodeURIComponent(agentDid)}`);
|
|
245
256
|
}
|
|
246
|
-
/**
|
|
247
|
-
*
|
|
257
|
+
/** Create a paid hiring for an agent.
|
|
258
|
+
* Agrenting requires a concrete task, capability, and offered price.
|
|
248
259
|
*/
|
|
249
|
-
async hireAgent(agentDid, options
|
|
250
|
-
const body = {
|
|
251
|
-
|
|
260
|
+
async hireAgent(agentDid, options) {
|
|
261
|
+
const body = {
|
|
262
|
+
task_description: options.taskDescription,
|
|
263
|
+
capability_requested: options.capabilityRequested,
|
|
264
|
+
price: String(options.price),
|
|
265
|
+
delivery_mode: options.deliveryMode ?? "output"
|
|
266
|
+
};
|
|
267
|
+
if (options.repoUrl) body.repo_url = options.repoUrl;
|
|
268
|
+
if (options.repoAccessToken) body.repo_access_token = options.repoAccessToken;
|
|
269
|
+
if (options.clientIdempotencyKey) {
|
|
270
|
+
body.client_idempotency_key = options.clientIdempotencyKey;
|
|
271
|
+
}
|
|
272
|
+
if (options.taskInput) body.task_input = options.taskInput;
|
|
273
|
+
if (options.clientMessage) body.client_message = options.clientMessage;
|
|
252
274
|
return this.request(
|
|
253
275
|
"POST",
|
|
254
276
|
`/api/v1/agents/${encodeURIComponent(agentDid)}/hire`,
|
|
@@ -307,13 +329,11 @@ var AgrentingClient = class {
|
|
|
307
329
|
);
|
|
308
330
|
}
|
|
309
331
|
/** Get messages for a hiring.
|
|
310
|
-
* GET /api/v1/hirings/:id
|
|
332
|
+
* The canonical API includes recent messages in GET /api/v1/hirings/:id.
|
|
311
333
|
*/
|
|
312
334
|
async getHiringMessages(hiringId) {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
`/api/v1/hirings/${hiringId}/messages`
|
|
316
|
-
);
|
|
335
|
+
const hiring = await this.getHiring(hiringId);
|
|
336
|
+
return hiring.messages ?? [];
|
|
317
337
|
}
|
|
318
338
|
/** Retry a failed hiring.
|
|
319
339
|
* POST /api/v1/hirings/:id/retry
|
|
@@ -335,6 +355,15 @@ var AgrentingClient = class {
|
|
|
335
355
|
async getHiring(hiringId) {
|
|
336
356
|
return this.request("GET", `/api/v1/hirings/${hiringId}`);
|
|
337
357
|
}
|
|
358
|
+
/** Cancel an active hiring.
|
|
359
|
+
* POST /api/v1/hirings/:id/cancel
|
|
360
|
+
*/
|
|
361
|
+
async cancelHiring(hiringId) {
|
|
362
|
+
return this.request(
|
|
363
|
+
"POST",
|
|
364
|
+
`/api/v1/hirings/${hiringId}/cancel`
|
|
365
|
+
);
|
|
366
|
+
}
|
|
338
367
|
/** List hirings for the authenticated agent.
|
|
339
368
|
* GET /api/v1/hirings
|
|
340
369
|
*/
|
|
@@ -342,9 +371,16 @@ var AgrentingClient = class {
|
|
|
342
371
|
const params = new URLSearchParams();
|
|
343
372
|
if (options.status) params.set("status", options.status);
|
|
344
373
|
if (options.limit) params.set("limit", String(options.limit));
|
|
345
|
-
if (options.offset)
|
|
374
|
+
if (options.offset) {
|
|
375
|
+
const perPage = options.limit ?? 20;
|
|
376
|
+
params.set("page", String(Math.floor(options.offset / perPage) + 1));
|
|
377
|
+
}
|
|
346
378
|
const query = params.toString() ? `?${params}` : "";
|
|
347
|
-
|
|
379
|
+
const result = await this.request(
|
|
380
|
+
"GET",
|
|
381
|
+
`/api/v1/hirings${query}`
|
|
382
|
+
);
|
|
383
|
+
return result.hirings;
|
|
348
384
|
}
|
|
349
385
|
/** List agents filtered by capability for auto-select.
|
|
350
386
|
* GET /api/v1/agents?capability=X
|
|
@@ -357,6 +393,493 @@ var AgrentingClient = class {
|
|
|
357
393
|
}
|
|
358
394
|
};
|
|
359
395
|
|
|
396
|
+
// server/src/paperclip.ts
|
|
397
|
+
var type = "agrenting";
|
|
398
|
+
var label = "Agrenting";
|
|
399
|
+
var agentConfigurationDoc = `# agrenting agent configuration
|
|
400
|
+
|
|
401
|
+
Adapter: agrenting
|
|
402
|
+
|
|
403
|
+
Use when:
|
|
404
|
+
- A Paperclip agent should delegate each heartbeat to a remote agent hired from Agrenting.
|
|
405
|
+
- The operator has an Agrenting user API token and sufficient ledger balance.
|
|
406
|
+
|
|
407
|
+
Core fields:
|
|
408
|
+
- agrentingUrl (required): Agrenting base URL, normally https://agrenting.com
|
|
409
|
+
- apiKey (required, secret): Agrenting user API token (ap_...)
|
|
410
|
+
- agentDid (required): DID of the marketplace agent to hire
|
|
411
|
+
- capabilityRequested (optional): defaults to the first capability on the agent profile
|
|
412
|
+
- price (optional): defaults to the agent's current base price
|
|
413
|
+
- timeoutSec (optional): maximum time to poll a hiring, default 600
|
|
414
|
+
- pollIntervalMs (optional): hiring status poll interval, default 2000
|
|
415
|
+
- deliveryMode (optional): output by default; push requires repository access
|
|
416
|
+
- repoUrl (optional): repository URL used only when deliveryMode is push
|
|
417
|
+
|
|
418
|
+
Recommended API-key scopes:
|
|
419
|
+
- agents:discover, agents:read, hire:create, hirings:read, hirings:cancel
|
|
420
|
+
- add balance:read and artifacts:read when sharing the key with Apps/Claude
|
|
421
|
+
- deposits:create and account:read/account:write are optional elevated scopes
|
|
422
|
+
- set max_price_per_hire on the key to cap paid actions
|
|
423
|
+
|
|
424
|
+
Execution:
|
|
425
|
+
- Each Paperclip run creates one canonical Agrenting hiring.
|
|
426
|
+
- The Paperclip run id is sent as client_idempotency_key so safe retries deduplicate.
|
|
427
|
+
- Paperclip polls the hiring until it completes, fails, is cancelled, or times out.
|
|
428
|
+
- Push delivery can use a repository URL from Paperclip and an Agrenting-stored
|
|
429
|
+
GitHub token. The adapter never stores a repository token in agent config.
|
|
430
|
+
`;
|
|
431
|
+
var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
|
|
432
|
+
"completed",
|
|
433
|
+
"failed",
|
|
434
|
+
"cancelled",
|
|
435
|
+
"disputed",
|
|
436
|
+
"refunded"
|
|
437
|
+
]);
|
|
438
|
+
var DEFAULT_TIMEOUT_SEC = 600;
|
|
439
|
+
var DEFAULT_POLL_INTERVAL_MS = 2e3;
|
|
440
|
+
var MAX_TASK_DESCRIPTION_LENGTH = 5e3;
|
|
441
|
+
function asRecord(value) {
|
|
442
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
443
|
+
return null;
|
|
444
|
+
}
|
|
445
|
+
return value;
|
|
446
|
+
}
|
|
447
|
+
function nonEmpty(value) {
|
|
448
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
449
|
+
}
|
|
450
|
+
function positiveNumber(value, fallback) {
|
|
451
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
452
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
453
|
+
}
|
|
454
|
+
function configFrom(raw) {
|
|
455
|
+
return {
|
|
456
|
+
agrentingUrl: nonEmpty(raw.agrentingUrl) ?? "https://agrenting.com",
|
|
457
|
+
apiKey: nonEmpty(raw.apiKey) ?? "",
|
|
458
|
+
agentDid: nonEmpty(raw.agentDid) ?? "",
|
|
459
|
+
capabilityRequested: nonEmpty(raw.capabilityRequested) ?? void 0,
|
|
460
|
+
price: nonEmpty(raw.price) ?? void 0,
|
|
461
|
+
timeoutSec: positiveNumber(raw.timeoutSec, DEFAULT_TIMEOUT_SEC),
|
|
462
|
+
pollIntervalMs: positiveNumber(
|
|
463
|
+
raw.pollIntervalMs,
|
|
464
|
+
DEFAULT_POLL_INTERVAL_MS
|
|
465
|
+
),
|
|
466
|
+
deliveryMode: raw.deliveryMode === "push" ? "push" : "output",
|
|
467
|
+
repoUrl: nonEmpty(raw.repoUrl) ?? void 0
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
function taskDescriptionFrom(ctx) {
|
|
471
|
+
const context = ctx.context;
|
|
472
|
+
const title = nonEmpty(context.taskTitle) ?? nonEmpty(context.issueTitle) ?? nonEmpty(context.title);
|
|
473
|
+
const body = nonEmpty(context.paperclipTaskMarkdown) ?? nonEmpty(context.taskBody) ?? nonEmpty(context.taskDescription) ?? nonEmpty(context.issueDescription) ?? nonEmpty(context.prompt) ?? nonEmpty(context.input);
|
|
474
|
+
let description = [title, body].filter(Boolean).join("\n\n").trim();
|
|
475
|
+
if (!description) {
|
|
476
|
+
description = `Continue the assigned Paperclip work for ${ctx.agent.name}.`;
|
|
477
|
+
}
|
|
478
|
+
if (description.length <= MAX_TASK_DESCRIPTION_LENGTH) return description;
|
|
479
|
+
return `${description.slice(0, MAX_TASK_DESCRIPTION_LENGTH - 32)}
|
|
480
|
+
|
|
481
|
+
[truncated by Paperclip adapter]`;
|
|
482
|
+
}
|
|
483
|
+
function capabilityFrom(config, context, profile) {
|
|
484
|
+
return nonEmpty(config.capabilityRequested) ?? nonEmpty(context.capabilityRequested) ?? nonEmpty(context.capability) ?? profile.capabilities.find((capability) => capability.trim().length > 0) ?? null;
|
|
485
|
+
}
|
|
486
|
+
function priceFrom(config, context, profile) {
|
|
487
|
+
return nonEmpty(config.price) ?? nonEmpty(context.price) ?? nonEmpty(context.maxPrice) ?? nonEmpty(profile.base_price);
|
|
488
|
+
}
|
|
489
|
+
function repoUrlFrom(ctx) {
|
|
490
|
+
const workspace = asRecord(ctx.context.paperclipWorkspace);
|
|
491
|
+
return nonEmpty(configFrom(ctx.config).repoUrl) ?? nonEmpty(workspace?.repoUrl) ?? void 0;
|
|
492
|
+
}
|
|
493
|
+
function taskInputFrom(ctx) {
|
|
494
|
+
const input = {
|
|
495
|
+
paperclip_run_id: ctx.runId,
|
|
496
|
+
paperclip_agent_id: ctx.agent.id,
|
|
497
|
+
paperclip_company_id: ctx.agent.companyId
|
|
498
|
+
};
|
|
499
|
+
const mappings = [
|
|
500
|
+
["paperclip_issue_id", ctx.context.issueId ?? ctx.context.taskId],
|
|
501
|
+
["paperclip_project_id", ctx.context.projectId],
|
|
502
|
+
["paperclip_wake_reason", ctx.context.wakeReason]
|
|
503
|
+
];
|
|
504
|
+
for (const [key, value] of mappings) {
|
|
505
|
+
const normalized = nonEmpty(value);
|
|
506
|
+
if (normalized) input[key] = normalized;
|
|
507
|
+
}
|
|
508
|
+
const wake = asRecord(ctx.context.paperclipWake);
|
|
509
|
+
if (wake) input.paperclip_wake = wake;
|
|
510
|
+
return input;
|
|
511
|
+
}
|
|
512
|
+
function outputText(hiring) {
|
|
513
|
+
const output = hiring.task_output;
|
|
514
|
+
if (typeof output === "string" && output.trim()) return output;
|
|
515
|
+
const record = asRecord(output);
|
|
516
|
+
if (record) {
|
|
517
|
+
for (const key of ["result", "output", "text", "summary"]) {
|
|
518
|
+
const direct = nonEmpty(record[key]);
|
|
519
|
+
if (direct) return direct;
|
|
520
|
+
}
|
|
521
|
+
return JSON.stringify(record, null, 2);
|
|
522
|
+
}
|
|
523
|
+
return `Agrenting hiring ${hiring.id} completed.`;
|
|
524
|
+
}
|
|
525
|
+
function firstLine(value) {
|
|
526
|
+
return value.split(/\r?\n/).map((line) => line.trim()).find(Boolean) ?? "Agrenting hiring completed";
|
|
527
|
+
}
|
|
528
|
+
function sleep(ms) {
|
|
529
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
530
|
+
}
|
|
531
|
+
function resultForFailure(hiring, message) {
|
|
532
|
+
return {
|
|
533
|
+
exitCode: 1,
|
|
534
|
+
signal: null,
|
|
535
|
+
timedOut: false,
|
|
536
|
+
errorMessage: message,
|
|
537
|
+
errorCode: `agrenting_hiring_${hiring.status}`,
|
|
538
|
+
provider: "agrenting",
|
|
539
|
+
biller: "agrenting",
|
|
540
|
+
sessionParams: { hiringId: hiring.id },
|
|
541
|
+
sessionDisplayId: hiring.id,
|
|
542
|
+
resultJson: {
|
|
543
|
+
hiringId: hiring.id,
|
|
544
|
+
status: hiring.status,
|
|
545
|
+
failedReason: hiring.failed_reason ?? null
|
|
546
|
+
}
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
async function executePaperclip(ctx) {
|
|
550
|
+
const config = configFrom(ctx.config);
|
|
551
|
+
if (!config.apiKey || !config.agentDid) {
|
|
552
|
+
return {
|
|
553
|
+
exitCode: 1,
|
|
554
|
+
signal: null,
|
|
555
|
+
timedOut: false,
|
|
556
|
+
errorCode: "agrenting_config_invalid",
|
|
557
|
+
errorMessage: "Agrenting requires apiKey and agentDid."
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
const client = new AgrentingClient(config);
|
|
561
|
+
try {
|
|
562
|
+
const profile = await client.getAgentProfile(config.agentDid);
|
|
563
|
+
const capability = capabilityFrom(config, ctx.context, profile);
|
|
564
|
+
const price = priceFrom(config, ctx.context, profile);
|
|
565
|
+
if (!capability) {
|
|
566
|
+
throw new Error(
|
|
567
|
+
"No capabilityRequested was configured and the Agrenting agent profile has no capabilities."
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
if (!price) {
|
|
571
|
+
throw new Error(
|
|
572
|
+
"No price was configured and the Agrenting agent profile has no base_price."
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
const taskDescription = taskDescriptionFrom(ctx);
|
|
576
|
+
await ctx.onLog(
|
|
577
|
+
"stdout",
|
|
578
|
+
`[agrenting] Hiring ${config.agentDid} for ${capability} at ${price}
|
|
579
|
+
`
|
|
580
|
+
);
|
|
581
|
+
const created = await client.hireAgent(config.agentDid, {
|
|
582
|
+
taskDescription,
|
|
583
|
+
capabilityRequested: capability,
|
|
584
|
+
price,
|
|
585
|
+
deliveryMode: config.deliveryMode,
|
|
586
|
+
clientIdempotencyKey: ctx.runId,
|
|
587
|
+
taskInput: taskInputFrom(ctx),
|
|
588
|
+
repoUrl: repoUrlFrom(ctx)
|
|
589
|
+
});
|
|
590
|
+
const hiringId = created.hiring.id;
|
|
591
|
+
await ctx.onMeta?.({
|
|
592
|
+
adapterType: type,
|
|
593
|
+
command: "POST /api/v1/agents/:did/hire",
|
|
594
|
+
context: { hiringId, agentDid: config.agentDid, capability, price }
|
|
595
|
+
});
|
|
596
|
+
await ctx.onLog(
|
|
597
|
+
"stdout",
|
|
598
|
+
`[agrenting] Hiring ${hiringId} created with status ${created.hiring.status}
|
|
599
|
+
`
|
|
600
|
+
);
|
|
601
|
+
const timeoutMs = (config.timeoutSec ?? DEFAULT_TIMEOUT_SEC) * 1e3;
|
|
602
|
+
const pollIntervalMs = config.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
603
|
+
const deadline = Date.now() + timeoutMs;
|
|
604
|
+
let hiring = created.hiring;
|
|
605
|
+
let lastStatus = hiring.status;
|
|
606
|
+
while (!TERMINAL_STATUSES.has(hiring.status) && Date.now() < deadline) {
|
|
607
|
+
await sleep(Math.min(pollIntervalMs, Math.max(1, deadline - Date.now())));
|
|
608
|
+
hiring = await client.getHiring(hiringId);
|
|
609
|
+
if (hiring.status !== lastStatus) {
|
|
610
|
+
lastStatus = hiring.status;
|
|
611
|
+
await ctx.onLog(
|
|
612
|
+
"stdout",
|
|
613
|
+
`[agrenting] Hiring ${hiringId} is ${hiring.status}
|
|
614
|
+
`
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
if (!TERMINAL_STATUSES.has(hiring.status)) {
|
|
619
|
+
try {
|
|
620
|
+
await client.cancelHiring(hiringId);
|
|
621
|
+
await ctx.onLog(
|
|
622
|
+
"stderr",
|
|
623
|
+
`[agrenting] Hiring ${hiringId} timed out and was cancelled
|
|
624
|
+
`
|
|
625
|
+
);
|
|
626
|
+
} catch (cancelError) {
|
|
627
|
+
await ctx.onLog(
|
|
628
|
+
"stderr",
|
|
629
|
+
`[agrenting] Hiring ${hiringId} timed out; cancellation failed: ${cancelError instanceof Error ? cancelError.message : String(cancelError)}
|
|
630
|
+
`
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
return {
|
|
634
|
+
exitCode: null,
|
|
635
|
+
signal: null,
|
|
636
|
+
timedOut: true,
|
|
637
|
+
errorCode: "agrenting_hiring_timeout",
|
|
638
|
+
errorMessage: `Agrenting hiring ${hiringId} timed out after ${config.timeoutSec ?? DEFAULT_TIMEOUT_SEC}s.`,
|
|
639
|
+
provider: "agrenting",
|
|
640
|
+
biller: "agrenting",
|
|
641
|
+
sessionParams: { hiringId },
|
|
642
|
+
sessionDisplayId: hiringId
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
if (hiring.status !== "completed") {
|
|
646
|
+
return resultForFailure(
|
|
647
|
+
hiring,
|
|
648
|
+
hiring.failed_reason ?? `Agrenting hiring ended with status ${hiring.status}.`
|
|
649
|
+
);
|
|
650
|
+
}
|
|
651
|
+
const output = outputText(hiring);
|
|
652
|
+
await ctx.onLog("stdout", `${output}
|
|
653
|
+
`);
|
|
654
|
+
const numericPrice = Number(hiring.price ?? price);
|
|
655
|
+
return {
|
|
656
|
+
exitCode: 0,
|
|
657
|
+
signal: null,
|
|
658
|
+
timedOut: false,
|
|
659
|
+
provider: "agrenting",
|
|
660
|
+
biller: "agrenting",
|
|
661
|
+
billingType: "fixed",
|
|
662
|
+
costUsd: Number.isFinite(numericPrice) ? numericPrice : null,
|
|
663
|
+
sessionParams: { hiringId },
|
|
664
|
+
sessionDisplayId: hiringId,
|
|
665
|
+
summary: firstLine(output),
|
|
666
|
+
resultJson: {
|
|
667
|
+
hiringId,
|
|
668
|
+
status: hiring.status,
|
|
669
|
+
taskOutput: hiring.task_output ?? null
|
|
670
|
+
}
|
|
671
|
+
};
|
|
672
|
+
} catch (error) {
|
|
673
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
674
|
+
const transient = /\b(429|5\d\d)\b|rate.?limit|temporar|timeout/i.test(message);
|
|
675
|
+
await ctx.onLog("stderr", `[agrenting] ${message}
|
|
676
|
+
`);
|
|
677
|
+
return {
|
|
678
|
+
exitCode: 1,
|
|
679
|
+
signal: null,
|
|
680
|
+
timedOut: false,
|
|
681
|
+
errorCode: "agrenting_hiring_failed",
|
|
682
|
+
errorFamily: transient ? "transient_upstream" : null,
|
|
683
|
+
errorMessage: message,
|
|
684
|
+
provider: "agrenting",
|
|
685
|
+
biller: "agrenting"
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
function summarizeChecks(checks) {
|
|
690
|
+
if (checks.some((check) => check.level === "error")) return "fail";
|
|
691
|
+
if (checks.some((check) => check.level === "warn")) return "warn";
|
|
692
|
+
return "pass";
|
|
693
|
+
}
|
|
694
|
+
async function testPaperclipEnvironment(ctx) {
|
|
695
|
+
const config = configFrom(ctx.config);
|
|
696
|
+
const checks = [];
|
|
697
|
+
let parsedUrl = null;
|
|
698
|
+
try {
|
|
699
|
+
parsedUrl = new URL(config.agrentingUrl);
|
|
700
|
+
if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
|
|
701
|
+
parsedUrl = null;
|
|
702
|
+
}
|
|
703
|
+
} catch {
|
|
704
|
+
parsedUrl = null;
|
|
705
|
+
}
|
|
706
|
+
if (!parsedUrl) {
|
|
707
|
+
checks.push({
|
|
708
|
+
code: "agrenting_url_invalid",
|
|
709
|
+
level: "error",
|
|
710
|
+
message: "agrentingUrl must be an http:// or https:// URL."
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
if (!config.apiKey) {
|
|
714
|
+
checks.push({
|
|
715
|
+
code: "agrenting_api_key_missing",
|
|
716
|
+
level: "error",
|
|
717
|
+
message: "Agrenting requires a user API token.",
|
|
718
|
+
hint: "Create an ap_... token in Agrenting and store it as a Paperclip secret."
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
if (!config.agentDid) {
|
|
722
|
+
checks.push({
|
|
723
|
+
code: "agrenting_agent_did_missing",
|
|
724
|
+
level: "error",
|
|
725
|
+
message: "Agrenting requires agentDid."
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
if (!checks.some((check) => check.level === "error")) {
|
|
729
|
+
const client = new AgrentingClient(config);
|
|
730
|
+
let connectionOk = false;
|
|
731
|
+
try {
|
|
732
|
+
await client.listHirings({ limit: 1 });
|
|
733
|
+
connectionOk = true;
|
|
734
|
+
checks.push({
|
|
735
|
+
code: "agrenting_connection_ok",
|
|
736
|
+
level: "info",
|
|
737
|
+
message: "Connected to the authenticated Agrenting hiring API."
|
|
738
|
+
});
|
|
739
|
+
} catch (error) {
|
|
740
|
+
checks.push({
|
|
741
|
+
code: "agrenting_connection_failed",
|
|
742
|
+
level: "error",
|
|
743
|
+
message: "Could not access the authenticated Agrenting hiring API.",
|
|
744
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
if (connectionOk) {
|
|
748
|
+
try {
|
|
749
|
+
const profile = await client.getAgentProfile(config.agentDid);
|
|
750
|
+
checks.push({
|
|
751
|
+
code: "agrenting_agent_profile_ok",
|
|
752
|
+
level: "info",
|
|
753
|
+
message: `Found Agrenting agent ${profile.name} (${profile.did}).`,
|
|
754
|
+
detail: `${profile.capabilities.length} capabilities; base price ${profile.base_price ?? "not reported"}.`
|
|
755
|
+
});
|
|
756
|
+
} catch (error) {
|
|
757
|
+
checks.push({
|
|
758
|
+
code: "agrenting_agent_profile_failed",
|
|
759
|
+
level: "error",
|
|
760
|
+
message: `Could not load Agrenting agent ${config.agentDid}.`,
|
|
761
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
return {
|
|
767
|
+
adapterType: ctx.adapterType,
|
|
768
|
+
status: summarizeChecks(checks),
|
|
769
|
+
checks,
|
|
770
|
+
testedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
function getPaperclipConfigSchema() {
|
|
774
|
+
return {
|
|
775
|
+
fields: [
|
|
776
|
+
{
|
|
777
|
+
key: "agrentingUrl",
|
|
778
|
+
label: "Agrenting URL",
|
|
779
|
+
type: "text",
|
|
780
|
+
required: true,
|
|
781
|
+
default: "https://agrenting.com",
|
|
782
|
+
hint: "Base URL of the Agrenting platform."
|
|
783
|
+
},
|
|
784
|
+
{
|
|
785
|
+
key: "apiKey",
|
|
786
|
+
label: "API token",
|
|
787
|
+
type: "text",
|
|
788
|
+
required: true,
|
|
789
|
+
hint: "Agrenting user API token (ap_...). Stored as a Paperclip secret.",
|
|
790
|
+
meta: { secret: true }
|
|
791
|
+
},
|
|
792
|
+
{
|
|
793
|
+
key: "agentDid",
|
|
794
|
+
label: "Agent DID",
|
|
795
|
+
type: "text",
|
|
796
|
+
required: true,
|
|
797
|
+
hint: "Marketplace agent DID, for example did:agrenting:code-reviewer."
|
|
798
|
+
},
|
|
799
|
+
{
|
|
800
|
+
key: "capabilityRequested",
|
|
801
|
+
label: "Capability",
|
|
802
|
+
type: "text",
|
|
803
|
+
hint: "Optional default. Falls back to the first capability on the agent profile."
|
|
804
|
+
},
|
|
805
|
+
{
|
|
806
|
+
key: "price",
|
|
807
|
+
label: "Price per hiring (USD)",
|
|
808
|
+
type: "text",
|
|
809
|
+
hint: "Optional. Falls back to the agent's current base price."
|
|
810
|
+
},
|
|
811
|
+
{
|
|
812
|
+
key: "timeoutSec",
|
|
813
|
+
label: "Timeout seconds",
|
|
814
|
+
type: "number",
|
|
815
|
+
default: DEFAULT_TIMEOUT_SEC
|
|
816
|
+
},
|
|
817
|
+
{
|
|
818
|
+
key: "pollIntervalMs",
|
|
819
|
+
label: "Poll interval milliseconds",
|
|
820
|
+
type: "number",
|
|
821
|
+
default: DEFAULT_POLL_INTERVAL_MS
|
|
822
|
+
},
|
|
823
|
+
{
|
|
824
|
+
key: "repoUrl",
|
|
825
|
+
label: "Repository URL",
|
|
826
|
+
type: "text",
|
|
827
|
+
hint: "Optional push target. Push delivery uses a GitHub token already stored in Agrenting."
|
|
828
|
+
},
|
|
829
|
+
{
|
|
830
|
+
key: "deliveryMode",
|
|
831
|
+
label: "Delivery mode",
|
|
832
|
+
type: "select",
|
|
833
|
+
default: "output",
|
|
834
|
+
options: [
|
|
835
|
+
{ value: "output", label: "Return output" },
|
|
836
|
+
{ value: "push", label: "Push to repository" }
|
|
837
|
+
],
|
|
838
|
+
hint: "Use output unless the hiring also has repository credentials."
|
|
839
|
+
}
|
|
840
|
+
]
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
var paperclipSessionCodec = {
|
|
844
|
+
deserialize(raw) {
|
|
845
|
+
if (typeof raw === "string") {
|
|
846
|
+
try {
|
|
847
|
+
return asRecord(JSON.parse(raw));
|
|
848
|
+
} catch {
|
|
849
|
+
return null;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
return asRecord(raw);
|
|
853
|
+
},
|
|
854
|
+
serialize(params) {
|
|
855
|
+
return params;
|
|
856
|
+
},
|
|
857
|
+
getDisplayId(params) {
|
|
858
|
+
return nonEmpty(params?.hiringId);
|
|
859
|
+
},
|
|
860
|
+
encode(state) {
|
|
861
|
+
return JSON.stringify(state ?? null);
|
|
862
|
+
},
|
|
863
|
+
decode(blob) {
|
|
864
|
+
if (!blob) return null;
|
|
865
|
+
try {
|
|
866
|
+
return JSON.parse(blob);
|
|
867
|
+
} catch {
|
|
868
|
+
return null;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
};
|
|
872
|
+
function createCanonicalServerAdapter() {
|
|
873
|
+
return {
|
|
874
|
+
type,
|
|
875
|
+
execute: executePaperclip,
|
|
876
|
+
testEnvironment: testPaperclipEnvironment,
|
|
877
|
+
sessionCodec: paperclipSessionCodec,
|
|
878
|
+
getConfigSchema: getPaperclipConfigSchema,
|
|
879
|
+
agentConfigurationDoc
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
|
|
360
883
|
// server/src/crypto.ts
|
|
361
884
|
async function verifyWebhookSignature(rawBody, signature, secret) {
|
|
362
885
|
const crypto = await import("crypto");
|
|
@@ -841,11 +1364,11 @@ async function startWebhookListener(config) {
|
|
|
841
1364
|
if (resolvedTaskId) {
|
|
842
1365
|
const pending = pendingTasks.get(resolvedTaskId);
|
|
843
1366
|
if (pending && !pending.settled) {
|
|
844
|
-
const
|
|
845
|
-
pending.status =
|
|
1367
|
+
const status2 = payload.status ?? pending.status;
|
|
1368
|
+
pending.status = status2;
|
|
846
1369
|
pending.progressPercent = payload.progress_percent ?? pending.progressPercent;
|
|
847
1370
|
pending.progressMessage = payload.progress_message ?? pending.progressMessage;
|
|
848
|
-
if (
|
|
1371
|
+
if (status2 === "completed") {
|
|
849
1372
|
pending.settled = true;
|
|
850
1373
|
pending.resolve({
|
|
851
1374
|
success: true,
|
|
@@ -853,7 +1376,7 @@ async function startWebhookListener(config) {
|
|
|
853
1376
|
taskId: resolvedTaskId,
|
|
854
1377
|
durationMs: Date.now() - pending.startedAt
|
|
855
1378
|
});
|
|
856
|
-
} else if (
|
|
1379
|
+
} else if (status2 === "failed") {
|
|
857
1380
|
pending.settled = true;
|
|
858
1381
|
pending.resolve({
|
|
859
1382
|
success: false,
|
|
@@ -861,7 +1384,7 @@ async function startWebhookListener(config) {
|
|
|
861
1384
|
taskId: resolvedTaskId,
|
|
862
1385
|
durationMs: Date.now() - pending.startedAt
|
|
863
1386
|
});
|
|
864
|
-
} else if (
|
|
1387
|
+
} else if (status2 === "cancelled") {
|
|
865
1388
|
pending.settled = true;
|
|
866
1389
|
pending.resolve({
|
|
867
1390
|
success: false,
|
|
@@ -1092,9 +1615,9 @@ async function cancelTask(config, taskId) {
|
|
|
1092
1615
|
var TASK_MAX_RETRIES = 2;
|
|
1093
1616
|
var TASK_RETRY_BASE_DELAY_MS = 1e3;
|
|
1094
1617
|
var TASK_RETRY_MAX_DELAY_MS = 3e4;
|
|
1095
|
-
async function hireAgent(config, agentDid) {
|
|
1618
|
+
async function hireAgent(config, agentDid, options) {
|
|
1096
1619
|
const client = new AgrentingClient(config);
|
|
1097
|
-
return client.hireAgent(agentDid);
|
|
1620
|
+
return client.hireAgent(agentDid, options);
|
|
1098
1621
|
}
|
|
1099
1622
|
async function getAgentProfile(config, agentDid) {
|
|
1100
1623
|
const client = new AgrentingClient(config);
|
|
@@ -1136,6 +1659,10 @@ async function listHirings(config, options) {
|
|
|
1136
1659
|
const client = new AgrentingClient(config);
|
|
1137
1660
|
return client.listHirings(options);
|
|
1138
1661
|
}
|
|
1662
|
+
async function cancelHiring(config, hiringId) {
|
|
1663
|
+
const client = new AgrentingClient(config);
|
|
1664
|
+
return client.cancelHiring(hiringId);
|
|
1665
|
+
}
|
|
1139
1666
|
async function autoSelectAgent(config, options) {
|
|
1140
1667
|
const client = new AgrentingClient(config);
|
|
1141
1668
|
const capabilities = await client.listCapabilities();
|
|
@@ -1161,7 +1688,8 @@ async function autoSelectAgent(config, options) {
|
|
|
1161
1688
|
const minRep = options.minReputation;
|
|
1162
1689
|
filtered = filtered.filter((a) => {
|
|
1163
1690
|
if (!a.reputation_score) return false;
|
|
1164
|
-
|
|
1691
|
+
const reputation = Number(a.reputation_score);
|
|
1692
|
+
return Number.isFinite(reputation) && reputation >= minRep;
|
|
1165
1693
|
});
|
|
1166
1694
|
}
|
|
1167
1695
|
if (filtered.length === 0) {
|
|
@@ -1172,15 +1700,17 @@ async function autoSelectAgent(config, options) {
|
|
|
1172
1700
|
const sortBy = options.sortBy ?? "reputation_score";
|
|
1173
1701
|
if (options.preferAvailable ?? true) {
|
|
1174
1702
|
filtered.sort((a, b) => {
|
|
1175
|
-
const aAvail = a.availability_status === "available" ? 0 : 1;
|
|
1176
|
-
const bAvail = b.availability_status === "available" ? 0 : 1;
|
|
1703
|
+
const aAvail = (a.availability_status ?? a.availability ?? a.status) === "available" ? 0 : 1;
|
|
1704
|
+
const bAvail = (b.availability_status ?? b.availability ?? b.status) === "available" ? 0 : 1;
|
|
1177
1705
|
if (aAvail !== bAvail) return aAvail - bAvail;
|
|
1178
1706
|
return 0;
|
|
1179
1707
|
});
|
|
1180
1708
|
}
|
|
1181
1709
|
filtered.sort((a, b) => {
|
|
1182
1710
|
if (sortBy === "reputation_score") {
|
|
1183
|
-
|
|
1711
|
+
const aReputation = Number(a.reputation_score ?? 0);
|
|
1712
|
+
const bReputation = Number(b.reputation_score ?? 0);
|
|
1713
|
+
return (Number.isFinite(bReputation) ? bReputation : 0) - (Number.isFinite(aReputation) ? aReputation : 0);
|
|
1184
1714
|
}
|
|
1185
1715
|
if (sortBy === "base_price") {
|
|
1186
1716
|
const aPrice = parseFloat(a.base_price ?? "999999");
|
|
@@ -1188,14 +1718,19 @@ async function autoSelectAgent(config, options) {
|
|
|
1188
1718
|
return aPrice - bPrice;
|
|
1189
1719
|
}
|
|
1190
1720
|
if (sortBy === "availability") {
|
|
1191
|
-
const aAvail = a.availability_status === "available" ? 0 : 1;
|
|
1192
|
-
const bAvail = b.availability_status === "available" ? 0 : 1;
|
|
1721
|
+
const aAvail = (a.availability_status ?? a.availability ?? a.status) === "available" ? 0 : 1;
|
|
1722
|
+
const bAvail = (b.availability_status ?? b.availability ?? b.status) === "available" ? 0 : 1;
|
|
1193
1723
|
return aAvail - bAvail;
|
|
1194
1724
|
}
|
|
1195
1725
|
return 0;
|
|
1196
1726
|
});
|
|
1197
1727
|
const selectedAgent = filtered[0];
|
|
1198
|
-
const hireResult = await client.hireAgent(selectedAgent.did
|
|
1728
|
+
const hireResult = await client.hireAgent(selectedAgent.did, {
|
|
1729
|
+
taskDescription: options.taskDescription,
|
|
1730
|
+
capabilityRequested: options.capability,
|
|
1731
|
+
price: selectedAgent.base_price ?? options.maxPrice ?? "0",
|
|
1732
|
+
deliveryMode: "output"
|
|
1733
|
+
});
|
|
1199
1734
|
return {
|
|
1200
1735
|
...hireResult,
|
|
1201
1736
|
selectedAgent
|
|
@@ -1262,12 +1797,63 @@ function processIncomingMessage2(message) {
|
|
|
1262
1797
|
const senderName = message.sender_name ?? "Agent";
|
|
1263
1798
|
return formatAgentResponse(senderName, message.content);
|
|
1264
1799
|
}
|
|
1800
|
+
async function detectModel(config) {
|
|
1801
|
+
if (!config.agentDid) return null;
|
|
1802
|
+
try {
|
|
1803
|
+
const profile = await getAgentProfile(config, config.agentDid);
|
|
1804
|
+
const provider = profile.ai_provider ?? null;
|
|
1805
|
+
const model = profile.ai_model ?? null;
|
|
1806
|
+
if (provider && model) return { provider, model };
|
|
1807
|
+
} catch {
|
|
1808
|
+
}
|
|
1809
|
+
return null;
|
|
1810
|
+
}
|
|
1811
|
+
async function listSkills(config) {
|
|
1812
|
+
const profile = await getAgentProfile(config, config.agentDid);
|
|
1813
|
+
const capabilities = profile.capabilities ?? [];
|
|
1814
|
+
return capabilities.map((cap) => ({
|
|
1815
|
+
id: `${config.agentDid}:${cap}`,
|
|
1816
|
+
name: cap,
|
|
1817
|
+
description: `Capability "${cap}" of agent ${config.agentDid}`
|
|
1818
|
+
}));
|
|
1819
|
+
}
|
|
1820
|
+
async function syncSkills(config, existing) {
|
|
1821
|
+
const remote = await listSkills(config);
|
|
1822
|
+
const remoteIds = new Set(remote.map((s) => s.id));
|
|
1823
|
+
const existingIds = new Set(existing.map((s) => s.id));
|
|
1824
|
+
return {
|
|
1825
|
+
added: remote.filter((s) => !existingIds.has(s.id)),
|
|
1826
|
+
removed: existing.filter((s) => !remoteIds.has(s.id))
|
|
1827
|
+
};
|
|
1828
|
+
}
|
|
1829
|
+
var sessionCodec = paperclipSessionCodec;
|
|
1830
|
+
async function invoke(config, params) {
|
|
1831
|
+
return execute(config, params);
|
|
1832
|
+
}
|
|
1833
|
+
async function status(config, taskId) {
|
|
1834
|
+
const progress = await getTaskProgress(config, taskId);
|
|
1835
|
+
return {
|
|
1836
|
+
status: progress.status,
|
|
1837
|
+
progressPercent: progress.progressPercent,
|
|
1838
|
+
progressMessage: progress.progressMessage
|
|
1839
|
+
};
|
|
1840
|
+
}
|
|
1841
|
+
async function cancel(config, taskId) {
|
|
1842
|
+
const result = await cancelTask(config, taskId);
|
|
1843
|
+
if (!result.success) {
|
|
1844
|
+
throw new Error(result.error ?? "Failed to cancel task");
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1265
1847
|
function createServerAdapter() {
|
|
1848
|
+
const canonical = createCanonicalServerAdapter();
|
|
1266
1849
|
return {
|
|
1850
|
+
...canonical,
|
|
1267
1851
|
name: "agrenting",
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1852
|
+
// Legacy helpers remain available under explicit names. The canonical
|
|
1853
|
+
// Paperclip keys above use AdapterExecutionContext and structured tests.
|
|
1854
|
+
legacyExecute: execute,
|
|
1855
|
+
legacyTestEnvironment: testEnvironment,
|
|
1856
|
+
getLegacyConfigSchema: getConfigSchema,
|
|
1271
1857
|
startWebhookListener,
|
|
1272
1858
|
stopWebhookListener,
|
|
1273
1859
|
registerWebhook,
|
|
@@ -1285,6 +1871,7 @@ function createServerAdapter() {
|
|
|
1285
1871
|
sendMessageToHiring,
|
|
1286
1872
|
getHiringMessages,
|
|
1287
1873
|
retryHiring,
|
|
1874
|
+
cancelHiring,
|
|
1288
1875
|
getHiring,
|
|
1289
1876
|
listHirings,
|
|
1290
1877
|
autoSelectAgent,
|
|
@@ -1294,19 +1881,64 @@ function createServerAdapter() {
|
|
|
1294
1881
|
getBalance,
|
|
1295
1882
|
getTransactions,
|
|
1296
1883
|
deposit,
|
|
1297
|
-
withdraw
|
|
1884
|
+
withdraw,
|
|
1885
|
+
// Legacy pre-0.4 contract additions:
|
|
1886
|
+
invoke,
|
|
1887
|
+
status,
|
|
1888
|
+
cancel,
|
|
1889
|
+
legacyDetectModel: detectModel,
|
|
1890
|
+
legacyListSkills: listSkills,
|
|
1891
|
+
legacySyncSkills: syncSkills
|
|
1298
1892
|
};
|
|
1299
1893
|
}
|
|
1894
|
+
|
|
1895
|
+
// server/src/apps-v2.ts
|
|
1896
|
+
var agrentingAppGalleryEntry = {
|
|
1897
|
+
key: "agrenting",
|
|
1898
|
+
name: "Agrenting",
|
|
1899
|
+
logoUrl: "https://www.google.com/s2/favicons?domain=agrenting.com&sz=64",
|
|
1900
|
+
tagline: "Discover and hire governed remote AI agents.",
|
|
1901
|
+
description: "Give Paperclip agents governed access to discover, hire, monitor, and cancel remote agents in the Agrenting marketplace.",
|
|
1902
|
+
authKind: "api_key",
|
|
1903
|
+
transportTemplate: {
|
|
1904
|
+
transport: "remote_http",
|
|
1905
|
+
url: "https://agrenting.com/mcp/hirer"
|
|
1906
|
+
},
|
|
1907
|
+
credentialFields: [
|
|
1908
|
+
{
|
|
1909
|
+
label: "Agrenting API key",
|
|
1910
|
+
configPath: "credentials.authorization",
|
|
1911
|
+
helpUrl: "https://agrenting.com/dashboard/api-keys",
|
|
1912
|
+
required: true,
|
|
1913
|
+
placement: "header",
|
|
1914
|
+
key: "Authorization",
|
|
1915
|
+
prefix: "Bearer "
|
|
1916
|
+
}
|
|
1917
|
+
],
|
|
1918
|
+
recommendedDefaults: {
|
|
1919
|
+
access: "all_agents",
|
|
1920
|
+
askFirstRiskLevels: ["write", "destructive"]
|
|
1921
|
+
},
|
|
1922
|
+
urlPatterns: ["https://agrenting.com/mcp/hirer*"]
|
|
1923
|
+
};
|
|
1300
1924
|
export {
|
|
1301
1925
|
AgrentingClient,
|
|
1302
1926
|
MAX_POLLS,
|
|
1303
1927
|
POLL_INTERVALS_MS,
|
|
1928
|
+
agentConfigurationDoc,
|
|
1929
|
+
agrentingAppGalleryEntry,
|
|
1304
1930
|
autoSelectAgent,
|
|
1305
1931
|
canSubmitTask,
|
|
1932
|
+
cancel,
|
|
1933
|
+
cancelHiring,
|
|
1934
|
+
cancelTask,
|
|
1306
1935
|
checkBalance,
|
|
1307
1936
|
createServerAdapter,
|
|
1308
1937
|
createWebhookHandler,
|
|
1309
1938
|
deregisterWebhook,
|
|
1939
|
+
detectModel,
|
|
1940
|
+
execute,
|
|
1941
|
+
executePaperclip,
|
|
1310
1942
|
executeWithRetry,
|
|
1311
1943
|
formatAgentResponse,
|
|
1312
1944
|
formatInsufficientBalanceComment,
|
|
@@ -1315,13 +1947,20 @@ export {
|
|
|
1315
1947
|
getActiveTaskMappings,
|
|
1316
1948
|
getAgentProfile,
|
|
1317
1949
|
getBackoffMs,
|
|
1950
|
+
getConfigSchema,
|
|
1318
1951
|
getHiring,
|
|
1319
1952
|
getHiringMessages,
|
|
1953
|
+
getPaperclipConfigSchema,
|
|
1320
1954
|
getTaskMessages,
|
|
1955
|
+
getTaskProgress,
|
|
1321
1956
|
getWebhookGracePeriodMs,
|
|
1322
1957
|
hireAgent,
|
|
1958
|
+
invoke,
|
|
1959
|
+
label,
|
|
1323
1960
|
listCapabilities,
|
|
1324
1961
|
listHirings,
|
|
1962
|
+
listSkills,
|
|
1963
|
+
paperclipSessionCodec,
|
|
1325
1964
|
pollTaskUntilDone,
|
|
1326
1965
|
processIncomingMessage,
|
|
1327
1966
|
reassignTask,
|
|
@@ -1330,6 +1969,12 @@ export {
|
|
|
1330
1969
|
retryHiring,
|
|
1331
1970
|
sendMessageToHiring,
|
|
1332
1971
|
sendMessageToTask,
|
|
1972
|
+
sessionCodec,
|
|
1973
|
+
status,
|
|
1974
|
+
syncSkills,
|
|
1975
|
+
testEnvironment,
|
|
1976
|
+
testPaperclipEnvironment,
|
|
1977
|
+
type,
|
|
1333
1978
|
unregisterTaskMapping,
|
|
1334
1979
|
verifyWebhookSignature
|
|
1335
1980
|
};
|